1
0

dataprovider.go 127 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676
  1. // Package dataprovider provides data access.
  2. // It abstracts different data providers and exposes a common API.
  3. package dataprovider
  4. import (
  5. "bufio"
  6. "bytes"
  7. "context"
  8. "crypto/md5"
  9. "crypto/sha1"
  10. "crypto/sha256"
  11. "crypto/sha512"
  12. "crypto/subtle"
  13. "crypto/x509"
  14. "encoding/base64"
  15. "encoding/hex"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "hash"
  20. "io"
  21. "net"
  22. "net/http"
  23. "net/url"
  24. "os"
  25. "os/exec"
  26. "path"
  27. "path/filepath"
  28. "regexp"
  29. "runtime"
  30. "strconv"
  31. "strings"
  32. "sync"
  33. "sync/atomic"
  34. "time"
  35. "github.com/GehirnInc/crypt"
  36. "github.com/GehirnInc/crypt/apr1_crypt"
  37. "github.com/GehirnInc/crypt/md5_crypt"
  38. "github.com/GehirnInc/crypt/sha512_crypt"
  39. "github.com/alexedwards/argon2id"
  40. "github.com/go-chi/render"
  41. "github.com/rs/xid"
  42. "github.com/sftpgo/sdk"
  43. passwordvalidator "github.com/wagslane/go-password-validator"
  44. "golang.org/x/crypto/bcrypt"
  45. "golang.org/x/crypto/pbkdf2"
  46. "golang.org/x/crypto/ssh"
  47. "github.com/drakkan/sftpgo/v2/httpclient"
  48. "github.com/drakkan/sftpgo/v2/kms"
  49. "github.com/drakkan/sftpgo/v2/logger"
  50. "github.com/drakkan/sftpgo/v2/mfa"
  51. "github.com/drakkan/sftpgo/v2/plugin"
  52. "github.com/drakkan/sftpgo/v2/util"
  53. "github.com/drakkan/sftpgo/v2/vfs"
  54. )
  55. const (
  56. // SQLiteDataProviderName defines the name for SQLite database provider
  57. SQLiteDataProviderName = "sqlite"
  58. // PGSQLDataProviderName defines the name for PostgreSQL database provider
  59. PGSQLDataProviderName = "postgresql"
  60. // MySQLDataProviderName defines the name for MySQL database provider
  61. MySQLDataProviderName = "mysql"
  62. // BoltDataProviderName defines the name for bbolt key/value store provider
  63. BoltDataProviderName = "bolt"
  64. // MemoryDataProviderName defines the name for memory provider
  65. MemoryDataProviderName = "memory"
  66. // CockroachDataProviderName defines the for CockroachDB provider
  67. CockroachDataProviderName = "cockroachdb"
  68. // DumpVersion defines the version for the dump.
  69. // For restore/load we support the current version and the previous one
  70. DumpVersion = 12
  71. argonPwdPrefix = "$argon2id$"
  72. bcryptPwdPrefix = "$2a$"
  73. pbkdf2SHA1Prefix = "$pbkdf2-sha1$"
  74. pbkdf2SHA256Prefix = "$pbkdf2-sha256$"
  75. pbkdf2SHA512Prefix = "$pbkdf2-sha512$"
  76. pbkdf2SHA256B64SaltPrefix = "$pbkdf2-b64salt-sha256$"
  77. md5cryptPwdPrefix = "$1$"
  78. md5cryptApr1PwdPrefix = "$apr1$"
  79. sha512cryptPwdPrefix = "$6$"
  80. md5LDAPPwdPrefix = "{MD5}"
  81. trackQuotaDisabledError = "please enable track_quota in your configuration to use this method"
  82. operationAdd = "add"
  83. operationUpdate = "update"
  84. operationDelete = "delete"
  85. sqlPrefixValidChars = "abcdefghijklmnopqrstuvwxyz_0123456789"
  86. maxHookResponseSize = 1048576 // 1MB
  87. )
  88. // Supported algorithms for hashing passwords.
  89. // These algorithms can be used when SFTPGo hashes a plain text password
  90. const (
  91. HashingAlgoBcrypt = "bcrypt"
  92. HashingAlgoArgon2ID = "argon2id"
  93. )
  94. // ordering constants
  95. const (
  96. OrderASC = "ASC"
  97. OrderDESC = "DESC"
  98. )
  99. const (
  100. protocolSSH = "SSH"
  101. protocolFTP = "FTP"
  102. protocolWebDAV = "DAV"
  103. protocolHTTP = "HTTP"
  104. )
  105. var (
  106. // SupportedProviders defines the supported data providers
  107. SupportedProviders = []string{SQLiteDataProviderName, PGSQLDataProviderName, MySQLDataProviderName,
  108. BoltDataProviderName, MemoryDataProviderName, CockroachDataProviderName}
  109. // ValidPerms defines all the valid permissions for a user
  110. ValidPerms = []string{PermAny, PermListItems, PermDownload, PermUpload, PermOverwrite, PermCreateDirs, PermRename,
  111. PermRenameFiles, PermRenameDirs, PermDelete, PermDeleteFiles, PermDeleteDirs, PermCreateSymlinks, PermChmod,
  112. PermChown, PermChtimes}
  113. // ValidLoginMethods defines all the valid login methods
  114. ValidLoginMethods = []string{SSHLoginMethodPublicKey, LoginMethodPassword, SSHLoginMethodPassword,
  115. SSHLoginMethodKeyboardInteractive, SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt,
  116. LoginMethodTLSCertificate, LoginMethodTLSCertificateAndPwd}
  117. // SSHMultiStepsLoginMethods defines the supported Multi-Step Authentications
  118. SSHMultiStepsLoginMethods = []string{SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt}
  119. // ErrNoAuthTryed defines the error for connection closed before authentication
  120. ErrNoAuthTryed = errors.New("no auth tryed")
  121. // ErrNotImplemented defines the error for features not supported for a particular data provider
  122. ErrNotImplemented = errors.New("feature not supported with the configured data provider")
  123. // ValidProtocols defines all the valid protcols
  124. ValidProtocols = []string{protocolSSH, protocolFTP, protocolWebDAV, protocolHTTP}
  125. // MFAProtocols defines the supported protocols for multi-factor authentication
  126. MFAProtocols = []string{protocolHTTP, protocolSSH, protocolFTP}
  127. // ErrNoInitRequired defines the error returned by InitProvider if no inizialization/update is required
  128. ErrNoInitRequired = errors.New("the data provider is up to date")
  129. // ErrInvalidCredentials defines the error to return if the supplied credentials are invalid
  130. ErrInvalidCredentials = errors.New("invalid credentials")
  131. // ErrLoginNotAllowedFromIP defines the error to return if login is denied from the current IP
  132. ErrLoginNotAllowedFromIP = errors.New("login is not allowed from this IP")
  133. isAdminCreated = int32(0)
  134. validTLSUsernames = []string{string(sdk.TLSUsernameNone), string(sdk.TLSUsernameCN)}
  135. config Config
  136. provider Provider
  137. sqlPlaceholders []string
  138. internalHashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix}
  139. hashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix,
  140. pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, md5LDAPPwdPrefix,
  141. sha512cryptPwdPrefix}
  142. pbkdfPwdPrefixes = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix}
  143. pbkdfPwdB64SaltPrefixes = []string{pbkdf2SHA256B64SaltPrefix}
  144. unixPwdPrefixes = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha512cryptPwdPrefix}
  145. sharedProviders = []string{PGSQLDataProviderName, MySQLDataProviderName, CockroachDataProviderName}
  146. logSender = "dataprovider"
  147. credentialsDirPath string
  148. sqlTableUsers = "users"
  149. sqlTableFolders = "folders"
  150. sqlTableFoldersMapping = "folders_mapping"
  151. sqlTableUsersFoldersMapping = "users_folders_mapping"
  152. sqlTableAdmins = "admins"
  153. sqlTableAPIKeys = "api_keys"
  154. sqlTableShares = "shares"
  155. sqlTableDefenderHosts = "defender_hosts"
  156. sqlTableDefenderEvents = "defender_events"
  157. sqlTableActiveTransfers = "active_transfers"
  158. sqlTableGroups = "groups"
  159. sqlTableUsersGroupsMapping = "users_groups_mapping"
  160. sqlTableGroupsFoldersMapping = "groups_folders_mapping"
  161. sqlTableSchemaVersion = "schema_version"
  162. argon2Params *argon2id.Params
  163. lastLoginMinDelay = 10 * time.Minute
  164. usernameRegex = regexp.MustCompile("^[a-zA-Z0-9-_.~]+$")
  165. tempPath string
  166. )
  167. type schemaVersion struct {
  168. Version int
  169. }
  170. // BcryptOptions defines the options for bcrypt password hashing
  171. type BcryptOptions struct {
  172. Cost int `json:"cost" mapstructure:"cost"`
  173. }
  174. // Argon2Options defines the options for argon2 password hashing
  175. type Argon2Options struct {
  176. Memory uint32 `json:"memory" mapstructure:"memory"`
  177. Iterations uint32 `json:"iterations" mapstructure:"iterations"`
  178. Parallelism uint8 `json:"parallelism" mapstructure:"parallelism"`
  179. }
  180. // PasswordHashing defines the configuration for password hashing
  181. type PasswordHashing struct {
  182. BcryptOptions BcryptOptions `json:"bcrypt_options" mapstructure:"bcrypt_options"`
  183. Argon2Options Argon2Options `json:"argon2_options" mapstructure:"argon2_options"`
  184. // Algorithm to use for hashing passwords. Available algorithms: argon2id, bcrypt. Default: bcrypt
  185. Algo string `json:"algo" mapstructure:"algo"`
  186. }
  187. // PasswordValidationRules defines the password validation rules
  188. type PasswordValidationRules struct {
  189. // MinEntropy defines the minimum password entropy.
  190. // 0 means disabled, any password will be accepted.
  191. // Take a look at the following link for more details
  192. // https://github.com/wagslane/go-password-validator#what-entropy-value-should-i-use
  193. MinEntropy float64 `json:"min_entropy" mapstructure:"min_entropy"`
  194. }
  195. // PasswordValidation defines the password validation rules for admins and protocol users
  196. type PasswordValidation struct {
  197. // Password validation rules for SFTPGo admin users
  198. Admins PasswordValidationRules `json:"admins" mapstructure:"admins"`
  199. // Password validation rules for SFTPGo protocol users
  200. Users PasswordValidationRules `json:"users" mapstructure:"users"`
  201. }
  202. // ObjectsActions defines the action to execute on user create, update, delete for the specified objects
  203. type ObjectsActions struct {
  204. // Valid values are add, update, delete. Empty slice to disable
  205. ExecuteOn []string `json:"execute_on" mapstructure:"execute_on"`
  206. // Valid values are user, admin, api_key
  207. ExecuteFor []string `json:"execute_for" mapstructure:"execute_for"`
  208. // Absolute path to an external program or an HTTP URL
  209. Hook string `json:"hook" mapstructure:"hook"`
  210. }
  211. // ProviderStatus defines the provider status
  212. type ProviderStatus struct {
  213. Driver string `json:"driver"`
  214. IsActive bool `json:"is_active"`
  215. Error string `json:"error"`
  216. }
  217. // AutoBackup defines the settings for automatic provider backups.
  218. // Example: hour "0" and day_of_week "*" means a backup every day at midnight.
  219. // The backup file name is in the format backup_<day_of_week>_<hour>.json
  220. // files with the same name will be overwritten
  221. type AutoBackup struct {
  222. Enabled bool `json:"enabled" mapstructure:"enabled"`
  223. // hour as standard cron expression. Allowed values: 0-23.
  224. // Allowed special characters: asterisk (*), slash (/), comma (,), hyphen (-).
  225. // More info about special characters here:
  226. // https://pkg.go.dev/github.com/robfig/cron#hdr-Special_Characters
  227. Hour string `json:"hour" mapstructure:"hour"`
  228. // Day of the week as cron expression. Allowed values: 0-6 (Sunday to Saturday).
  229. // Allowed special characters: asterisk (*), slash (/), comma (,), hyphen (-), question mark (?).
  230. // More info about special characters here:
  231. // https://pkg.go.dev/github.com/robfig/cron#hdr-Special_Characters
  232. DayOfWeek string `json:"day_of_week" mapstructure:"day_of_week"`
  233. }
  234. // Config provider configuration
  235. type Config struct {
  236. // Driver name, must be one of the SupportedProviders
  237. Driver string `json:"driver" mapstructure:"driver"`
  238. // Database name. For driver sqlite this can be the database name relative to the config dir
  239. // or the absolute path to the SQLite database.
  240. Name string `json:"name" mapstructure:"name"`
  241. // Database host
  242. Host string `json:"host" mapstructure:"host"`
  243. // Database port
  244. Port int `json:"port" mapstructure:"port"`
  245. // Database username
  246. Username string `json:"username" mapstructure:"username"`
  247. // Database password
  248. Password string `json:"password" mapstructure:"password"`
  249. // Used for drivers mysql and postgresql.
  250. // 0 disable SSL/TLS connections.
  251. // 1 require ssl.
  252. // 2 set ssl mode to verify-ca for driver postgresql and skip-verify for driver mysql.
  253. // 3 set ssl mode to verify-full for driver postgresql and preferred for driver mysql.
  254. SSLMode int `json:"sslmode" mapstructure:"sslmode"`
  255. // Path to the root certificate authority used to verify that the server certificate was signed by a trusted CA
  256. RootCert string `json:"root_cert" mapstructure:"root_cert"`
  257. // Path to the client certificate for two-way TLS authentication
  258. ClientCert string `json:"client_cert" mapstructure:"client_cert"`
  259. // Path to the client key for two-way TLS authentication
  260. ClientKey string `json:"client_key" mapstructure:"client_key"`
  261. // Custom database connection string.
  262. // If not empty this connection string will be used instead of build one using the previous parameters
  263. ConnectionString string `json:"connection_string" mapstructure:"connection_string"`
  264. // prefix for SQL tables
  265. SQLTablesPrefix string `json:"sql_tables_prefix" mapstructure:"sql_tables_prefix"`
  266. // Set the preferred way to track users quota between the following choices:
  267. // 0, disable quota tracking. REST API to scan user dir and update quota will do nothing
  268. // 1, quota is updated each time a user upload or delete a file even if the user has no quota restrictions
  269. // 2, quota is updated each time a user upload or delete a file but only for users with quota restrictions
  270. // and for virtual folders.
  271. // With this configuration the "quota scan" REST API can still be used to periodically update space usage
  272. // for users without quota restrictions
  273. TrackQuota int `json:"track_quota" mapstructure:"track_quota"`
  274. // Sets the maximum number of open connections for mysql and postgresql driver.
  275. // Default 0 (unlimited)
  276. PoolSize int `json:"pool_size" mapstructure:"pool_size"`
  277. // Users default base directory.
  278. // If no home dir is defined while adding a new user, and this value is
  279. // a valid absolute path, then the user home dir will be automatically
  280. // defined as the path obtained joining the base dir and the username
  281. UsersBaseDir string `json:"users_base_dir" mapstructure:"users_base_dir"`
  282. // Actions to execute on objects add, update, delete.
  283. // The supported objects are user, admin, api_key.
  284. // Update action will not be fired for internal updates such as the last login or the user quota fields.
  285. Actions ObjectsActions `json:"actions" mapstructure:"actions"`
  286. // Absolute path to an external program or an HTTP URL to invoke for users authentication.
  287. // Leave empty to use builtin authentication.
  288. // If the authentication succeed the user will be automatically added/updated inside the defined data provider.
  289. // Actions defined for user added/updated will not be executed in this case.
  290. // This method is slower than built-in authentication methods, but it's very flexible as anyone can
  291. // easily write his own authentication hooks.
  292. ExternalAuthHook string `json:"external_auth_hook" mapstructure:"external_auth_hook"`
  293. // ExternalAuthScope defines the scope for the external authentication hook.
  294. // - 0 means all supported authentication scopes, the external hook will be executed for password,
  295. // public key, keyboard interactive authentication and TLS certificates
  296. // - 1 means passwords only
  297. // - 2 means public keys only
  298. // - 4 means keyboard interactive only
  299. // - 8 means TLS certificates only
  300. // you can combine the scopes, for example 3 means password and public key, 5 password and keyboard
  301. // interactive and so on
  302. ExternalAuthScope int `json:"external_auth_scope" mapstructure:"external_auth_scope"`
  303. // CredentialsPath defines the directory for storing user provided credential files such as
  304. // Google Cloud Storage credentials. It can be a path relative to the config dir or an
  305. // absolute path
  306. CredentialsPath string `json:"credentials_path" mapstructure:"credentials_path"`
  307. // Absolute path to an external program or an HTTP URL to invoke just before the user login.
  308. // This program/URL allows to modify or create the user trying to login.
  309. // It is useful if you have users with dynamic fields to update just before the login.
  310. // Please note that if you want to create a new user, the pre-login hook response must
  311. // include all the mandatory user fields.
  312. //
  313. // The pre-login hook must finish within 30 seconds.
  314. //
  315. // If an error happens while executing the "PreLoginHook" then login will be denied.
  316. // PreLoginHook and ExternalAuthHook are mutally exclusive.
  317. // Leave empty to disable.
  318. PreLoginHook string `json:"pre_login_hook" mapstructure:"pre_login_hook"`
  319. // Absolute path to an external program or an HTTP URL to invoke after the user login.
  320. // Based on the configured scope you can choose if notify failed or successful logins
  321. // or both
  322. PostLoginHook string `json:"post_login_hook" mapstructure:"post_login_hook"`
  323. // PostLoginScope defines the scope for the post-login hook.
  324. // - 0 means notify both failed and successful logins
  325. // - 1 means notify failed logins
  326. // - 2 means notify successful logins
  327. PostLoginScope int `json:"post_login_scope" mapstructure:"post_login_scope"`
  328. // Absolute path to an external program or an HTTP URL to invoke just before password
  329. // authentication. This hook allows you to externally check the provided password,
  330. // its main use case is to allow to easily support things like password+OTP for protocols
  331. // without keyboard interactive support such as FTP and WebDAV. You can ask your users
  332. // to login using a string consisting of a fixed password and a One Time Token, you
  333. // can verify the token inside the hook and ask to SFTPGo to verify the fixed part.
  334. CheckPasswordHook string `json:"check_password_hook" mapstructure:"check_password_hook"`
  335. // CheckPasswordScope defines the scope for the check password hook.
  336. // - 0 means all protocols
  337. // - 1 means SSH
  338. // - 2 means FTP
  339. // - 4 means WebDAV
  340. // you can combine the scopes, for example 6 means FTP and WebDAV
  341. CheckPasswordScope int `json:"check_password_scope" mapstructure:"check_password_scope"`
  342. // Defines how the database will be initialized/updated:
  343. // - 0 means automatically
  344. // - 1 means manually using the initprovider sub-command
  345. UpdateMode int `json:"update_mode" mapstructure:"update_mode"`
  346. // PasswordHashing defines the configuration for password hashing
  347. PasswordHashing PasswordHashing `json:"password_hashing" mapstructure:"password_hashing"`
  348. // PasswordValidation defines the password validation rules
  349. PasswordValidation PasswordValidation `json:"password_validation" mapstructure:"password_validation"`
  350. // Verifying argon2 passwords has a high memory and computational cost,
  351. // by enabling, in memory, password caching you reduce this cost.
  352. PasswordCaching bool `json:"password_caching" mapstructure:"password_caching"`
  353. // DelayedQuotaUpdate defines the number of seconds to accumulate quota updates.
  354. // If there are a lot of close uploads, accumulating quota updates can save you many
  355. // queries to the data provider.
  356. // If you want to track quotas, a scheduled quota update is recommended in any case, the stored
  357. // quota size may be incorrect for several reasons, such as an unexpected shutdown, temporary provider
  358. // failures, file copied outside of SFTPGo, and so on.
  359. // 0 means immediate quota update.
  360. DelayedQuotaUpdate int `json:"delayed_quota_update" mapstructure:"delayed_quota_update"`
  361. // If enabled, a default admin user with username "admin" and password "password" will be created
  362. // on first start.
  363. // You can also create the first admin user by using the web interface or by loading initial data.
  364. CreateDefaultAdmin bool `json:"create_default_admin" mapstructure:"create_default_admin"`
  365. // Rules for usernames and folder names:
  366. // - 0 means no rules
  367. // - 1 means you can use any UTF-8 character. The names are used in URIs for REST API and Web admin.
  368. // By default only unreserved URI characters are allowed: ALPHA / DIGIT / "-" / "." / "_" / "~".
  369. // - 2 means names are converted to lowercase before saving/matching and so case
  370. // insensitive matching is possible
  371. // - 4 means trimming trailing and leading white spaces before saving/matching
  372. // Rules can be combined, for example 3 means both converting to lowercase and allowing any UTF-8 character.
  373. // Enabling these options for existing installations could be backward incompatible, some users
  374. // could be unable to login, for example existing users with mixed cases in their usernames.
  375. // You have to ensure that all existing users respect the defined rules.
  376. NamingRules int `json:"naming_rules" mapstructure:"naming_rules"`
  377. // If the data provider is shared across multiple SFTPGo instances, set this parameter to 1.
  378. // MySQL, PostgreSQL and CockroachDB can be shared, this setting is ignored for other data
  379. // providers. For shared data providers, SFTPGo periodically reloads the latest updated users,
  380. // based on the "updated_at" field, and updates its internal caches if users are updated from
  381. // a different instance. This check, if enabled, is executed every 10 minutes.
  382. // For shared data providers, active transfers are persisted in the database and thus
  383. // quota checks between ongoing transfers will work cross multiple instances
  384. IsShared int `json:"is_shared" mapstructure:"is_shared"`
  385. // Path to the backup directory. This can be an absolute path or a path relative to the config dir
  386. BackupsPath string `json:"backups_path" mapstructure:"backups_path"`
  387. // Settings for automatic backups
  388. AutoBackup AutoBackup `json:"auto_backup" mapstructure:"auto_backup"`
  389. }
  390. // GetShared returns the provider share mode
  391. func (c *Config) GetShared() int {
  392. if !util.IsStringInSlice(c.Driver, sharedProviders) {
  393. return 0
  394. }
  395. return c.IsShared
  396. }
  397. func (c *Config) convertName(name string) string {
  398. if c.NamingRules == 0 {
  399. return name
  400. }
  401. if c.NamingRules&2 != 0 {
  402. name = strings.ToLower(name)
  403. }
  404. if c.NamingRules&4 != 0 {
  405. name = strings.TrimSpace(name)
  406. }
  407. return name
  408. }
  409. // IsDefenderSupported returns true if the configured provider supports the defender
  410. func (c *Config) IsDefenderSupported() bool {
  411. switch c.Driver {
  412. case MySQLDataProviderName, PGSQLDataProviderName, CockroachDataProviderName:
  413. return true
  414. default:
  415. return false
  416. }
  417. }
  418. func (c *Config) requireCustomTLSForMySQL() bool {
  419. if config.RootCert != "" && util.IsFileInputValid(config.RootCert) {
  420. return config.SSLMode != 0
  421. }
  422. if config.ClientCert != "" && config.ClientKey != "" && util.IsFileInputValid(config.ClientCert) &&
  423. util.IsFileInputValid(config.ClientKey) {
  424. return config.SSLMode != 0
  425. }
  426. return false
  427. }
  428. func (c *Config) doBackup() {
  429. now := time.Now()
  430. outputFile := filepath.Join(c.BackupsPath, fmt.Sprintf("backup_%v_%v.json", now.Weekday(), now.Hour()))
  431. providerLog(logger.LevelDebug, "starting auto backup to file %#v", outputFile)
  432. err := os.MkdirAll(filepath.Dir(outputFile), 0700)
  433. if err != nil {
  434. providerLog(logger.LevelError, "unable to create backup dir %#v: %v", outputFile, err)
  435. return
  436. }
  437. backup, err := DumpData()
  438. if err != nil {
  439. providerLog(logger.LevelError, "unable to execute backup: %v", err)
  440. return
  441. }
  442. dump, err := json.Marshal(backup)
  443. if err != nil {
  444. providerLog(logger.LevelError, "unable to marshal backup as JSON: %v", err)
  445. return
  446. }
  447. err = os.WriteFile(outputFile, dump, 0600)
  448. if err != nil {
  449. providerLog(logger.LevelError, "unable to save backup: %v", err)
  450. return
  451. }
  452. providerLog(logger.LevelDebug, "auto backup saved to %#v", outputFile)
  453. }
  454. // ConvertName converts the given name based on the configured rules
  455. func ConvertName(name string) string {
  456. return config.convertName(name)
  457. }
  458. // ActiveTransfer defines an active protocol transfer
  459. type ActiveTransfer struct {
  460. ID int64
  461. Type int
  462. ConnID string
  463. Username string
  464. FolderName string
  465. IP string
  466. TruncatedSize int64
  467. CurrentULSize int64
  468. CurrentDLSize int64
  469. CreatedAt int64
  470. UpdatedAt int64
  471. }
  472. // TransferQuota stores the allowed transfer quota fields
  473. type TransferQuota struct {
  474. ULSize int64
  475. DLSize int64
  476. TotalSize int64
  477. AllowedULSize int64
  478. AllowedDLSize int64
  479. AllowedTotalSize int64
  480. }
  481. // HasSizeLimits returns true if any size limit is set
  482. func (q *TransferQuota) HasSizeLimits() bool {
  483. return q.AllowedDLSize > 0 || q.AllowedULSize > 0 || q.AllowedTotalSize > 0
  484. }
  485. // HasUploadSpace returns true if there is transfer upload space available
  486. func (q *TransferQuota) HasUploadSpace() bool {
  487. if q.TotalSize <= 0 && q.ULSize <= 0 {
  488. return true
  489. }
  490. if q.TotalSize > 0 {
  491. return q.AllowedTotalSize > 0
  492. }
  493. return q.AllowedULSize > 0
  494. }
  495. // HasDownloadSpace returns true if there is transfer download space available
  496. func (q *TransferQuota) HasDownloadSpace() bool {
  497. if q.TotalSize <= 0 && q.DLSize <= 0 {
  498. return true
  499. }
  500. if q.TotalSize > 0 {
  501. return q.AllowedTotalSize > 0
  502. }
  503. return q.AllowedDLSize > 0
  504. }
  505. // DefenderEntry defines a defender entry
  506. type DefenderEntry struct {
  507. ID int64 `json:"-"`
  508. IP string `json:"ip"`
  509. Score int `json:"score,omitempty"`
  510. BanTime time.Time `json:"ban_time,omitempty"`
  511. }
  512. // GetID returns an unique ID for a defender entry
  513. func (d *DefenderEntry) GetID() string {
  514. return hex.EncodeToString([]byte(d.IP))
  515. }
  516. // GetBanTime returns the ban time for a defender entry as string
  517. func (d *DefenderEntry) GetBanTime() string {
  518. if d.BanTime.IsZero() {
  519. return ""
  520. }
  521. return d.BanTime.UTC().Format(time.RFC3339)
  522. }
  523. // MarshalJSON returns the JSON encoding of a DefenderEntry.
  524. func (d *DefenderEntry) MarshalJSON() ([]byte, error) {
  525. return json.Marshal(&struct {
  526. ID string `json:"id"`
  527. IP string `json:"ip"`
  528. Score int `json:"score,omitempty"`
  529. BanTime string `json:"ban_time,omitempty"`
  530. }{
  531. ID: d.GetID(),
  532. IP: d.IP,
  533. Score: d.Score,
  534. BanTime: d.GetBanTime(),
  535. })
  536. }
  537. // BackupData defines the structure for the backup/restore files
  538. type BackupData struct {
  539. Users []User `json:"users"`
  540. Groups []Group `json:"groups"`
  541. Folders []vfs.BaseVirtualFolder `json:"folders"`
  542. Admins []Admin `json:"admins"`
  543. APIKeys []APIKey `json:"api_keys"`
  544. Shares []Share `json:"shares"`
  545. Version int `json:"version"`
  546. }
  547. // HasFolder returns true if the folder with the given name is included
  548. func (d *BackupData) HasFolder(name string) bool {
  549. for _, folder := range d.Folders {
  550. if folder.Name == name {
  551. return true
  552. }
  553. }
  554. return false
  555. }
  556. type checkPasswordRequest struct {
  557. Username string `json:"username"`
  558. IP string `json:"ip"`
  559. Password string `json:"password"`
  560. Protocol string `json:"protocol"`
  561. }
  562. type checkPasswordResponse struct {
  563. // 0 KO, 1 OK, 2 partial success, -1 not executed
  564. Status int `json:"status"`
  565. // for status = 2 this is the password to check against the one stored
  566. // inside the SFTPGo data provider
  567. ToVerify string `json:"to_verify"`
  568. }
  569. // GetQuotaTracking returns the configured mode for user's quota tracking
  570. func GetQuotaTracking() int {
  571. return config.TrackQuota
  572. }
  573. // HasUsersBaseDir returns true if users base dir is set
  574. func HasUsersBaseDir() bool {
  575. return config.UsersBaseDir != ""
  576. }
  577. // Provider defines the interface that data providers must implement.
  578. type Provider interface {
  579. validateUserAndPass(username, password, ip, protocol string) (User, error)
  580. validateUserAndPubKey(username string, pubKey []byte, isSSHCert bool) (User, string, error)
  581. validateUserAndTLSCert(username, protocol string, tlsCert *x509.Certificate) (User, error)
  582. updateQuota(username string, filesAdd int, sizeAdd int64, reset bool) error
  583. updateTransferQuota(username string, uploadSize, downloadSize int64, reset bool) error
  584. getUsedQuota(username string) (int, int64, int64, int64, error)
  585. userExists(username string) (User, error)
  586. addUser(user *User) error
  587. updateUser(user *User) error
  588. deleteUser(user User) error
  589. updateUserPassword(username, password string) error
  590. getUsers(limit int, offset int, order string) ([]User, error)
  591. dumpUsers() ([]User, error)
  592. getRecentlyUpdatedUsers(after int64) ([]User, error)
  593. getUsersForQuotaCheck(toFetch map[string]bool) ([]User, error)
  594. updateLastLogin(username string) error
  595. updateAdminLastLogin(username string) error
  596. setUpdatedAt(username string)
  597. getFolders(limit, offset int, order string, minimal bool) ([]vfs.BaseVirtualFolder, error)
  598. getFolderByName(name string) (vfs.BaseVirtualFolder, error)
  599. addFolder(folder *vfs.BaseVirtualFolder) error
  600. updateFolder(folder *vfs.BaseVirtualFolder) error
  601. deleteFolder(folder vfs.BaseVirtualFolder) error
  602. updateFolderQuota(name string, filesAdd int, sizeAdd int64, reset bool) error
  603. getUsedFolderQuota(name string) (int, int64, error)
  604. dumpFolders() ([]vfs.BaseVirtualFolder, error)
  605. getGroups(limit, offset int, order string, minimal bool) ([]Group, error)
  606. getGroupsWithNames(names []string) ([]Group, error)
  607. getUsersInGroups(names []string) ([]string, error)
  608. groupExists(name string) (Group, error)
  609. addGroup(group *Group) error
  610. updateGroup(group *Group) error
  611. deleteGroup(group Group) error
  612. dumpGroups() ([]Group, error)
  613. adminExists(username string) (Admin, error)
  614. addAdmin(admin *Admin) error
  615. updateAdmin(admin *Admin) error
  616. deleteAdmin(admin Admin) error
  617. getAdmins(limit int, offset int, order string) ([]Admin, error)
  618. dumpAdmins() ([]Admin, error)
  619. validateAdminAndPass(username, password, ip string) (Admin, error)
  620. apiKeyExists(keyID string) (APIKey, error)
  621. addAPIKey(apiKey *APIKey) error
  622. updateAPIKey(apiKey *APIKey) error
  623. deleteAPIKey(apiKey APIKey) error
  624. getAPIKeys(limit int, offset int, order string) ([]APIKey, error)
  625. dumpAPIKeys() ([]APIKey, error)
  626. updateAPIKeyLastUse(keyID string) error
  627. shareExists(shareID, username string) (Share, error)
  628. addShare(share *Share) error
  629. updateShare(share *Share) error
  630. deleteShare(share Share) error
  631. getShares(limit int, offset int, order, username string) ([]Share, error)
  632. dumpShares() ([]Share, error)
  633. updateShareLastUse(shareID string, numTokens int) error
  634. getDefenderHosts(from int64, limit int) ([]DefenderEntry, error)
  635. getDefenderHostByIP(ip string, from int64) (DefenderEntry, error)
  636. isDefenderHostBanned(ip string) (DefenderEntry, error)
  637. updateDefenderBanTime(ip string, minutes int) error
  638. deleteDefenderHost(ip string) error
  639. addDefenderEvent(ip string, score int) error
  640. setDefenderBanTime(ip string, banTime int64) error
  641. cleanupDefender(from int64) error
  642. addActiveTransfer(transfer ActiveTransfer) error
  643. updateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) error
  644. removeActiveTransfer(transferID int64, connectionID string) error
  645. cleanupActiveTransfers(before time.Time) error
  646. getActiveTransfers(from time.Time) ([]ActiveTransfer, error)
  647. checkAvailability() error
  648. close() error
  649. reloadConfig() error
  650. initializeDatabase() error
  651. migrateDatabase() error
  652. revertDatabase(targetVersion int) error
  653. resetDatabase() error
  654. }
  655. // SetTempPath sets the path for temporary files
  656. func SetTempPath(fsPath string) {
  657. tempPath = fsPath
  658. }
  659. // Initialize the data provider.
  660. // An error is returned if the configured driver is invalid or if the data provider cannot be initialized
  661. func Initialize(cnf Config, basePath string, checkAdmins bool) error {
  662. var err error
  663. config = cnf
  664. cnf.BackupsPath = getConfigPath(cnf.BackupsPath, basePath)
  665. if cnf.BackupsPath == "" {
  666. return fmt.Errorf("required directory is invalid, backup path %#v", cnf.BackupsPath)
  667. }
  668. credentialsDirPath = getConfigPath(config.CredentialsPath, basePath)
  669. vfs.SetCredentialsDirPath(credentialsDirPath)
  670. if err = initializeHashingAlgo(&cnf); err != nil {
  671. return err
  672. }
  673. if err = validateHooks(); err != nil {
  674. return err
  675. }
  676. err = createProvider(basePath)
  677. if err != nil {
  678. return err
  679. }
  680. if cnf.UpdateMode == 0 {
  681. err = provider.initializeDatabase()
  682. if err != nil && err != ErrNoInitRequired {
  683. logger.WarnToConsole("Unable to initialize data provider: %v", err)
  684. providerLog(logger.LevelError, "Unable to initialize data provider: %v", err)
  685. return err
  686. }
  687. if err == nil {
  688. logger.DebugToConsole("Data provider successfully initialized")
  689. }
  690. err = provider.migrateDatabase()
  691. if err != nil && err != ErrNoInitRequired {
  692. providerLog(logger.LevelError, "database migration error: %v", err)
  693. return err
  694. }
  695. if checkAdmins && cnf.CreateDefaultAdmin {
  696. err = checkDefaultAdmin()
  697. if err != nil {
  698. providerLog(logger.LevelError, "erro checking the default admin: %v", err)
  699. return err
  700. }
  701. }
  702. } else {
  703. providerLog(logger.LevelInfo, "database initialization/migration skipped, manual mode is configured")
  704. }
  705. admins, err := provider.getAdmins(1, 0, OrderASC)
  706. if err != nil {
  707. return err
  708. }
  709. atomic.StoreInt32(&isAdminCreated, int32(len(admins)))
  710. delayedQuotaUpdater.start()
  711. return startScheduler()
  712. }
  713. func validateHooks() error {
  714. var hooks []string
  715. if config.PreLoginHook != "" && !strings.HasPrefix(config.PreLoginHook, "http") {
  716. hooks = append(hooks, config.PreLoginHook)
  717. }
  718. if config.ExternalAuthHook != "" && !strings.HasPrefix(config.ExternalAuthHook, "http") {
  719. hooks = append(hooks, config.ExternalAuthHook)
  720. }
  721. if config.PostLoginHook != "" && !strings.HasPrefix(config.PostLoginHook, "http") {
  722. hooks = append(hooks, config.PostLoginHook)
  723. }
  724. if config.CheckPasswordHook != "" && !strings.HasPrefix(config.CheckPasswordHook, "http") {
  725. hooks = append(hooks, config.CheckPasswordHook)
  726. }
  727. for _, hook := range hooks {
  728. if !filepath.IsAbs(hook) {
  729. return fmt.Errorf("invalid hook: %#v must be an absolute path", hook)
  730. }
  731. _, err := os.Stat(hook)
  732. if err != nil {
  733. providerLog(logger.LevelError, "invalid hook: %v", err)
  734. return err
  735. }
  736. }
  737. return nil
  738. }
  739. // GetBackupsPath returns the normalized backups path
  740. func GetBackupsPath() string {
  741. return config.BackupsPath
  742. }
  743. func initializeHashingAlgo(cnf *Config) error {
  744. argon2Params = &argon2id.Params{
  745. Memory: cnf.PasswordHashing.Argon2Options.Memory,
  746. Iterations: cnf.PasswordHashing.Argon2Options.Iterations,
  747. Parallelism: cnf.PasswordHashing.Argon2Options.Parallelism,
  748. SaltLength: 16,
  749. KeyLength: 32,
  750. }
  751. if config.PasswordHashing.Algo == HashingAlgoBcrypt {
  752. if config.PasswordHashing.BcryptOptions.Cost > bcrypt.MaxCost {
  753. err := fmt.Errorf("invalid bcrypt cost %v, max allowed %v", config.PasswordHashing.BcryptOptions.Cost, bcrypt.MaxCost)
  754. logger.WarnToConsole("Unable to initialize data provider: %v", err)
  755. providerLog(logger.LevelError, "Unable to initialize data provider: %v", err)
  756. return err
  757. }
  758. }
  759. return nil
  760. }
  761. func validateSQLTablesPrefix() error {
  762. if config.SQLTablesPrefix != "" {
  763. for _, char := range config.SQLTablesPrefix {
  764. if !strings.Contains(sqlPrefixValidChars, strings.ToLower(string(char))) {
  765. return errors.New("invalid sql_tables_prefix only chars in range 'a..z', 'A..Z', '0-9' and '_' are allowed")
  766. }
  767. }
  768. sqlTableUsers = config.SQLTablesPrefix + sqlTableUsers
  769. sqlTableFolders = config.SQLTablesPrefix + sqlTableFolders
  770. sqlTableFoldersMapping = config.SQLTablesPrefix + sqlTableFoldersMapping
  771. sqlTableAdmins = config.SQLTablesPrefix + sqlTableAdmins
  772. sqlTableAPIKeys = config.SQLTablesPrefix + sqlTableAPIKeys
  773. sqlTableShares = config.SQLTablesPrefix + sqlTableShares
  774. sqlTableDefenderEvents = config.SQLTablesPrefix + sqlTableDefenderEvents
  775. sqlTableDefenderHosts = config.SQLTablesPrefix + sqlTableDefenderHosts
  776. sqlTableActiveTransfers = config.SQLTablesPrefix + sqlTableActiveTransfers
  777. sqlTableSchemaVersion = config.SQLTablesPrefix + sqlTableSchemaVersion
  778. providerLog(logger.LevelDebug, "sql table for users %#v, folders %#v folders mapping %#v admins %#v "+
  779. "api keys %#v shares %#v defender hosts %#v defender events %#v transfers %#v schema version %#v",
  780. sqlTableUsers, sqlTableFolders, sqlTableFoldersMapping, sqlTableAdmins, sqlTableAPIKeys,
  781. sqlTableShares, sqlTableDefenderHosts, sqlTableDefenderEvents, sqlTableActiveTransfers, sqlTableSchemaVersion)
  782. }
  783. return nil
  784. }
  785. func checkDefaultAdmin() error {
  786. admins, err := provider.getAdmins(1, 0, OrderASC)
  787. if err != nil {
  788. return err
  789. }
  790. if len(admins) > 0 {
  791. return nil
  792. }
  793. logger.Debug(logSender, "", "no admins found, try to create the default one")
  794. // we need to create the default admin
  795. admin := &Admin{}
  796. if err := admin.setFromEnv(); err != nil {
  797. return err
  798. }
  799. return provider.addAdmin(admin)
  800. }
  801. // InitializeDatabase creates the initial database structure
  802. func InitializeDatabase(cnf Config, basePath string) error {
  803. config = cnf
  804. if filepath.IsAbs(config.CredentialsPath) {
  805. credentialsDirPath = config.CredentialsPath
  806. } else {
  807. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  808. }
  809. vfs.SetCredentialsDirPath(credentialsDirPath)
  810. if err := initializeHashingAlgo(&cnf); err != nil {
  811. return err
  812. }
  813. err := createProvider(basePath)
  814. if err != nil {
  815. return err
  816. }
  817. err = provider.initializeDatabase()
  818. if err != nil && err != ErrNoInitRequired {
  819. return err
  820. }
  821. return provider.migrateDatabase()
  822. }
  823. // RevertDatabase restores schema and/or data to a previous version
  824. func RevertDatabase(cnf Config, basePath string, targetVersion int) error {
  825. config = cnf
  826. if filepath.IsAbs(config.CredentialsPath) {
  827. credentialsDirPath = config.CredentialsPath
  828. } else {
  829. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  830. }
  831. err := createProvider(basePath)
  832. if err != nil {
  833. return err
  834. }
  835. err = provider.initializeDatabase()
  836. if err != nil && err != ErrNoInitRequired {
  837. return err
  838. }
  839. return provider.revertDatabase(targetVersion)
  840. }
  841. // ResetDatabase restores schema and/or data to a previous version
  842. func ResetDatabase(cnf Config, basePath string) error {
  843. config = cnf
  844. if filepath.IsAbs(config.CredentialsPath) {
  845. credentialsDirPath = config.CredentialsPath
  846. } else {
  847. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  848. }
  849. if err := createProvider(basePath); err != nil {
  850. return err
  851. }
  852. return provider.resetDatabase()
  853. }
  854. // CheckAdminAndPass validates the given admin and password connecting from ip
  855. func CheckAdminAndPass(username, password, ip string) (Admin, error) {
  856. username = config.convertName(username)
  857. return provider.validateAdminAndPass(username, password, ip)
  858. }
  859. // CheckCachedUserCredentials checks the credentials for a cached user
  860. func CheckCachedUserCredentials(user *CachedUser, password, loginMethod, protocol string, tlsCert *x509.Certificate) error {
  861. if loginMethod != LoginMethodPassword {
  862. _, err := checkUserAndTLSCertificate(&user.User, protocol, tlsCert)
  863. if err != nil {
  864. return err
  865. }
  866. if loginMethod == LoginMethodTLSCertificate {
  867. if !user.User.IsLoginMethodAllowed(LoginMethodTLSCertificate, protocol, nil) {
  868. return fmt.Errorf("certificate login method is not allowed for user %#v", user.User.Username)
  869. }
  870. return nil
  871. }
  872. }
  873. if err := user.User.CheckLoginConditions(); err != nil {
  874. return err
  875. }
  876. if password == "" {
  877. return ErrInvalidCredentials
  878. }
  879. if user.Password != "" {
  880. if password == user.Password {
  881. return nil
  882. }
  883. } else {
  884. if ok, _ := isPasswordOK(&user.User, password); ok {
  885. return nil
  886. }
  887. }
  888. return ErrInvalidCredentials
  889. }
  890. // CheckCompositeCredentials checks multiple credentials.
  891. // WebDAV users can send both a password and a TLS certificate within the same request
  892. func CheckCompositeCredentials(username, password, ip, loginMethod, protocol string, tlsCert *x509.Certificate) (User, string, error) {
  893. username = config.convertName(username)
  894. if loginMethod == LoginMethodPassword {
  895. user, err := CheckUserAndPass(username, password, ip, protocol)
  896. return user, loginMethod, err
  897. }
  898. user, err := CheckUserBeforeTLSAuth(username, ip, protocol, tlsCert)
  899. if err != nil {
  900. return user, loginMethod, err
  901. }
  902. if !user.IsTLSUsernameVerificationEnabled() {
  903. // for backward compatibility with 2.0.x we only check the password and change the login method here
  904. // in future updates we have to return an error
  905. user, err := CheckUserAndPass(username, password, ip, protocol)
  906. return user, LoginMethodPassword, err
  907. }
  908. user, err = checkUserAndTLSCertificate(&user, protocol, tlsCert)
  909. if err != nil {
  910. return user, loginMethod, err
  911. }
  912. if loginMethod == LoginMethodTLSCertificate && !user.IsLoginMethodAllowed(LoginMethodTLSCertificate, protocol, nil) {
  913. return user, loginMethod, fmt.Errorf("certificate login method is not allowed for user %#v", user.Username)
  914. }
  915. if loginMethod == LoginMethodTLSCertificateAndPwd {
  916. if plugin.Handler.HasAuthScope(plugin.AuthScopePassword) {
  917. user, err = doPluginAuth(username, password, nil, ip, protocol, nil, plugin.AuthScopePassword)
  918. } else if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
  919. user, err = doExternalAuth(username, password, nil, "", ip, protocol, nil)
  920. } else if config.PreLoginHook != "" {
  921. user, err = executePreLoginHook(username, LoginMethodPassword, ip, protocol, nil)
  922. }
  923. if err != nil {
  924. return user, loginMethod, err
  925. }
  926. user, err = checkUserAndPass(&user, password, ip, protocol)
  927. }
  928. return user, loginMethod, err
  929. }
  930. // CheckUserBeforeTLSAuth checks if a user exits before trying mutual TLS
  931. func CheckUserBeforeTLSAuth(username, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
  932. username = config.convertName(username)
  933. if plugin.Handler.HasAuthScope(plugin.AuthScopeTLSCertificate) {
  934. user, err := doPluginAuth(username, "", nil, ip, protocol, tlsCert, plugin.AuthScopeTLSCertificate)
  935. if err != nil {
  936. return user, err
  937. }
  938. err = user.LoadAndApplyGroupSettings()
  939. return user, err
  940. }
  941. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&8 != 0) {
  942. user, err := doExternalAuth(username, "", nil, "", ip, protocol, tlsCert)
  943. if err != nil {
  944. return user, err
  945. }
  946. err = user.LoadAndApplyGroupSettings()
  947. return user, err
  948. }
  949. if config.PreLoginHook != "" {
  950. user, err := executePreLoginHook(username, LoginMethodTLSCertificate, ip, protocol, nil)
  951. if err != nil {
  952. return user, err
  953. }
  954. err = user.LoadAndApplyGroupSettings()
  955. return user, err
  956. }
  957. user, err := UserExists(username)
  958. if err != nil {
  959. return user, err
  960. }
  961. err = user.LoadAndApplyGroupSettings()
  962. return user, err
  963. }
  964. // CheckUserAndTLSCert returns the SFTPGo user with the given username and check if the
  965. // given TLS certificate allow authentication without password
  966. func CheckUserAndTLSCert(username, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
  967. username = config.convertName(username)
  968. if plugin.Handler.HasAuthScope(plugin.AuthScopeTLSCertificate) {
  969. user, err := doPluginAuth(username, "", nil, ip, protocol, tlsCert, plugin.AuthScopeTLSCertificate)
  970. if err != nil {
  971. return user, err
  972. }
  973. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  974. }
  975. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&8 != 0) {
  976. user, err := doExternalAuth(username, "", nil, "", ip, protocol, tlsCert)
  977. if err != nil {
  978. return user, err
  979. }
  980. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  981. }
  982. if config.PreLoginHook != "" {
  983. user, err := executePreLoginHook(username, LoginMethodTLSCertificate, ip, protocol, nil)
  984. if err != nil {
  985. return user, err
  986. }
  987. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  988. }
  989. return provider.validateUserAndTLSCert(username, protocol, tlsCert)
  990. }
  991. // CheckUserAndPass retrieves the SFTPGo user with the given username and password if a match is found or an error
  992. func CheckUserAndPass(username, password, ip, protocol string) (User, error) {
  993. username = config.convertName(username)
  994. if plugin.Handler.HasAuthScope(plugin.AuthScopePassword) {
  995. user, err := doPluginAuth(username, password, nil, ip, protocol, nil, plugin.AuthScopePassword)
  996. if err != nil {
  997. return user, err
  998. }
  999. return checkUserAndPass(&user, password, ip, protocol)
  1000. }
  1001. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
  1002. user, err := doExternalAuth(username, password, nil, "", ip, protocol, nil)
  1003. if err != nil {
  1004. return user, err
  1005. }
  1006. return checkUserAndPass(&user, password, ip, protocol)
  1007. }
  1008. if config.PreLoginHook != "" {
  1009. user, err := executePreLoginHook(username, LoginMethodPassword, ip, protocol, nil)
  1010. if err != nil {
  1011. return user, err
  1012. }
  1013. return checkUserAndPass(&user, password, ip, protocol)
  1014. }
  1015. return provider.validateUserAndPass(username, password, ip, protocol)
  1016. }
  1017. // CheckUserAndPubKey retrieves the SFTP user with the given username and public key if a match is found or an error
  1018. func CheckUserAndPubKey(username string, pubKey []byte, ip, protocol string, isSSHCert bool) (User, string, error) {
  1019. username = config.convertName(username)
  1020. if plugin.Handler.HasAuthScope(plugin.AuthScopePublicKey) {
  1021. user, err := doPluginAuth(username, "", pubKey, ip, protocol, nil, plugin.AuthScopePublicKey)
  1022. if err != nil {
  1023. return user, "", err
  1024. }
  1025. return checkUserAndPubKey(&user, pubKey, isSSHCert)
  1026. }
  1027. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&2 != 0) {
  1028. user, err := doExternalAuth(username, "", pubKey, "", ip, protocol, nil)
  1029. if err != nil {
  1030. return user, "", err
  1031. }
  1032. return checkUserAndPubKey(&user, pubKey, isSSHCert)
  1033. }
  1034. if config.PreLoginHook != "" {
  1035. user, err := executePreLoginHook(username, SSHLoginMethodPublicKey, ip, protocol, nil)
  1036. if err != nil {
  1037. return user, "", err
  1038. }
  1039. return checkUserAndPubKey(&user, pubKey, isSSHCert)
  1040. }
  1041. return provider.validateUserAndPubKey(username, pubKey, isSSHCert)
  1042. }
  1043. // CheckKeyboardInteractiveAuth checks the keyboard interactive authentication and returns
  1044. // the authenticated user or an error
  1045. func CheckKeyboardInteractiveAuth(username, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (User, error) {
  1046. var user User
  1047. var err error
  1048. username = config.convertName(username)
  1049. if plugin.Handler.HasAuthScope(plugin.AuthScopeKeyboardInteractive) {
  1050. user, err = doPluginAuth(username, "", nil, ip, protocol, nil, plugin.AuthScopeKeyboardInteractive)
  1051. } else if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&4 != 0) {
  1052. user, err = doExternalAuth(username, "", nil, "1", ip, protocol, nil)
  1053. } else if config.PreLoginHook != "" {
  1054. user, err = executePreLoginHook(username, SSHLoginMethodKeyboardInteractive, ip, protocol, nil)
  1055. } else {
  1056. user, err = provider.userExists(username)
  1057. }
  1058. if err != nil {
  1059. return user, err
  1060. }
  1061. return doKeyboardInteractiveAuth(&user, authHook, client, ip, protocol)
  1062. }
  1063. // GetUserAfterIDPAuth returns the SFTPGo user with the specified username
  1064. // after a successful authentication with an external identity provider.
  1065. // If a pre-login hook is defined it will be executed so the SFTPGo user
  1066. // can be created if it does not exist
  1067. func GetUserAfterIDPAuth(username, ip, protocol string, oidcTokenFields *map[string]interface{}) (User, error) {
  1068. var user User
  1069. var err error
  1070. if config.PreLoginHook != "" {
  1071. user, err = executePreLoginHook(username, LoginMethodIDP, ip, protocol, oidcTokenFields)
  1072. } else {
  1073. user, err = UserExists(username)
  1074. }
  1075. if err != nil {
  1076. return user, err
  1077. }
  1078. err = user.LoadAndApplyGroupSettings()
  1079. return user, err
  1080. }
  1081. // GetDefenderHosts returns hosts that are banned or for which some violations have been detected
  1082. func GetDefenderHosts(from int64, limit int) ([]DefenderEntry, error) {
  1083. return provider.getDefenderHosts(from, limit)
  1084. }
  1085. // GetDefenderHostByIP returns a defender host by ip, if any
  1086. func GetDefenderHostByIP(ip string, from int64) (DefenderEntry, error) {
  1087. return provider.getDefenderHostByIP(ip, from)
  1088. }
  1089. // IsDefenderHostBanned returns a defender entry and no error if the specified host is banned
  1090. func IsDefenderHostBanned(ip string) (DefenderEntry, error) {
  1091. return provider.isDefenderHostBanned(ip)
  1092. }
  1093. // UpdateDefenderBanTime increments ban time for the specified ip
  1094. func UpdateDefenderBanTime(ip string, minutes int) error {
  1095. return provider.updateDefenderBanTime(ip, minutes)
  1096. }
  1097. // DeleteDefenderHost removes the specified IP from the defender lists
  1098. func DeleteDefenderHost(ip string) error {
  1099. return provider.deleteDefenderHost(ip)
  1100. }
  1101. // AddDefenderEvent adds an event for the given IP with the given score
  1102. // and returns the host with the updated score
  1103. func AddDefenderEvent(ip string, score int, from int64) (DefenderEntry, error) {
  1104. if err := provider.addDefenderEvent(ip, score); err != nil {
  1105. return DefenderEntry{}, err
  1106. }
  1107. return provider.getDefenderHostByIP(ip, from)
  1108. }
  1109. // SetDefenderBanTime sets the ban time for the specified IP
  1110. func SetDefenderBanTime(ip string, banTime int64) error {
  1111. return provider.setDefenderBanTime(ip, banTime)
  1112. }
  1113. // CleanupDefender removes events and hosts older than "from" from the data provider
  1114. func CleanupDefender(from int64) error {
  1115. return provider.cleanupDefender(from)
  1116. }
  1117. // UpdateShareLastUse updates the LastUseAt and UsedTokens for the given share
  1118. func UpdateShareLastUse(share *Share, numTokens int) error {
  1119. return provider.updateShareLastUse(share.ShareID, numTokens)
  1120. }
  1121. // UpdateAPIKeyLastUse updates the LastUseAt field for the given API key
  1122. func UpdateAPIKeyLastUse(apiKey *APIKey) error {
  1123. lastUse := util.GetTimeFromMsecSinceEpoch(apiKey.LastUseAt)
  1124. diff := -time.Until(lastUse)
  1125. if diff < 0 || diff > lastLoginMinDelay {
  1126. return provider.updateAPIKeyLastUse(apiKey.KeyID)
  1127. }
  1128. return nil
  1129. }
  1130. // UpdateLastLogin updates the last login field for the given SFTPGo user
  1131. func UpdateLastLogin(user *User) {
  1132. delay := lastLoginMinDelay
  1133. if user.Filters.ExternalAuthCacheTime > 0 {
  1134. delay = time.Duration(user.Filters.ExternalAuthCacheTime) * time.Second
  1135. }
  1136. if user.LastLogin <= user.UpdatedAt || !isLastActivityRecent(user.LastLogin, delay) {
  1137. err := provider.updateLastLogin(user.Username)
  1138. if err == nil {
  1139. webDAVUsersCache.updateLastLogin(user.Username)
  1140. }
  1141. }
  1142. }
  1143. // UpdateAdminLastLogin updates the last login field for the given SFTPGo admin
  1144. func UpdateAdminLastLogin(admin *Admin) {
  1145. if !isLastActivityRecent(admin.LastLogin, lastLoginMinDelay) {
  1146. provider.updateAdminLastLogin(admin.Username) //nolint:errcheck
  1147. }
  1148. }
  1149. // UpdateUserQuota updates the quota for the given SFTPGo user adding filesAdd and sizeAdd.
  1150. // If reset is true filesAdd and sizeAdd indicates the total files and the total size instead of the difference.
  1151. func UpdateUserQuota(user *User, filesAdd int, sizeAdd int64, reset bool) error {
  1152. if config.TrackQuota == 0 {
  1153. return util.NewMethodDisabledError(trackQuotaDisabledError)
  1154. } else if config.TrackQuota == 2 && !reset && !user.HasQuotaRestrictions() {
  1155. return nil
  1156. }
  1157. if filesAdd == 0 && sizeAdd == 0 && !reset {
  1158. return nil
  1159. }
  1160. if config.DelayedQuotaUpdate == 0 || reset {
  1161. if reset {
  1162. delayedQuotaUpdater.resetUserQuota(user.Username)
  1163. }
  1164. return provider.updateQuota(user.Username, filesAdd, sizeAdd, reset)
  1165. }
  1166. delayedQuotaUpdater.updateUserQuota(user.Username, filesAdd, sizeAdd)
  1167. return nil
  1168. }
  1169. // UpdateVirtualFolderQuota updates the quota for the given virtual folder adding filesAdd and sizeAdd.
  1170. // If reset is true filesAdd and sizeAdd indicates the total files and the total size instead of the difference.
  1171. func UpdateVirtualFolderQuota(vfolder *vfs.BaseVirtualFolder, filesAdd int, sizeAdd int64, reset bool) error {
  1172. if config.TrackQuota == 0 {
  1173. return util.NewMethodDisabledError(trackQuotaDisabledError)
  1174. }
  1175. if filesAdd == 0 && sizeAdd == 0 && !reset {
  1176. return nil
  1177. }
  1178. if config.DelayedQuotaUpdate == 0 || reset {
  1179. if reset {
  1180. delayedQuotaUpdater.resetFolderQuota(vfolder.Name)
  1181. }
  1182. return provider.updateFolderQuota(vfolder.Name, filesAdd, sizeAdd, reset)
  1183. }
  1184. delayedQuotaUpdater.updateFolderQuota(vfolder.Name, filesAdd, sizeAdd)
  1185. return nil
  1186. }
  1187. // UpdateUserTransferQuota updates the transfer quota for the given SFTPGo user.
  1188. // If reset is true uploadSize and downloadSize indicates the actual sizes instead of the difference.
  1189. func UpdateUserTransferQuota(user *User, uploadSize, downloadSize int64, reset bool) error {
  1190. if config.TrackQuota == 0 {
  1191. return util.NewMethodDisabledError(trackQuotaDisabledError)
  1192. } else if config.TrackQuota == 2 && !reset && !user.HasTransferQuotaRestrictions() {
  1193. return nil
  1194. }
  1195. if downloadSize == 0 && uploadSize == 0 && !reset {
  1196. return nil
  1197. }
  1198. if config.DelayedQuotaUpdate == 0 || reset {
  1199. if reset {
  1200. delayedQuotaUpdater.resetUserTransferQuota(user.Username)
  1201. }
  1202. return provider.updateTransferQuota(user.Username, uploadSize, downloadSize, reset)
  1203. }
  1204. delayedQuotaUpdater.updateUserTransferQuota(user.Username, uploadSize, downloadSize)
  1205. return nil
  1206. }
  1207. // GetUsedQuota returns the used quota for the given SFTPGo user.
  1208. func GetUsedQuota(username string) (int, int64, int64, int64, error) {
  1209. if config.TrackQuota == 0 {
  1210. return 0, 0, 0, 0, util.NewMethodDisabledError(trackQuotaDisabledError)
  1211. }
  1212. files, size, ulTransferSize, dlTransferSize, err := provider.getUsedQuota(username)
  1213. if err != nil {
  1214. return files, size, ulTransferSize, dlTransferSize, err
  1215. }
  1216. delayedFiles, delayedSize := delayedQuotaUpdater.getUserPendingQuota(username)
  1217. delayedUlTransferSize, delayedDLTransferSize := delayedQuotaUpdater.getUserPendingTransferQuota(username)
  1218. return files + delayedFiles, size + delayedSize, ulTransferSize + delayedUlTransferSize,
  1219. dlTransferSize + delayedDLTransferSize, err
  1220. }
  1221. // GetUsedVirtualFolderQuota returns the used quota for the given virtual folder.
  1222. func GetUsedVirtualFolderQuota(name string) (int, int64, error) {
  1223. if config.TrackQuota == 0 {
  1224. return 0, 0, util.NewMethodDisabledError(trackQuotaDisabledError)
  1225. }
  1226. files, size, err := provider.getUsedFolderQuota(name)
  1227. if err != nil {
  1228. return files, size, err
  1229. }
  1230. delayedFiles, delayedSize := delayedQuotaUpdater.getFolderPendingQuota(name)
  1231. return files + delayedFiles, size + delayedSize, err
  1232. }
  1233. // AddShare adds a new share
  1234. func AddShare(share *Share, executor, ipAddress string) error {
  1235. err := provider.addShare(share)
  1236. if err == nil {
  1237. executeAction(operationAdd, executor, ipAddress, actionObjectShare, share.ShareID, share)
  1238. }
  1239. return err
  1240. }
  1241. // UpdateShare updates an existing share
  1242. func UpdateShare(share *Share, executor, ipAddress string) error {
  1243. err := provider.updateShare(share)
  1244. if err == nil {
  1245. executeAction(operationUpdate, executor, ipAddress, actionObjectShare, share.ShareID, share)
  1246. }
  1247. return err
  1248. }
  1249. // DeleteShare deletes an existing share
  1250. func DeleteShare(shareID string, executor, ipAddress string) error {
  1251. share, err := provider.shareExists(shareID, executor)
  1252. if err != nil {
  1253. return err
  1254. }
  1255. err = provider.deleteShare(share)
  1256. if err == nil {
  1257. executeAction(operationDelete, executor, ipAddress, actionObjectShare, shareID, &share)
  1258. }
  1259. return err
  1260. }
  1261. // ShareExists returns the share with the given ID if it exists
  1262. func ShareExists(shareID, username string) (Share, error) {
  1263. if shareID == "" {
  1264. return Share{}, util.NewRecordNotFoundError(fmt.Sprintf("Share %#v does not exist", shareID))
  1265. }
  1266. return provider.shareExists(shareID, username)
  1267. }
  1268. // AddGroup adds a new group
  1269. func AddGroup(group *Group, executor, ipAddress string) error {
  1270. group.Name = config.convertName(group.Name)
  1271. err := provider.addGroup(group)
  1272. if err == nil {
  1273. executeAction(operationAdd, executor, ipAddress, actionObjectGroup, group.Name, group)
  1274. }
  1275. return err
  1276. }
  1277. // UpdateGroup updates an existing Group
  1278. func UpdateGroup(group *Group, users []string, executor, ipAddress string) error {
  1279. err := provider.updateGroup(group)
  1280. if err == nil {
  1281. for _, user := range users {
  1282. provider.setUpdatedAt(user)
  1283. u, err := provider.userExists(user)
  1284. if err == nil {
  1285. webDAVUsersCache.swap(&u)
  1286. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, &u)
  1287. } else {
  1288. RemoveCachedWebDAVUser(user)
  1289. }
  1290. }
  1291. executeAction(operationUpdate, executor, ipAddress, actionObjectGroup, group.Name, group)
  1292. }
  1293. return err
  1294. }
  1295. // DeleteGroup deletes an existing Group
  1296. func DeleteGroup(name string, executor, ipAddress string) error {
  1297. name = config.convertName(name)
  1298. group, err := provider.groupExists(name)
  1299. if err != nil {
  1300. return err
  1301. }
  1302. if len(group.Users) > 0 {
  1303. errorString := fmt.Sprintf("the group %#v is referenced, it cannot be removed", group.Name)
  1304. return util.NewValidationError(errorString)
  1305. }
  1306. err = provider.deleteGroup(group)
  1307. if err == nil {
  1308. for _, user := range group.Users {
  1309. provider.setUpdatedAt(user)
  1310. u, err := provider.userExists(user)
  1311. if err == nil {
  1312. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, &u)
  1313. }
  1314. RemoveCachedWebDAVUser(user)
  1315. }
  1316. executeAction(operationDelete, executor, ipAddress, actionObjectGroup, group.Name, &group)
  1317. }
  1318. return err
  1319. }
  1320. // GroupExists returns the Group with the given name if it exists
  1321. func GroupExists(name string) (Group, error) {
  1322. name = config.convertName(name)
  1323. return provider.groupExists(name)
  1324. }
  1325. // AddAPIKey adds a new API key
  1326. func AddAPIKey(apiKey *APIKey, executor, ipAddress string) error {
  1327. err := provider.addAPIKey(apiKey)
  1328. if err == nil {
  1329. executeAction(operationAdd, executor, ipAddress, actionObjectAPIKey, apiKey.KeyID, apiKey)
  1330. }
  1331. return err
  1332. }
  1333. // UpdateAPIKey updates an existing API key
  1334. func UpdateAPIKey(apiKey *APIKey, executor, ipAddress string) error {
  1335. err := provider.updateAPIKey(apiKey)
  1336. if err == nil {
  1337. executeAction(operationUpdate, executor, ipAddress, actionObjectAPIKey, apiKey.KeyID, apiKey)
  1338. }
  1339. return err
  1340. }
  1341. // DeleteAPIKey deletes an existing API key
  1342. func DeleteAPIKey(keyID string, executor, ipAddress string) error {
  1343. apiKey, err := provider.apiKeyExists(keyID)
  1344. if err != nil {
  1345. return err
  1346. }
  1347. err = provider.deleteAPIKey(apiKey)
  1348. if err == nil {
  1349. executeAction(operationDelete, executor, ipAddress, actionObjectAPIKey, apiKey.KeyID, &apiKey)
  1350. }
  1351. return err
  1352. }
  1353. // APIKeyExists returns the API key with the given ID if it exists
  1354. func APIKeyExists(keyID string) (APIKey, error) {
  1355. if keyID == "" {
  1356. return APIKey{}, util.NewRecordNotFoundError(fmt.Sprintf("API key %#v does not exist", keyID))
  1357. }
  1358. return provider.apiKeyExists(keyID)
  1359. }
  1360. // HasAdmin returns true if the first admin has been created
  1361. // and so SFTPGo is ready to be used
  1362. func HasAdmin() bool {
  1363. return atomic.LoadInt32(&isAdminCreated) > 0
  1364. }
  1365. // AddAdmin adds a new SFTPGo admin
  1366. func AddAdmin(admin *Admin, executor, ipAddress string) error {
  1367. admin.Filters.RecoveryCodes = nil
  1368. admin.Filters.TOTPConfig = AdminTOTPConfig{
  1369. Enabled: false,
  1370. }
  1371. admin.Username = config.convertName(admin.Username)
  1372. err := provider.addAdmin(admin)
  1373. if err == nil {
  1374. atomic.StoreInt32(&isAdminCreated, 1)
  1375. executeAction(operationAdd, executor, ipAddress, actionObjectAdmin, admin.Username, admin)
  1376. }
  1377. return err
  1378. }
  1379. // UpdateAdmin updates an existing SFTPGo admin
  1380. func UpdateAdmin(admin *Admin, executor, ipAddress string) error {
  1381. err := provider.updateAdmin(admin)
  1382. if err == nil {
  1383. executeAction(operationUpdate, executor, ipAddress, actionObjectAdmin, admin.Username, admin)
  1384. }
  1385. return err
  1386. }
  1387. // DeleteAdmin deletes an existing SFTPGo admin
  1388. func DeleteAdmin(username, executor, ipAddress string) error {
  1389. username = config.convertName(username)
  1390. admin, err := provider.adminExists(username)
  1391. if err != nil {
  1392. return err
  1393. }
  1394. err = provider.deleteAdmin(admin)
  1395. if err == nil {
  1396. executeAction(operationDelete, executor, ipAddress, actionObjectAdmin, admin.Username, &admin)
  1397. }
  1398. return err
  1399. }
  1400. // AdminExists returns the admin with the given username if it exists
  1401. func AdminExists(username string) (Admin, error) {
  1402. username = config.convertName(username)
  1403. return provider.adminExists(username)
  1404. }
  1405. // UserExists checks if the given SFTPGo username exists, returns an error if no match is found
  1406. func UserExists(username string) (User, error) {
  1407. username = config.convertName(username)
  1408. return provider.userExists(username)
  1409. }
  1410. // GetUserWithGroupSettings tries to return the user with the specified username
  1411. // loading also the group settings
  1412. func GetUserWithGroupSettings(username string) (User, error) {
  1413. username = config.convertName(username)
  1414. user, err := provider.userExists(username)
  1415. if err != nil {
  1416. return user, err
  1417. }
  1418. err = user.LoadAndApplyGroupSettings()
  1419. return user, err
  1420. }
  1421. // GetUserVariants tries to return the user with the specified username with and without
  1422. // group settings applied
  1423. func GetUserVariants(username string) (User, User, error) {
  1424. username = config.convertName(username)
  1425. user, err := provider.userExists(username)
  1426. if err != nil {
  1427. return user, User{}, err
  1428. }
  1429. userWithGroupSettings := user.getACopy()
  1430. err = userWithGroupSettings.LoadAndApplyGroupSettings()
  1431. return user, userWithGroupSettings, err
  1432. }
  1433. // AddUser adds a new SFTPGo user.
  1434. func AddUser(user *User, executor, ipAddress string) error {
  1435. user.Filters.RecoveryCodes = nil
  1436. user.Filters.TOTPConfig = UserTOTPConfig{
  1437. Enabled: false,
  1438. }
  1439. user.Username = config.convertName(user.Username)
  1440. err := provider.addUser(user)
  1441. if err == nil {
  1442. executeAction(operationAdd, executor, ipAddress, actionObjectUser, user.Username, user)
  1443. }
  1444. return err
  1445. }
  1446. // UpdateUserPassword updates the user password
  1447. func UpdateUserPassword(username, plainPwd, executor, ipAddress string) error {
  1448. hashedPwd, err := hashPlainPassword(plainPwd)
  1449. if err != nil {
  1450. return util.NewGenericError(fmt.Sprintf("unable to set the new password: %v", err))
  1451. }
  1452. err = provider.updateUserPassword(username, hashedPwd)
  1453. if err != nil {
  1454. return util.NewGenericError(fmt.Sprintf("unable to set the new password: %v", err))
  1455. }
  1456. cachedPasswords.Remove(username)
  1457. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, username, &User{})
  1458. return nil
  1459. }
  1460. // UpdateUser updates an existing SFTPGo user.
  1461. func UpdateUser(user *User, executor, ipAddress string) error {
  1462. if user.groupSettingsApplied {
  1463. return errors.New("cannot save a user with group settings applied")
  1464. }
  1465. err := provider.updateUser(user)
  1466. if err == nil {
  1467. webDAVUsersCache.swap(user)
  1468. cachedPasswords.Remove(user.Username)
  1469. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, user.Username, user)
  1470. }
  1471. return err
  1472. }
  1473. // DeleteUser deletes an existing SFTPGo user.
  1474. func DeleteUser(username, executor, ipAddress string) error {
  1475. username = config.convertName(username)
  1476. user, err := provider.userExists(username)
  1477. if err != nil {
  1478. return err
  1479. }
  1480. err = provider.deleteUser(user)
  1481. if err == nil {
  1482. RemoveCachedWebDAVUser(user.Username)
  1483. delayedQuotaUpdater.resetUserQuota(username)
  1484. cachedPasswords.Remove(username)
  1485. executeAction(operationDelete, executor, ipAddress, actionObjectUser, user.Username, &user)
  1486. }
  1487. return err
  1488. }
  1489. // AddActiveTransfer stores the specified transfer
  1490. func AddActiveTransfer(transfer ActiveTransfer) {
  1491. if err := provider.addActiveTransfer(transfer); err != nil {
  1492. providerLog(logger.LevelError, "unable to add transfer id %v, connection id %v: %v",
  1493. transfer.ID, transfer.ConnID, err)
  1494. }
  1495. }
  1496. // UpdateActiveTransferSizes updates the current upload and download sizes for the specified transfer
  1497. func UpdateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) {
  1498. if err := provider.updateActiveTransferSizes(ulSize, dlSize, transferID, connectionID); err != nil {
  1499. providerLog(logger.LevelError, "unable to update sizes for transfer id %v, connection id %v: %v",
  1500. transferID, connectionID, err)
  1501. }
  1502. }
  1503. // RemoveActiveTransfer removes the specified transfer
  1504. func RemoveActiveTransfer(transferID int64, connectionID string) {
  1505. if err := provider.removeActiveTransfer(transferID, connectionID); err != nil {
  1506. providerLog(logger.LevelError, "unable to delete transfer id %v, connection id %v: %v",
  1507. transferID, connectionID, err)
  1508. }
  1509. }
  1510. // CleanupActiveTransfers removes the transfer before the specified time
  1511. func CleanupActiveTransfers(before time.Time) error {
  1512. err := provider.cleanupActiveTransfers(before)
  1513. if err == nil {
  1514. providerLog(logger.LevelDebug, "deleted active transfers updated before: %v", before)
  1515. } else {
  1516. providerLog(logger.LevelError, "error deleting active transfers updated before %v: %v", before, err)
  1517. }
  1518. return err
  1519. }
  1520. // GetActiveTransfers retrieves the active transfers with an update time after the specified value
  1521. func GetActiveTransfers(from time.Time) ([]ActiveTransfer, error) {
  1522. return provider.getActiveTransfers(from)
  1523. }
  1524. // ReloadConfig reloads provider configuration.
  1525. // Currently only implemented for memory provider, allows to reload the users
  1526. // from the configured file, if defined
  1527. func ReloadConfig() error {
  1528. return provider.reloadConfig()
  1529. }
  1530. // GetShares returns an array of shares respecting limit and offset
  1531. func GetShares(limit, offset int, order, username string) ([]Share, error) {
  1532. return provider.getShares(limit, offset, order, username)
  1533. }
  1534. // GetAPIKeys returns an array of API keys respecting limit and offset
  1535. func GetAPIKeys(limit, offset int, order string) ([]APIKey, error) {
  1536. return provider.getAPIKeys(limit, offset, order)
  1537. }
  1538. // GetAdmins returns an array of admins respecting limit and offset
  1539. func GetAdmins(limit, offset int, order string) ([]Admin, error) {
  1540. return provider.getAdmins(limit, offset, order)
  1541. }
  1542. // GetGroups returns an array of groups respecting limit and offset
  1543. func GetGroups(limit, offset int, order string, minimal bool) ([]Group, error) {
  1544. return provider.getGroups(limit, offset, order, minimal)
  1545. }
  1546. // GetUsers returns an array of users respecting limit and offset
  1547. func GetUsers(limit, offset int, order string) ([]User, error) {
  1548. return provider.getUsers(limit, offset, order)
  1549. }
  1550. // GetUsersForQuotaCheck returns the users with the fields required for a quota check
  1551. func GetUsersForQuotaCheck(toFetch map[string]bool) ([]User, error) {
  1552. return provider.getUsersForQuotaCheck(toFetch)
  1553. }
  1554. // AddFolder adds a new virtual folder.
  1555. func AddFolder(folder *vfs.BaseVirtualFolder) error {
  1556. folder.Name = config.convertName(folder.Name)
  1557. return provider.addFolder(folder)
  1558. }
  1559. // UpdateFolder updates the specified virtual folder
  1560. func UpdateFolder(folder *vfs.BaseVirtualFolder, users []string, groups []string, executor, ipAddress string) error {
  1561. err := provider.updateFolder(folder)
  1562. if err == nil {
  1563. usersInGroups, errGrp := provider.getUsersInGroups(groups)
  1564. if errGrp == nil {
  1565. users = append(users, usersInGroups...)
  1566. users = util.RemoveDuplicates(users)
  1567. } else {
  1568. providerLog(logger.LevelWarn, "unable to get users in groups %+v: %v", groups, errGrp)
  1569. }
  1570. for _, user := range users {
  1571. provider.setUpdatedAt(user)
  1572. u, err := provider.userExists(user)
  1573. if err == nil {
  1574. webDAVUsersCache.swap(&u)
  1575. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, &u)
  1576. } else {
  1577. RemoveCachedWebDAVUser(user)
  1578. }
  1579. }
  1580. }
  1581. return err
  1582. }
  1583. // DeleteFolder deletes an existing folder.
  1584. func DeleteFolder(folderName, executor, ipAddress string) error {
  1585. folderName = config.convertName(folderName)
  1586. folder, err := provider.getFolderByName(folderName)
  1587. if err != nil {
  1588. return err
  1589. }
  1590. err = provider.deleteFolder(folder)
  1591. if err == nil {
  1592. users := folder.Users
  1593. usersInGroups, errGrp := provider.getUsersInGroups(folder.Groups)
  1594. if errGrp == nil {
  1595. users = append(users, usersInGroups...)
  1596. users = util.RemoveDuplicates(users)
  1597. } else {
  1598. providerLog(logger.LevelWarn, "unable to get users in groups %+v: %v", folder.Groups, errGrp)
  1599. }
  1600. for _, user := range users {
  1601. provider.setUpdatedAt(user)
  1602. u, err := provider.userExists(user)
  1603. if err == nil {
  1604. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, &u)
  1605. }
  1606. RemoveCachedWebDAVUser(user)
  1607. }
  1608. delayedQuotaUpdater.resetFolderQuota(folderName)
  1609. }
  1610. return err
  1611. }
  1612. // GetFolderByName returns the folder with the specified name if any
  1613. func GetFolderByName(name string) (vfs.BaseVirtualFolder, error) {
  1614. name = config.convertName(name)
  1615. return provider.getFolderByName(name)
  1616. }
  1617. // GetFolders returns an array of folders respecting limit and offset
  1618. func GetFolders(limit, offset int, order string, minimal bool) ([]vfs.BaseVirtualFolder, error) {
  1619. return provider.getFolders(limit, offset, order, minimal)
  1620. }
  1621. // DumpData returns all users, folders, admins, api keys, shares
  1622. func DumpData() (BackupData, error) {
  1623. var data BackupData
  1624. groups, err := provider.dumpGroups()
  1625. if err != nil {
  1626. return data, err
  1627. }
  1628. users, err := provider.dumpUsers()
  1629. if err != nil {
  1630. return data, err
  1631. }
  1632. folders, err := provider.dumpFolders()
  1633. if err != nil {
  1634. return data, err
  1635. }
  1636. admins, err := provider.dumpAdmins()
  1637. if err != nil {
  1638. return data, err
  1639. }
  1640. apiKeys, err := provider.dumpAPIKeys()
  1641. if err != nil {
  1642. return data, err
  1643. }
  1644. shares, err := provider.dumpShares()
  1645. if err != nil {
  1646. return data, err
  1647. }
  1648. data.Users = users
  1649. data.Groups = groups
  1650. data.Folders = folders
  1651. data.Admins = admins
  1652. data.APIKeys = apiKeys
  1653. data.Shares = shares
  1654. data.Version = DumpVersion
  1655. return data, err
  1656. }
  1657. // ParseDumpData tries to parse data as BackupData
  1658. func ParseDumpData(data []byte) (BackupData, error) {
  1659. var dump BackupData
  1660. err := json.Unmarshal(data, &dump)
  1661. return dump, err
  1662. }
  1663. // GetProviderConfig returns the current provider configuration
  1664. func GetProviderConfig() Config {
  1665. return config
  1666. }
  1667. // GetProviderStatus returns an error if the provider is not available
  1668. func GetProviderStatus() ProviderStatus {
  1669. err := provider.checkAvailability()
  1670. status := ProviderStatus{
  1671. Driver: config.Driver,
  1672. }
  1673. if err == nil {
  1674. status.IsActive = true
  1675. } else {
  1676. status.IsActive = false
  1677. status.Error = err.Error()
  1678. }
  1679. return status
  1680. }
  1681. // Close releases all provider resources.
  1682. // This method is used in test cases.
  1683. // Closing an uninitialized provider is not supported
  1684. func Close() error {
  1685. stopScheduler()
  1686. return provider.close()
  1687. }
  1688. func createProvider(basePath string) error {
  1689. var err error
  1690. sqlPlaceholders = getSQLPlaceholders()
  1691. if err = validateSQLTablesPrefix(); err != nil {
  1692. return err
  1693. }
  1694. logSender = fmt.Sprintf("dataprovider_%v", config.Driver)
  1695. switch config.Driver {
  1696. case SQLiteDataProviderName:
  1697. return initializeSQLiteProvider(basePath)
  1698. case PGSQLDataProviderName, CockroachDataProviderName:
  1699. return initializePGSQLProvider()
  1700. case MySQLDataProviderName:
  1701. return initializeMySQLProvider()
  1702. case BoltDataProviderName:
  1703. return initializeBoltProvider(basePath)
  1704. case MemoryDataProviderName:
  1705. initializeMemoryProvider(basePath)
  1706. return nil
  1707. default:
  1708. return fmt.Errorf("unsupported data provider: %v", config.Driver)
  1709. }
  1710. }
  1711. func copyBaseUserFilters(in sdk.BaseUserFilters) sdk.BaseUserFilters {
  1712. filters := sdk.BaseUserFilters{}
  1713. filters.MaxUploadFileSize = in.MaxUploadFileSize
  1714. filters.TLSUsername = in.TLSUsername
  1715. filters.UserType = in.UserType
  1716. filters.AllowedIP = make([]string, len(in.AllowedIP))
  1717. copy(filters.AllowedIP, in.AllowedIP)
  1718. filters.DeniedIP = make([]string, len(in.DeniedIP))
  1719. copy(filters.DeniedIP, in.DeniedIP)
  1720. filters.DeniedLoginMethods = make([]string, len(in.DeniedLoginMethods))
  1721. copy(filters.DeniedLoginMethods, in.DeniedLoginMethods)
  1722. filters.FilePatterns = make([]sdk.PatternsFilter, len(in.FilePatterns))
  1723. copy(filters.FilePatterns, in.FilePatterns)
  1724. filters.DeniedProtocols = make([]string, len(in.DeniedProtocols))
  1725. copy(filters.DeniedProtocols, in.DeniedProtocols)
  1726. filters.TwoFactorAuthProtocols = make([]string, len(in.TwoFactorAuthProtocols))
  1727. copy(filters.TwoFactorAuthProtocols, in.TwoFactorAuthProtocols)
  1728. filters.Hooks.ExternalAuthDisabled = in.Hooks.ExternalAuthDisabled
  1729. filters.Hooks.PreLoginDisabled = in.Hooks.PreLoginDisabled
  1730. filters.Hooks.CheckPasswordDisabled = in.Hooks.CheckPasswordDisabled
  1731. filters.DisableFsChecks = in.DisableFsChecks
  1732. filters.StartDirectory = in.StartDirectory
  1733. filters.AllowAPIKeyAuth = in.AllowAPIKeyAuth
  1734. filters.ExternalAuthCacheTime = in.ExternalAuthCacheTime
  1735. filters.WebClient = make([]string, len(in.WebClient))
  1736. copy(filters.WebClient, in.WebClient)
  1737. filters.BandwidthLimits = make([]sdk.BandwidthLimit, 0, len(in.BandwidthLimits))
  1738. for _, limit := range in.BandwidthLimits {
  1739. bwLimit := sdk.BandwidthLimit{
  1740. UploadBandwidth: limit.UploadBandwidth,
  1741. DownloadBandwidth: limit.DownloadBandwidth,
  1742. Sources: make([]string, 0, len(limit.Sources)),
  1743. }
  1744. bwLimit.Sources = make([]string, len(limit.Sources))
  1745. copy(bwLimit.Sources, limit.Sources)
  1746. filters.BandwidthLimits = append(filters.BandwidthLimits, bwLimit)
  1747. }
  1748. filters.DataTransferLimits = make([]sdk.DataTransferLimit, 0, len(in.DataTransferLimits))
  1749. for _, limit := range in.DataTransferLimits {
  1750. dtLimit := sdk.DataTransferLimit{
  1751. UploadDataTransfer: limit.UploadDataTransfer,
  1752. DownloadDataTransfer: limit.DownloadDataTransfer,
  1753. TotalDataTransfer: limit.TotalDataTransfer,
  1754. Sources: make([]string, 0, len(limit.Sources)),
  1755. }
  1756. dtLimit.Sources = make([]string, len(limit.Sources))
  1757. copy(dtLimit.Sources, limit.Sources)
  1758. filters.DataTransferLimits = append(filters.DataTransferLimits, dtLimit)
  1759. }
  1760. return filters
  1761. }
  1762. func buildUserHomeDir(user *User) {
  1763. if user.HomeDir == "" {
  1764. if config.UsersBaseDir != "" {
  1765. user.HomeDir = filepath.Join(config.UsersBaseDir, user.Username)
  1766. return
  1767. }
  1768. switch user.FsConfig.Provider {
  1769. case sdk.SFTPFilesystemProvider, sdk.S3FilesystemProvider, sdk.AzureBlobFilesystemProvider, sdk.GCSFilesystemProvider:
  1770. if tempPath != "" {
  1771. user.HomeDir = filepath.Join(tempPath, user.Username)
  1772. } else {
  1773. user.HomeDir = filepath.Join(os.TempDir(), user.Username)
  1774. }
  1775. }
  1776. } else {
  1777. user.HomeDir = filepath.Clean(user.HomeDir)
  1778. }
  1779. }
  1780. func isVirtualDirOverlapped(dir1, dir2 string, fullCheck bool) bool {
  1781. if dir1 == dir2 {
  1782. return true
  1783. }
  1784. if fullCheck {
  1785. if len(dir1) > len(dir2) {
  1786. if strings.HasPrefix(dir1, dir2+"/") {
  1787. return true
  1788. }
  1789. }
  1790. if len(dir2) > len(dir1) {
  1791. if strings.HasPrefix(dir2, dir1+"/") {
  1792. return true
  1793. }
  1794. }
  1795. }
  1796. return false
  1797. }
  1798. func validateFolderQuotaLimits(folder vfs.VirtualFolder) error {
  1799. if folder.QuotaSize < -1 {
  1800. return util.NewValidationError(fmt.Sprintf("invalid quota_size: %v folder path %#v", folder.QuotaSize, folder.MappedPath))
  1801. }
  1802. if folder.QuotaFiles < -1 {
  1803. return util.NewValidationError(fmt.Sprintf("invalid quota_file: %v folder path %#v", folder.QuotaFiles, folder.MappedPath))
  1804. }
  1805. if (folder.QuotaSize == -1 && folder.QuotaFiles != -1) || (folder.QuotaFiles == -1 && folder.QuotaSize != -1) {
  1806. return util.NewValidationError(fmt.Sprintf("virtual folder quota_size and quota_files must be both -1 or >= 0, quota_size: %v quota_files: %v",
  1807. folder.QuotaFiles, folder.QuotaSize))
  1808. }
  1809. return nil
  1810. }
  1811. func getVirtualFolderIfInvalid(folder *vfs.BaseVirtualFolder) *vfs.BaseVirtualFolder {
  1812. if err := ValidateFolder(folder); err == nil {
  1813. return folder
  1814. }
  1815. // we try to get the folder from the data provider if only the Name is populated
  1816. if folder.MappedPath != "" {
  1817. return folder
  1818. }
  1819. if folder.Name == "" {
  1820. return folder
  1821. }
  1822. if folder.FsConfig.Provider != sdk.LocalFilesystemProvider {
  1823. return folder
  1824. }
  1825. if f, err := GetFolderByName(folder.Name); err == nil {
  1826. return &f
  1827. }
  1828. return folder
  1829. }
  1830. func validateUserGroups(user *User) error {
  1831. if len(user.Groups) == 0 {
  1832. return nil
  1833. }
  1834. hasPrimary := false
  1835. groupNames := make(map[string]bool)
  1836. for _, g := range user.Groups {
  1837. if g.Type < sdk.GroupTypePrimary && g.Type > sdk.GroupTypeSecondary {
  1838. return util.NewValidationError(fmt.Sprintf("invalid group type: %v", g.Type))
  1839. }
  1840. if g.Type == sdk.GroupTypePrimary {
  1841. if hasPrimary {
  1842. return util.NewValidationError("only one primary group is allowed")
  1843. }
  1844. hasPrimary = true
  1845. }
  1846. if groupNames[g.Name] {
  1847. return util.NewValidationError(fmt.Sprintf("the group %#v is duplicated", g.Name))
  1848. }
  1849. groupNames[g.Name] = true
  1850. }
  1851. return nil
  1852. }
  1853. func validateAssociatedVirtualFolders(vfolders []vfs.VirtualFolder) ([]vfs.VirtualFolder, error) {
  1854. if len(vfolders) == 0 {
  1855. return []vfs.VirtualFolder{}, nil
  1856. }
  1857. var virtualFolders []vfs.VirtualFolder
  1858. folderNames := make(map[string]bool)
  1859. for _, v := range vfolders {
  1860. if v.VirtualPath == "" {
  1861. return nil, util.NewValidationError("mount/virtual path is mandatory")
  1862. }
  1863. cleanedVPath := util.CleanPath(v.VirtualPath)
  1864. if err := validateFolderQuotaLimits(v); err != nil {
  1865. return nil, err
  1866. }
  1867. folder := getVirtualFolderIfInvalid(&v.BaseVirtualFolder)
  1868. if err := ValidateFolder(folder); err != nil {
  1869. return nil, err
  1870. }
  1871. if folderNames[folder.Name] {
  1872. return nil, util.NewValidationError(fmt.Sprintf("the folder %#v is duplicated", folder.Name))
  1873. }
  1874. for _, vFolder := range virtualFolders {
  1875. if isVirtualDirOverlapped(vFolder.VirtualPath, cleanedVPath, false) {
  1876. return nil, util.NewValidationError(fmt.Sprintf("invalid virtual folder %#v, it overlaps with virtual folder %#v",
  1877. v.VirtualPath, vFolder.VirtualPath))
  1878. }
  1879. }
  1880. virtualFolders = append(virtualFolders, vfs.VirtualFolder{
  1881. BaseVirtualFolder: *folder,
  1882. VirtualPath: cleanedVPath,
  1883. QuotaSize: v.QuotaSize,
  1884. QuotaFiles: v.QuotaFiles,
  1885. })
  1886. folderNames[folder.Name] = true
  1887. }
  1888. return virtualFolders, nil
  1889. }
  1890. func validateUserTOTPConfig(c *UserTOTPConfig, username string) error {
  1891. if !c.Enabled {
  1892. c.ConfigName = ""
  1893. c.Secret = kms.NewEmptySecret()
  1894. c.Protocols = nil
  1895. return nil
  1896. }
  1897. if c.ConfigName == "" {
  1898. return util.NewValidationError("totp: config name is mandatory")
  1899. }
  1900. if !util.IsStringInSlice(c.ConfigName, mfa.GetAvailableTOTPConfigNames()) {
  1901. return util.NewValidationError(fmt.Sprintf("totp: config name %#v not found", c.ConfigName))
  1902. }
  1903. if c.Secret.IsEmpty() {
  1904. return util.NewValidationError("totp: secret is mandatory")
  1905. }
  1906. if c.Secret.IsPlain() {
  1907. c.Secret.SetAdditionalData(username)
  1908. if err := c.Secret.Encrypt(); err != nil {
  1909. return util.NewValidationError(fmt.Sprintf("totp: unable to encrypt secret: %v", err))
  1910. }
  1911. }
  1912. if len(c.Protocols) == 0 {
  1913. return util.NewValidationError("totp: specify at least one protocol")
  1914. }
  1915. for _, protocol := range c.Protocols {
  1916. if !util.IsStringInSlice(protocol, MFAProtocols) {
  1917. return util.NewValidationError(fmt.Sprintf("totp: invalid protocol %#v", protocol))
  1918. }
  1919. }
  1920. return nil
  1921. }
  1922. func validateUserRecoveryCodes(user *User) error {
  1923. for i := 0; i < len(user.Filters.RecoveryCodes); i++ {
  1924. code := &user.Filters.RecoveryCodes[i]
  1925. if code.Secret.IsEmpty() {
  1926. return util.NewValidationError("mfa: recovery code cannot be empty")
  1927. }
  1928. if code.Secret.IsPlain() {
  1929. code.Secret.SetAdditionalData(user.Username)
  1930. if err := code.Secret.Encrypt(); err != nil {
  1931. return util.NewValidationError(fmt.Sprintf("mfa: unable to encrypt recovery code: %v", err))
  1932. }
  1933. }
  1934. }
  1935. return nil
  1936. }
  1937. func validateUserPermissions(permsToCheck map[string][]string) (map[string][]string, error) {
  1938. permissions := make(map[string][]string)
  1939. for dir, perms := range permsToCheck {
  1940. if len(perms) == 0 && dir == "/" {
  1941. return permissions, util.NewValidationError(fmt.Sprintf("no permissions granted for the directory: %#v", dir))
  1942. }
  1943. if len(perms) > len(ValidPerms) {
  1944. return permissions, util.NewValidationError("invalid permissions")
  1945. }
  1946. for _, p := range perms {
  1947. if !util.IsStringInSlice(p, ValidPerms) {
  1948. return permissions, util.NewValidationError(fmt.Sprintf("invalid permission: %#v", p))
  1949. }
  1950. }
  1951. cleanedDir := filepath.ToSlash(path.Clean(dir))
  1952. if cleanedDir != "/" {
  1953. cleanedDir = strings.TrimSuffix(cleanedDir, "/")
  1954. }
  1955. if !path.IsAbs(cleanedDir) {
  1956. return permissions, util.NewValidationError(fmt.Sprintf("cannot set permissions for non absolute path: %#v", dir))
  1957. }
  1958. if dir != cleanedDir && cleanedDir == "/" {
  1959. return permissions, util.NewValidationError(fmt.Sprintf("cannot set permissions for invalid subdirectory: %#v is an alias for \"/\"", dir))
  1960. }
  1961. if util.IsStringInSlice(PermAny, perms) {
  1962. permissions[cleanedDir] = []string{PermAny}
  1963. } else {
  1964. permissions[cleanedDir] = util.RemoveDuplicates(perms)
  1965. }
  1966. }
  1967. return permissions, nil
  1968. }
  1969. func validatePermissions(user *User) error {
  1970. if len(user.Permissions) == 0 {
  1971. return util.NewValidationError("please grant some permissions to this user")
  1972. }
  1973. if _, ok := user.Permissions["/"]; !ok {
  1974. return util.NewValidationError("permissions for the root dir \"/\" must be set")
  1975. }
  1976. permissions, err := validateUserPermissions(user.Permissions)
  1977. if err != nil {
  1978. return err
  1979. }
  1980. user.Permissions = permissions
  1981. return nil
  1982. }
  1983. func validatePublicKeys(user *User) error {
  1984. if len(user.PublicKeys) == 0 {
  1985. user.PublicKeys = []string{}
  1986. }
  1987. var validatedKeys []string
  1988. for i, k := range user.PublicKeys {
  1989. if k == "" {
  1990. continue
  1991. }
  1992. _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
  1993. if err != nil {
  1994. return util.NewValidationError(fmt.Sprintf("could not parse key nr. %d: %s", i+1, err))
  1995. }
  1996. validatedKeys = append(validatedKeys, k)
  1997. }
  1998. user.PublicKeys = util.RemoveDuplicates(validatedKeys)
  1999. return nil
  2000. }
  2001. func validateFiltersPatternExtensions(baseFilters *sdk.BaseUserFilters) error {
  2002. if len(baseFilters.FilePatterns) == 0 {
  2003. baseFilters.FilePatterns = []sdk.PatternsFilter{}
  2004. return nil
  2005. }
  2006. filteredPaths := []string{}
  2007. var filters []sdk.PatternsFilter
  2008. for _, f := range baseFilters.FilePatterns {
  2009. cleanedPath := filepath.ToSlash(path.Clean(f.Path))
  2010. if !path.IsAbs(cleanedPath) {
  2011. return util.NewValidationError(fmt.Sprintf("invalid path %#v for file patterns filter", f.Path))
  2012. }
  2013. if util.IsStringInSlice(cleanedPath, filteredPaths) {
  2014. return util.NewValidationError(fmt.Sprintf("duplicate file patterns filter for path %#v", f.Path))
  2015. }
  2016. if len(f.AllowedPatterns) == 0 && len(f.DeniedPatterns) == 0 {
  2017. return util.NewValidationError(fmt.Sprintf("empty file patterns filter for path %#v", f.Path))
  2018. }
  2019. if f.DenyPolicy < sdk.DenyPolicyDefault || f.DenyPolicy > sdk.DenyPolicyHide {
  2020. return util.NewValidationError(fmt.Sprintf("invalid deny policy %v for path %#v", f.DenyPolicy, f.Path))
  2021. }
  2022. f.Path = cleanedPath
  2023. allowed := make([]string, 0, len(f.AllowedPatterns))
  2024. denied := make([]string, 0, len(f.DeniedPatterns))
  2025. for _, pattern := range f.AllowedPatterns {
  2026. _, err := path.Match(pattern, "abc")
  2027. if err != nil {
  2028. return util.NewValidationError(fmt.Sprintf("invalid file pattern filter %#v", pattern))
  2029. }
  2030. allowed = append(allowed, strings.ToLower(pattern))
  2031. }
  2032. for _, pattern := range f.DeniedPatterns {
  2033. _, err := path.Match(pattern, "abc")
  2034. if err != nil {
  2035. return util.NewValidationError(fmt.Sprintf("invalid file pattern filter %#v", pattern))
  2036. }
  2037. denied = append(denied, strings.ToLower(pattern))
  2038. }
  2039. f.AllowedPatterns = util.RemoveDuplicates(allowed)
  2040. f.DeniedPatterns = util.RemoveDuplicates(denied)
  2041. filters = append(filters, f)
  2042. filteredPaths = append(filteredPaths, cleanedPath)
  2043. }
  2044. baseFilters.FilePatterns = filters
  2045. return nil
  2046. }
  2047. func checkEmptyFiltersStruct(filters *sdk.BaseUserFilters) {
  2048. if len(filters.AllowedIP) == 0 {
  2049. filters.AllowedIP = []string{}
  2050. }
  2051. if len(filters.DeniedIP) == 0 {
  2052. filters.DeniedIP = []string{}
  2053. }
  2054. if len(filters.DeniedLoginMethods) == 0 {
  2055. filters.DeniedLoginMethods = []string{}
  2056. }
  2057. if len(filters.DeniedProtocols) == 0 {
  2058. filters.DeniedProtocols = []string{}
  2059. }
  2060. }
  2061. func validateIPFilters(filters *sdk.BaseUserFilters) error {
  2062. filters.DeniedIP = util.RemoveDuplicates(filters.DeniedIP)
  2063. for _, IPMask := range filters.DeniedIP {
  2064. _, _, err := net.ParseCIDR(IPMask)
  2065. if err != nil {
  2066. return util.NewValidationError(fmt.Sprintf("could not parse denied IP/Mask %#v: %v", IPMask, err))
  2067. }
  2068. }
  2069. filters.AllowedIP = util.RemoveDuplicates(filters.AllowedIP)
  2070. for _, IPMask := range filters.AllowedIP {
  2071. _, _, err := net.ParseCIDR(IPMask)
  2072. if err != nil {
  2073. return util.NewValidationError(fmt.Sprintf("could not parse allowed IP/Mask %#v: %v", IPMask, err))
  2074. }
  2075. }
  2076. return nil
  2077. }
  2078. func validateBandwidthLimit(bl sdk.BandwidthLimit) error {
  2079. if len(bl.Sources) == 0 {
  2080. return util.NewValidationError("no bandwidth limit source specified")
  2081. }
  2082. for _, source := range bl.Sources {
  2083. _, _, err := net.ParseCIDR(source)
  2084. if err != nil {
  2085. return util.NewValidationError(fmt.Sprintf("could not parse bandwidth limit source %#v: %v", source, err))
  2086. }
  2087. }
  2088. return nil
  2089. }
  2090. func validateBandwidthLimitsFilter(filters *sdk.BaseUserFilters) error {
  2091. for idx, bandwidthLimit := range filters.BandwidthLimits {
  2092. if err := validateBandwidthLimit(bandwidthLimit); err != nil {
  2093. return err
  2094. }
  2095. if bandwidthLimit.DownloadBandwidth < 0 {
  2096. filters.BandwidthLimits[idx].DownloadBandwidth = 0
  2097. }
  2098. if bandwidthLimit.UploadBandwidth < 0 {
  2099. filters.BandwidthLimits[idx].UploadBandwidth = 0
  2100. }
  2101. }
  2102. return nil
  2103. }
  2104. func validateTransferLimitsFilter(filters *sdk.BaseUserFilters) error {
  2105. for idx, limit := range filters.DataTransferLimits {
  2106. filters.DataTransferLimits[idx].Sources = util.RemoveDuplicates(limit.Sources)
  2107. if len(limit.Sources) == 0 {
  2108. return util.NewValidationError("no data transfer limit source specified")
  2109. }
  2110. for _, source := range limit.Sources {
  2111. _, _, err := net.ParseCIDR(source)
  2112. if err != nil {
  2113. return util.NewValidationError(fmt.Sprintf("could not parse data transfer limit source %#v: %v", source, err))
  2114. }
  2115. }
  2116. if limit.TotalDataTransfer > 0 {
  2117. filters.DataTransferLimits[idx].UploadDataTransfer = 0
  2118. filters.DataTransferLimits[idx].DownloadDataTransfer = 0
  2119. }
  2120. }
  2121. return nil
  2122. }
  2123. func updateFiltersValues(filters *sdk.BaseUserFilters) {
  2124. if filters.StartDirectory != "" {
  2125. filters.StartDirectory = util.CleanPath(filters.StartDirectory)
  2126. if filters.StartDirectory == "/" {
  2127. filters.StartDirectory = ""
  2128. }
  2129. }
  2130. }
  2131. func validateFilterProtocols(filters *sdk.BaseUserFilters) error {
  2132. if len(filters.DeniedProtocols) >= len(ValidProtocols) {
  2133. return util.NewValidationError("invalid denied_protocols")
  2134. }
  2135. for _, p := range filters.DeniedProtocols {
  2136. if !util.IsStringInSlice(p, ValidProtocols) {
  2137. return util.NewValidationError(fmt.Sprintf("invalid denied protocol %#v", p))
  2138. }
  2139. }
  2140. for _, p := range filters.TwoFactorAuthProtocols {
  2141. if !util.IsStringInSlice(p, MFAProtocols) {
  2142. return util.NewValidationError(fmt.Sprintf("invalid two factor protocol %#v", p))
  2143. }
  2144. }
  2145. return nil
  2146. }
  2147. func validateBaseFilters(filters *sdk.BaseUserFilters) error {
  2148. checkEmptyFiltersStruct(filters)
  2149. if err := validateIPFilters(filters); err != nil {
  2150. return err
  2151. }
  2152. if err := validateBandwidthLimitsFilter(filters); err != nil {
  2153. return err
  2154. }
  2155. if err := validateTransferLimitsFilter(filters); err != nil {
  2156. return err
  2157. }
  2158. if len(filters.DeniedLoginMethods) >= len(ValidLoginMethods) {
  2159. return util.NewValidationError("invalid denied_login_methods")
  2160. }
  2161. for _, loginMethod := range filters.DeniedLoginMethods {
  2162. if !util.IsStringInSlice(loginMethod, ValidLoginMethods) {
  2163. return util.NewValidationError(fmt.Sprintf("invalid login method: %#v", loginMethod))
  2164. }
  2165. }
  2166. if err := validateFilterProtocols(filters); err != nil {
  2167. return err
  2168. }
  2169. if filters.TLSUsername != "" {
  2170. if !util.IsStringInSlice(string(filters.TLSUsername), validTLSUsernames) {
  2171. return util.NewValidationError(fmt.Sprintf("invalid TLS username: %#v", filters.TLSUsername))
  2172. }
  2173. }
  2174. for _, opts := range filters.WebClient {
  2175. if !util.IsStringInSlice(opts, sdk.WebClientOptions) {
  2176. return util.NewValidationError(fmt.Sprintf("invalid web client options %#v", opts))
  2177. }
  2178. }
  2179. updateFiltersValues(filters)
  2180. return validateFiltersPatternExtensions(filters)
  2181. }
  2182. func validateBaseParams(user *User) error {
  2183. if user.Username == "" {
  2184. return util.NewValidationError("username is mandatory")
  2185. }
  2186. if user.Email != "" && !emailRegex.MatchString(user.Email) {
  2187. return util.NewValidationError(fmt.Sprintf("email %#v is not valid", user.Email))
  2188. }
  2189. if config.NamingRules&1 == 0 && !usernameRegex.MatchString(user.Username) {
  2190. return util.NewValidationError(fmt.Sprintf("username %#v is not valid, the following characters are allowed: a-zA-Z0-9-_.~",
  2191. user.Username))
  2192. }
  2193. if user.hasRedactedSecret() {
  2194. return util.NewValidationError("cannot save a user with a redacted secret")
  2195. }
  2196. if user.HomeDir == "" {
  2197. return util.NewValidationError("home_dir is mandatory")
  2198. }
  2199. // we can have users with no passwords and public keys, they can authenticate via SSH user certs or OIDC
  2200. /*if user.Password == "" && len(user.PublicKeys) == 0 {
  2201. return util.NewValidationError("please set a password or at least a public_key")
  2202. }*/
  2203. if !filepath.IsAbs(user.HomeDir) {
  2204. return util.NewValidationError(fmt.Sprintf("home_dir must be an absolute path, actual value: %v", user.HomeDir))
  2205. }
  2206. if user.DownloadBandwidth < 0 {
  2207. user.DownloadBandwidth = 0
  2208. }
  2209. if user.UploadBandwidth < 0 {
  2210. user.UploadBandwidth = 0
  2211. }
  2212. if user.TotalDataTransfer > 0 {
  2213. // if a total data transfer is defined we reset the separate upload and download limits
  2214. user.UploadDataTransfer = 0
  2215. user.DownloadDataTransfer = 0
  2216. }
  2217. return user.FsConfig.Validate(user.GetEncryptionAdditionalData())
  2218. }
  2219. func hashPlainPassword(plainPwd string) (string, error) {
  2220. if config.PasswordHashing.Algo == HashingAlgoBcrypt {
  2221. pwd, err := bcrypt.GenerateFromPassword([]byte(plainPwd), config.PasswordHashing.BcryptOptions.Cost)
  2222. if err != nil {
  2223. return "", fmt.Errorf("bcrypt hashing error: %w", err)
  2224. }
  2225. return string(pwd), nil
  2226. }
  2227. pwd, err := argon2id.CreateHash(plainPwd, argon2Params)
  2228. if err != nil {
  2229. return "", fmt.Errorf("argon2ID hashing error: %w", err)
  2230. }
  2231. return pwd, nil
  2232. }
  2233. func createUserPasswordHash(user *User) error {
  2234. if user.Password != "" && !user.IsPasswordHashed() {
  2235. if config.PasswordValidation.Users.MinEntropy > 0 {
  2236. if err := passwordvalidator.Validate(user.Password, config.PasswordValidation.Users.MinEntropy); err != nil {
  2237. return util.NewValidationError(err.Error())
  2238. }
  2239. }
  2240. hashedPwd, err := hashPlainPassword(user.Password)
  2241. if err != nil {
  2242. return err
  2243. }
  2244. user.Password = hashedPwd
  2245. }
  2246. return nil
  2247. }
  2248. // ValidateFolder returns an error if the folder is not valid
  2249. // FIXME: this should be defined as Folder struct method
  2250. func ValidateFolder(folder *vfs.BaseVirtualFolder) error {
  2251. folder.FsConfig.SetEmptySecretsIfNil()
  2252. if folder.Name == "" {
  2253. return util.NewValidationError("folder name is mandatory")
  2254. }
  2255. if config.NamingRules&1 == 0 && !usernameRegex.MatchString(folder.Name) {
  2256. return util.NewValidationError(fmt.Sprintf("folder name %#v is not valid, the following characters are allowed: a-zA-Z0-9-_.~",
  2257. folder.Name))
  2258. }
  2259. if folder.FsConfig.Provider == sdk.LocalFilesystemProvider || folder.FsConfig.Provider == sdk.CryptedFilesystemProvider ||
  2260. folder.MappedPath != "" {
  2261. cleanedMPath := filepath.Clean(folder.MappedPath)
  2262. if !filepath.IsAbs(cleanedMPath) {
  2263. return util.NewValidationError(fmt.Sprintf("invalid folder mapped path %#v", folder.MappedPath))
  2264. }
  2265. folder.MappedPath = cleanedMPath
  2266. }
  2267. if folder.HasRedactedSecret() {
  2268. return errors.New("cannot save a folder with a redacted secret")
  2269. }
  2270. return folder.FsConfig.Validate(folder.GetEncryptionAdditionalData())
  2271. }
  2272. // ValidateUser returns an error if the user is not valid
  2273. // FIXME: this should be defined as User struct method
  2274. func ValidateUser(user *User) error {
  2275. user.OIDCCustomFields = nil
  2276. user.SetEmptySecretsIfNil()
  2277. buildUserHomeDir(user)
  2278. if err := validateBaseParams(user); err != nil {
  2279. return err
  2280. }
  2281. if err := validateUserGroups(user); err != nil {
  2282. return err
  2283. }
  2284. if err := validatePermissions(user); err != nil {
  2285. return err
  2286. }
  2287. if err := validateUserTOTPConfig(&user.Filters.TOTPConfig, user.Username); err != nil {
  2288. return err
  2289. }
  2290. if err := validateUserRecoveryCodes(user); err != nil {
  2291. return err
  2292. }
  2293. vfolders, err := validateAssociatedVirtualFolders(user.VirtualFolders)
  2294. if err != nil {
  2295. return err
  2296. }
  2297. user.VirtualFolders = vfolders
  2298. if user.Status < 0 || user.Status > 1 {
  2299. return util.NewValidationError(fmt.Sprintf("invalid user status: %v", user.Status))
  2300. }
  2301. if err := createUserPasswordHash(user); err != nil {
  2302. return err
  2303. }
  2304. if err := validatePublicKeys(user); err != nil {
  2305. return err
  2306. }
  2307. if err := validateBaseFilters(&user.Filters.BaseUserFilters); err != nil {
  2308. return err
  2309. }
  2310. if !user.HasExternalAuth() {
  2311. user.Filters.ExternalAuthCacheTime = 0
  2312. }
  2313. if user.Filters.TOTPConfig.Enabled && util.IsStringInSlice(sdk.WebClientMFADisabled, user.Filters.WebClient) {
  2314. return util.NewValidationError("two-factor authentication cannot be disabled for a user with an active configuration")
  2315. }
  2316. return nil
  2317. }
  2318. func isPasswordOK(user *User, password string) (bool, error) {
  2319. if config.PasswordCaching {
  2320. found, match := cachedPasswords.Check(user.Username, password)
  2321. if found {
  2322. return match, nil
  2323. }
  2324. }
  2325. match := false
  2326. updatePwd := true
  2327. var err error
  2328. if strings.HasPrefix(user.Password, bcryptPwdPrefix) {
  2329. if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
  2330. return match, ErrInvalidCredentials
  2331. }
  2332. match = true
  2333. updatePwd = config.PasswordHashing.Algo != HashingAlgoBcrypt
  2334. } else if strings.HasPrefix(user.Password, argonPwdPrefix) {
  2335. match, err = argon2id.ComparePasswordAndHash(password, user.Password)
  2336. if err != nil {
  2337. providerLog(logger.LevelError, "error comparing password with argon hash: %v", err)
  2338. return match, err
  2339. }
  2340. updatePwd = config.PasswordHashing.Algo != HashingAlgoArgon2ID
  2341. } else if util.IsStringPrefixInSlice(user.Password, pbkdfPwdPrefixes) {
  2342. match, err = comparePbkdf2PasswordAndHash(password, user.Password)
  2343. if err != nil {
  2344. return match, err
  2345. }
  2346. } else if util.IsStringPrefixInSlice(user.Password, unixPwdPrefixes) {
  2347. match, err = compareUnixPasswordAndHash(user, password)
  2348. if err != nil {
  2349. return match, err
  2350. }
  2351. } else if strings.HasPrefix(user.Password, md5LDAPPwdPrefix) {
  2352. h := md5.New()
  2353. h.Write([]byte(password))
  2354. match = fmt.Sprintf("%s%x", md5LDAPPwdPrefix, h.Sum(nil)) == user.Password
  2355. }
  2356. if err == nil && match {
  2357. cachedPasswords.Add(user.Username, password)
  2358. if updatePwd {
  2359. convertUserPassword(user.Username, password)
  2360. }
  2361. }
  2362. return match, err
  2363. }
  2364. func convertUserPassword(username, plainPwd string) {
  2365. hashedPwd, err := hashPlainPassword(plainPwd)
  2366. if err == nil {
  2367. err = provider.updateUserPassword(username, hashedPwd)
  2368. }
  2369. if err != nil {
  2370. providerLog(logger.LevelWarn, "unable to convert password for user %s: %v", username, err)
  2371. } else {
  2372. providerLog(logger.LevelDebug, "password converted for user %s", username)
  2373. }
  2374. }
  2375. func checkUserAndTLSCertificate(user *User, protocol string, tlsCert *x509.Certificate) (User, error) {
  2376. err := user.LoadAndApplyGroupSettings()
  2377. if err != nil {
  2378. return *user, err
  2379. }
  2380. err = user.CheckLoginConditions()
  2381. if err != nil {
  2382. return *user, err
  2383. }
  2384. switch protocol {
  2385. case protocolFTP, protocolWebDAV:
  2386. if user.Filters.TLSUsername == sdk.TLSUsernameCN {
  2387. if user.Username == tlsCert.Subject.CommonName {
  2388. return *user, nil
  2389. }
  2390. return *user, fmt.Errorf("CN %#v does not match username %#v", tlsCert.Subject.CommonName, user.Username)
  2391. }
  2392. return *user, errors.New("TLS certificate is not valid")
  2393. default:
  2394. return *user, fmt.Errorf("certificate authentication is not supported for protocol %v", protocol)
  2395. }
  2396. }
  2397. func checkUserAndPass(user *User, password, ip, protocol string) (User, error) {
  2398. err := user.LoadAndApplyGroupSettings()
  2399. if err != nil {
  2400. return *user, err
  2401. }
  2402. err = user.CheckLoginConditions()
  2403. if err != nil {
  2404. return *user, err
  2405. }
  2406. password, err = checkUserPasscode(user, password, protocol)
  2407. if err != nil {
  2408. return *user, ErrInvalidCredentials
  2409. }
  2410. if user.Password == "" {
  2411. return *user, errors.New("credentials cannot be null or empty")
  2412. }
  2413. if !user.Filters.Hooks.CheckPasswordDisabled {
  2414. hookResponse, err := executeCheckPasswordHook(user.Username, password, ip, protocol)
  2415. if err != nil {
  2416. providerLog(logger.LevelDebug, "error executing check password hook for user %#v, ip %v, protocol %v: %v",
  2417. user.Username, ip, protocol, err)
  2418. return *user, errors.New("unable to check credentials")
  2419. }
  2420. switch hookResponse.Status {
  2421. case -1:
  2422. // no hook configured
  2423. case 1:
  2424. providerLog(logger.LevelDebug, "password accepted by check password hook for user %#v, ip %v, protocol %v",
  2425. user.Username, ip, protocol)
  2426. return *user, nil
  2427. case 2:
  2428. providerLog(logger.LevelDebug, "partial success from check password hook for user %#v, ip %v, protocol %v",
  2429. user.Username, ip, protocol)
  2430. password = hookResponse.ToVerify
  2431. default:
  2432. providerLog(logger.LevelDebug, "password rejected by check password hook for user %#v, ip %v, protocol %v, status: %v",
  2433. user.Username, ip, protocol, hookResponse.Status)
  2434. return *user, ErrInvalidCredentials
  2435. }
  2436. }
  2437. match, err := isPasswordOK(user, password)
  2438. if !match {
  2439. err = ErrInvalidCredentials
  2440. }
  2441. return *user, err
  2442. }
  2443. func checkUserPasscode(user *User, password, protocol string) (string, error) {
  2444. if user.Filters.TOTPConfig.Enabled {
  2445. switch protocol {
  2446. case protocolFTP:
  2447. if util.IsStringInSlice(protocol, user.Filters.TOTPConfig.Protocols) {
  2448. // the TOTP passcode has six digits
  2449. pwdLen := len(password)
  2450. if pwdLen < 7 {
  2451. providerLog(logger.LevelDebug, "password len %v is too short to contain a passcode, user %#v, protocol %v",
  2452. pwdLen, user.Username, protocol)
  2453. return "", util.NewValidationError("password too short, cannot contain the passcode")
  2454. }
  2455. err := user.Filters.TOTPConfig.Secret.TryDecrypt()
  2456. if err != nil {
  2457. providerLog(logger.LevelError, "unable to decrypt TOTP secret for user %#v, protocol %v, err: %v",
  2458. user.Username, protocol, err)
  2459. return "", err
  2460. }
  2461. pwd := password[0:(pwdLen - 6)]
  2462. passcode := password[(pwdLen - 6):]
  2463. match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, passcode,
  2464. user.Filters.TOTPConfig.Secret.GetPayload())
  2465. if !match || err != nil {
  2466. providerLog(logger.LevelWarn, "invalid passcode for user %#v, protocol %v, err: %v",
  2467. user.Username, protocol, err)
  2468. return "", util.NewValidationError("invalid passcode")
  2469. }
  2470. return pwd, nil
  2471. }
  2472. }
  2473. }
  2474. return password, nil
  2475. }
  2476. func checkUserAndPubKey(user *User, pubKey []byte, isSSHCert bool) (User, string, error) {
  2477. err := user.LoadAndApplyGroupSettings()
  2478. if err != nil {
  2479. return *user, "", err
  2480. }
  2481. err = user.CheckLoginConditions()
  2482. if err != nil {
  2483. return *user, "", err
  2484. }
  2485. if isSSHCert {
  2486. return *user, "", nil
  2487. }
  2488. if len(user.PublicKeys) == 0 {
  2489. return *user, "", ErrInvalidCredentials
  2490. }
  2491. for i, k := range user.PublicKeys {
  2492. storedPubKey, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
  2493. if err != nil {
  2494. providerLog(logger.LevelError, "error parsing stored public key %d for user %s: %v", i, user.Username, err)
  2495. return *user, "", err
  2496. }
  2497. if bytes.Equal(storedPubKey.Marshal(), pubKey) {
  2498. return *user, fmt.Sprintf("%s:%s", ssh.FingerprintSHA256(storedPubKey), comment), nil
  2499. }
  2500. }
  2501. return *user, "", ErrInvalidCredentials
  2502. }
  2503. func compareUnixPasswordAndHash(user *User, password string) (bool, error) {
  2504. var crypter crypt.Crypter
  2505. if strings.HasPrefix(user.Password, sha512cryptPwdPrefix) {
  2506. crypter = sha512_crypt.New()
  2507. } else if strings.HasPrefix(user.Password, md5cryptPwdPrefix) {
  2508. crypter = md5_crypt.New()
  2509. } else if strings.HasPrefix(user.Password, md5cryptApr1PwdPrefix) {
  2510. crypter = apr1_crypt.New()
  2511. } else {
  2512. return false, errors.New("unix crypt: invalid or unsupported hash format")
  2513. }
  2514. if err := crypter.Verify(user.Password, []byte(password)); err != nil {
  2515. return false, err
  2516. }
  2517. return true, nil
  2518. }
  2519. func comparePbkdf2PasswordAndHash(password, hashedPassword string) (bool, error) {
  2520. vals := strings.Split(hashedPassword, "$")
  2521. if len(vals) != 5 {
  2522. return false, fmt.Errorf("pbkdf2: hash is not in the correct format")
  2523. }
  2524. iterations, err := strconv.Atoi(vals[2])
  2525. if err != nil {
  2526. return false, err
  2527. }
  2528. expected, err := base64.StdEncoding.DecodeString(vals[4])
  2529. if err != nil {
  2530. return false, err
  2531. }
  2532. var salt []byte
  2533. if util.IsStringPrefixInSlice(hashedPassword, pbkdfPwdB64SaltPrefixes) {
  2534. salt, err = base64.StdEncoding.DecodeString(vals[3])
  2535. if err != nil {
  2536. return false, err
  2537. }
  2538. } else {
  2539. salt = []byte(vals[3])
  2540. }
  2541. var hashFunc func() hash.Hash
  2542. if strings.HasPrefix(hashedPassword, pbkdf2SHA256Prefix) || strings.HasPrefix(hashedPassword, pbkdf2SHA256B64SaltPrefix) {
  2543. hashFunc = sha256.New
  2544. } else if strings.HasPrefix(hashedPassword, pbkdf2SHA512Prefix) {
  2545. hashFunc = sha512.New
  2546. } else if strings.HasPrefix(hashedPassword, pbkdf2SHA1Prefix) {
  2547. hashFunc = sha1.New
  2548. } else {
  2549. return false, fmt.Errorf("pbkdf2: invalid or unsupported hash format %v", vals[1])
  2550. }
  2551. df := pbkdf2.Key([]byte(password), salt, iterations, len(expected), hashFunc)
  2552. return subtle.ConstantTimeCompare(df, expected) == 1, nil
  2553. }
  2554. func getSSLMode() string {
  2555. if config.Driver == PGSQLDataProviderName || config.Driver == CockroachDataProviderName {
  2556. switch config.SSLMode {
  2557. case 0:
  2558. return "disable"
  2559. case 1:
  2560. return "require"
  2561. case 2:
  2562. return "verify-ca"
  2563. case 3:
  2564. return "verify-full"
  2565. }
  2566. } else if config.Driver == MySQLDataProviderName {
  2567. if config.requireCustomTLSForMySQL() {
  2568. return "custom"
  2569. }
  2570. switch config.SSLMode {
  2571. case 0:
  2572. return "false"
  2573. case 1:
  2574. return "true"
  2575. case 2:
  2576. return "skip-verify"
  2577. case 3:
  2578. return "preferred"
  2579. }
  2580. }
  2581. return ""
  2582. }
  2583. func terminateInteractiveAuthProgram(cmd *exec.Cmd, isFinished bool) {
  2584. if isFinished {
  2585. return
  2586. }
  2587. providerLog(logger.LevelInfo, "kill interactive auth program after an unexpected error")
  2588. err := cmd.Process.Kill()
  2589. if err != nil {
  2590. providerLog(logger.LevelDebug, "error killing interactive auth program: %v", err)
  2591. }
  2592. }
  2593. func sendKeyboardAuthHTTPReq(url string, request *plugin.KeyboardAuthRequest) (*plugin.KeyboardAuthResponse, error) {
  2594. reqAsJSON, err := json.Marshal(request)
  2595. if err != nil {
  2596. providerLog(logger.LevelError, "error serializing keyboard interactive auth request: %v", err)
  2597. return nil, err
  2598. }
  2599. resp, err := httpclient.Post(url, "application/json", bytes.NewBuffer(reqAsJSON))
  2600. if err != nil {
  2601. providerLog(logger.LevelError, "error getting keyboard interactive auth hook HTTP response: %v", err)
  2602. return nil, err
  2603. }
  2604. defer resp.Body.Close()
  2605. if resp.StatusCode != http.StatusOK {
  2606. return nil, fmt.Errorf("wrong keyboard interactive auth http status code: %v, expected 200", resp.StatusCode)
  2607. }
  2608. var response plugin.KeyboardAuthResponse
  2609. err = render.DecodeJSON(resp.Body, &response)
  2610. return &response, err
  2611. }
  2612. func doBuiltinKeyboardInteractiveAuth(user *User, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2613. answers, err := client("", "", []string{"Password: "}, []bool{false})
  2614. if err != nil {
  2615. return 0, err
  2616. }
  2617. if len(answers) != 1 {
  2618. return 0, fmt.Errorf("unexpected number of answers: %v", len(answers))
  2619. }
  2620. err = user.LoadAndApplyGroupSettings()
  2621. if err != nil {
  2622. return 0, err
  2623. }
  2624. _, err = checkUserAndPass(user, answers[0], ip, protocol)
  2625. if err != nil {
  2626. return 0, err
  2627. }
  2628. if !user.Filters.TOTPConfig.Enabled || !util.IsStringInSlice(protocolSSH, user.Filters.TOTPConfig.Protocols) {
  2629. return 1, nil
  2630. }
  2631. err = user.Filters.TOTPConfig.Secret.TryDecrypt()
  2632. if err != nil {
  2633. providerLog(logger.LevelError, "unable to decrypt TOTP secret for user %#v, protocol %v, err: %v",
  2634. user.Username, protocol, err)
  2635. return 0, err
  2636. }
  2637. answers, err = client("", "", []string{"Authentication code: "}, []bool{false})
  2638. if err != nil {
  2639. return 0, err
  2640. }
  2641. if len(answers) != 1 {
  2642. return 0, fmt.Errorf("unexpected number of answers: %v", len(answers))
  2643. }
  2644. match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, answers[0],
  2645. user.Filters.TOTPConfig.Secret.GetPayload())
  2646. if !match || err != nil {
  2647. providerLog(logger.LevelWarn, "invalid passcode for user %#v, protocol %v, err: %v",
  2648. user.Username, protocol, err)
  2649. return 0, util.NewValidationError("invalid passcode")
  2650. }
  2651. return 1, nil
  2652. }
  2653. func executeKeyboardInteractivePlugin(user *User, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2654. authResult := 0
  2655. requestID := xid.New().String()
  2656. authStep := 1
  2657. req := &plugin.KeyboardAuthRequest{
  2658. Username: user.Username,
  2659. IP: ip,
  2660. Password: user.Password,
  2661. RequestID: requestID,
  2662. Step: authStep,
  2663. }
  2664. var response *plugin.KeyboardAuthResponse
  2665. var err error
  2666. for {
  2667. response, err = plugin.Handler.ExecuteKeyboardInteractiveStep(req)
  2668. if err != nil {
  2669. return authResult, err
  2670. }
  2671. if response.AuthResult != 0 {
  2672. return response.AuthResult, err
  2673. }
  2674. if err = response.Validate(); err != nil {
  2675. providerLog(logger.LevelInfo, "invalid response from keyboard interactive plugin: %v", err)
  2676. return authResult, err
  2677. }
  2678. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  2679. if err != nil {
  2680. return authResult, err
  2681. }
  2682. authStep++
  2683. req = &plugin.KeyboardAuthRequest{
  2684. RequestID: requestID,
  2685. Step: authStep,
  2686. Username: user.Username,
  2687. Password: user.Password,
  2688. Answers: answers,
  2689. Questions: response.Questions,
  2690. }
  2691. }
  2692. }
  2693. func executeKeyboardInteractiveHTTPHook(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2694. authResult := 0
  2695. requestID := xid.New().String()
  2696. authStep := 1
  2697. req := &plugin.KeyboardAuthRequest{
  2698. Username: user.Username,
  2699. IP: ip,
  2700. Password: user.Password,
  2701. RequestID: requestID,
  2702. Step: authStep,
  2703. }
  2704. var response *plugin.KeyboardAuthResponse
  2705. var err error
  2706. for {
  2707. response, err = sendKeyboardAuthHTTPReq(authHook, req)
  2708. if err != nil {
  2709. return authResult, err
  2710. }
  2711. if response.AuthResult != 0 {
  2712. return response.AuthResult, err
  2713. }
  2714. if err = response.Validate(); err != nil {
  2715. providerLog(logger.LevelInfo, "invalid response from keyboard interactive http hook: %v", err)
  2716. return authResult, err
  2717. }
  2718. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  2719. if err != nil {
  2720. return authResult, err
  2721. }
  2722. authStep++
  2723. req = &plugin.KeyboardAuthRequest{
  2724. RequestID: requestID,
  2725. Step: authStep,
  2726. Username: user.Username,
  2727. Password: user.Password,
  2728. Answers: answers,
  2729. Questions: response.Questions,
  2730. }
  2731. }
  2732. }
  2733. func getKeyboardInteractiveAnswers(client ssh.KeyboardInteractiveChallenge, response *plugin.KeyboardAuthResponse,
  2734. user *User, ip, protocol string,
  2735. ) ([]string, error) {
  2736. questions := response.Questions
  2737. answers, err := client("", response.Instruction, questions, response.Echos)
  2738. if err != nil {
  2739. providerLog(logger.LevelInfo, "error getting interactive auth client response: %v", err)
  2740. return answers, err
  2741. }
  2742. if len(answers) != len(questions) {
  2743. err = fmt.Errorf("client answers does not match questions, expected: %v actual: %v", questions, answers)
  2744. providerLog(logger.LevelInfo, "keyboard interactive auth error: %v", err)
  2745. return answers, err
  2746. }
  2747. if len(answers) == 1 && response.CheckPwd > 0 {
  2748. if response.CheckPwd == 2 {
  2749. if !user.Filters.TOTPConfig.Enabled || !util.IsStringInSlice(protocolSSH, user.Filters.TOTPConfig.Protocols) {
  2750. providerLog(logger.LevelInfo, "keyboard interactive auth error: unable to check TOTP passcode, TOTP is not enabled for user %#v",
  2751. user.Username)
  2752. return answers, errors.New("TOTP not enabled for SSH protocol")
  2753. }
  2754. err := user.Filters.TOTPConfig.Secret.TryDecrypt()
  2755. if err != nil {
  2756. providerLog(logger.LevelError, "unable to decrypt TOTP secret for user %#v, protocol %v, err: %v",
  2757. user.Username, protocol, err)
  2758. return answers, fmt.Errorf("unable to decrypt TOTP secret: %w", err)
  2759. }
  2760. match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, answers[0],
  2761. user.Filters.TOTPConfig.Secret.GetPayload())
  2762. if !match || err != nil {
  2763. providerLog(logger.LevelInfo, "keyboard interactive auth error: unable to validate passcode for user %#v, match? %v, err: %v",
  2764. user.Username, match, err)
  2765. return answers, errors.New("unable to validate TOTP passcode")
  2766. }
  2767. } else {
  2768. _, err = checkUserAndPass(user, answers[0], ip, protocol)
  2769. providerLog(logger.LevelInfo, "interactive auth hook requested password validation for user %#v, validation error: %v",
  2770. user.Username, err)
  2771. if err != nil {
  2772. return answers, err
  2773. }
  2774. }
  2775. answers[0] = "OK"
  2776. }
  2777. return answers, err
  2778. }
  2779. func handleProgramInteractiveQuestions(client ssh.KeyboardInteractiveChallenge, response *plugin.KeyboardAuthResponse,
  2780. user *User, stdin io.WriteCloser, ip, protocol string,
  2781. ) error {
  2782. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  2783. if err != nil {
  2784. return err
  2785. }
  2786. for _, answer := range answers {
  2787. if runtime.GOOS == "windows" {
  2788. answer += "\r"
  2789. }
  2790. answer += "\n"
  2791. _, err = stdin.Write([]byte(answer))
  2792. if err != nil {
  2793. providerLog(logger.LevelError, "unable to write client answer to keyboard interactive program: %v", err)
  2794. return err
  2795. }
  2796. }
  2797. return nil
  2798. }
  2799. func executeKeyboardInteractiveProgram(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2800. authResult := 0
  2801. ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
  2802. defer cancel()
  2803. cmd := exec.CommandContext(ctx, authHook)
  2804. cmd.Env = append(os.Environ(),
  2805. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", user.Username),
  2806. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  2807. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", user.Password))
  2808. stdout, err := cmd.StdoutPipe()
  2809. if err != nil {
  2810. return authResult, err
  2811. }
  2812. stdin, err := cmd.StdinPipe()
  2813. if err != nil {
  2814. return authResult, err
  2815. }
  2816. err = cmd.Start()
  2817. if err != nil {
  2818. return authResult, err
  2819. }
  2820. var once sync.Once
  2821. scanner := bufio.NewScanner(stdout)
  2822. for scanner.Scan() {
  2823. var response plugin.KeyboardAuthResponse
  2824. err = json.Unmarshal(scanner.Bytes(), &response)
  2825. if err != nil {
  2826. providerLog(logger.LevelInfo, "interactive auth error parsing response: %v", err)
  2827. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  2828. break
  2829. }
  2830. if response.AuthResult != 0 {
  2831. authResult = response.AuthResult
  2832. break
  2833. }
  2834. if err = response.Validate(); err != nil {
  2835. providerLog(logger.LevelInfo, "invalid response from keyboard interactive program: %v", err)
  2836. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  2837. break
  2838. }
  2839. go func() {
  2840. err := handleProgramInteractiveQuestions(client, &response, user, stdin, ip, protocol)
  2841. if err != nil {
  2842. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  2843. }
  2844. }()
  2845. }
  2846. stdin.Close()
  2847. once.Do(func() { terminateInteractiveAuthProgram(cmd, true) })
  2848. go func() {
  2849. _, err := cmd.Process.Wait()
  2850. if err != nil {
  2851. providerLog(logger.LevelWarn, "error waiting for #%v process to exit: %v", authHook, err)
  2852. }
  2853. }()
  2854. return authResult, err
  2855. }
  2856. func doKeyboardInteractiveAuth(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (User, error) {
  2857. var authResult int
  2858. var err error
  2859. if plugin.Handler.HasAuthScope(plugin.AuthScopeKeyboardInteractive) {
  2860. authResult, err = executeKeyboardInteractivePlugin(user, client, ip, protocol)
  2861. } else if authHook != "" {
  2862. if strings.HasPrefix(authHook, "http") {
  2863. authResult, err = executeKeyboardInteractiveHTTPHook(user, authHook, client, ip, protocol)
  2864. } else {
  2865. authResult, err = executeKeyboardInteractiveProgram(user, authHook, client, ip, protocol)
  2866. }
  2867. } else {
  2868. authResult, err = doBuiltinKeyboardInteractiveAuth(user, client, ip, protocol)
  2869. }
  2870. if err != nil {
  2871. return *user, err
  2872. }
  2873. if authResult != 1 {
  2874. return *user, fmt.Errorf("keyboard interactive auth failed, result: %v", authResult)
  2875. }
  2876. err = user.LoadAndApplyGroupSettings()
  2877. if err != nil {
  2878. return *user, err
  2879. }
  2880. err = user.CheckLoginConditions()
  2881. if err != nil {
  2882. return *user, err
  2883. }
  2884. return *user, nil
  2885. }
  2886. func isCheckPasswordHookDefined(protocol string) bool {
  2887. if config.CheckPasswordHook == "" {
  2888. return false
  2889. }
  2890. if config.CheckPasswordScope == 0 {
  2891. return true
  2892. }
  2893. switch protocol {
  2894. case protocolSSH:
  2895. return config.CheckPasswordScope&1 != 0
  2896. case protocolFTP:
  2897. return config.CheckPasswordScope&2 != 0
  2898. case protocolWebDAV:
  2899. return config.CheckPasswordScope&4 != 0
  2900. default:
  2901. return false
  2902. }
  2903. }
  2904. func getPasswordHookResponse(username, password, ip, protocol string) ([]byte, error) {
  2905. if strings.HasPrefix(config.CheckPasswordHook, "http") {
  2906. var result []byte
  2907. req := checkPasswordRequest{
  2908. Username: username,
  2909. Password: password,
  2910. IP: ip,
  2911. Protocol: protocol,
  2912. }
  2913. reqAsJSON, err := json.Marshal(req)
  2914. if err != nil {
  2915. return result, err
  2916. }
  2917. resp, err := httpclient.Post(config.CheckPasswordHook, "application/json", bytes.NewBuffer(reqAsJSON))
  2918. if err != nil {
  2919. providerLog(logger.LevelError, "error getting check password hook response: %v", err)
  2920. return result, err
  2921. }
  2922. defer resp.Body.Close()
  2923. if resp.StatusCode != http.StatusOK {
  2924. return result, fmt.Errorf("wrong http status code from chek password hook: %v, expected 200", resp.StatusCode)
  2925. }
  2926. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  2927. }
  2928. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  2929. defer cancel()
  2930. cmd := exec.CommandContext(ctx, config.CheckPasswordHook)
  2931. cmd.Env = append(os.Environ(),
  2932. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username),
  2933. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password),
  2934. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  2935. fmt.Sprintf("SFTPGO_AUTHD_PROTOCOL=%v", protocol),
  2936. )
  2937. return cmd.Output()
  2938. }
  2939. func executeCheckPasswordHook(username, password, ip, protocol string) (checkPasswordResponse, error) {
  2940. var response checkPasswordResponse
  2941. if !isCheckPasswordHookDefined(protocol) {
  2942. response.Status = -1
  2943. return response, nil
  2944. }
  2945. startTime := time.Now()
  2946. out, err := getPasswordHookResponse(username, password, ip, protocol)
  2947. providerLog(logger.LevelDebug, "check password hook executed, error: %v, elapsed: %v", err, time.Since(startTime))
  2948. if err != nil {
  2949. return response, err
  2950. }
  2951. err = json.Unmarshal(out, &response)
  2952. return response, err
  2953. }
  2954. func getPreLoginHookResponse(loginMethod, ip, protocol string, userAsJSON []byte) ([]byte, error) {
  2955. if strings.HasPrefix(config.PreLoginHook, "http") {
  2956. var url *url.URL
  2957. var result []byte
  2958. url, err := url.Parse(config.PreLoginHook)
  2959. if err != nil {
  2960. providerLog(logger.LevelError, "invalid url for pre-login hook %#v, error: %v", config.PreLoginHook, err)
  2961. return result, err
  2962. }
  2963. q := url.Query()
  2964. q.Add("login_method", loginMethod)
  2965. q.Add("ip", ip)
  2966. q.Add("protocol", protocol)
  2967. url.RawQuery = q.Encode()
  2968. resp, err := httpclient.Post(url.String(), "application/json", bytes.NewBuffer(userAsJSON))
  2969. if err != nil {
  2970. providerLog(logger.LevelWarn, "error getting pre-login hook response: %v", err)
  2971. return result, err
  2972. }
  2973. defer resp.Body.Close()
  2974. if resp.StatusCode == http.StatusNoContent {
  2975. return result, nil
  2976. }
  2977. if resp.StatusCode != http.StatusOK {
  2978. return result, fmt.Errorf("wrong pre-login hook http status code: %v, expected 200", resp.StatusCode)
  2979. }
  2980. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  2981. }
  2982. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  2983. defer cancel()
  2984. cmd := exec.CommandContext(ctx, config.PreLoginHook)
  2985. cmd.Env = append(os.Environ(),
  2986. fmt.Sprintf("SFTPGO_LOGIND_USER=%v", string(userAsJSON)),
  2987. fmt.Sprintf("SFTPGO_LOGIND_METHOD=%v", loginMethod),
  2988. fmt.Sprintf("SFTPGO_LOGIND_IP=%v", ip),
  2989. fmt.Sprintf("SFTPGO_LOGIND_PROTOCOL=%v", protocol),
  2990. )
  2991. return cmd.Output()
  2992. }
  2993. func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFields *map[string]interface{}) (User, error) {
  2994. u, mergedUser, userAsJSON, err := getUserAndJSONForHook(username, oidcTokenFields)
  2995. if err != nil {
  2996. return u, err
  2997. }
  2998. if mergedUser.Filters.Hooks.PreLoginDisabled {
  2999. return u, nil
  3000. }
  3001. startTime := time.Now()
  3002. out, err := getPreLoginHookResponse(loginMethod, ip, protocol, userAsJSON)
  3003. if err != nil {
  3004. return u, fmt.Errorf("pre-login hook error: %v, username %#v, ip %v, protocol %v elapsed %v",
  3005. err, username, ip, protocol, time.Since(startTime))
  3006. }
  3007. providerLog(logger.LevelDebug, "pre-login hook completed, elapsed: %v", time.Since(startTime))
  3008. if util.IsByteArrayEmpty(out) {
  3009. providerLog(logger.LevelDebug, "empty response from pre-login hook, no modification requested for user %#v id: %v",
  3010. username, u.ID)
  3011. if u.ID == 0 {
  3012. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  3013. }
  3014. return u, nil
  3015. }
  3016. userID := u.ID
  3017. userPwd := u.Password
  3018. userUsedQuotaSize := u.UsedQuotaSize
  3019. userUsedQuotaFiles := u.UsedQuotaFiles
  3020. userUsedDownloadTransfer := u.UsedDownloadDataTransfer
  3021. userUsedUploadTransfer := u.UsedUploadDataTransfer
  3022. userLastQuotaUpdate := u.LastQuotaUpdate
  3023. userLastLogin := u.LastLogin
  3024. userCreatedAt := u.CreatedAt
  3025. totpConfig := u.Filters.TOTPConfig
  3026. recoveryCodes := u.Filters.RecoveryCodes
  3027. err = json.Unmarshal(out, &u)
  3028. if err != nil {
  3029. return u, fmt.Errorf("invalid pre-login hook response %#v, error: %v", string(out), err)
  3030. }
  3031. u.ID = userID
  3032. u.UsedQuotaSize = userUsedQuotaSize
  3033. u.UsedQuotaFiles = userUsedQuotaFiles
  3034. u.UsedUploadDataTransfer = userUsedUploadTransfer
  3035. u.UsedDownloadDataTransfer = userUsedDownloadTransfer
  3036. u.LastQuotaUpdate = userLastQuotaUpdate
  3037. u.LastLogin = userLastLogin
  3038. u.CreatedAt = userCreatedAt
  3039. if userID == 0 {
  3040. err = provider.addUser(&u)
  3041. } else {
  3042. u.UpdatedAt = util.GetTimeAsMsSinceEpoch(time.Now())
  3043. // preserve TOTP config and recovery codes
  3044. u.Filters.TOTPConfig = totpConfig
  3045. u.Filters.RecoveryCodes = recoveryCodes
  3046. err = provider.updateUser(&u)
  3047. if err == nil {
  3048. webDAVUsersCache.swap(&u)
  3049. if u.Password != userPwd {
  3050. cachedPasswords.Remove(username)
  3051. }
  3052. }
  3053. }
  3054. if err != nil {
  3055. return u, err
  3056. }
  3057. providerLog(logger.LevelDebug, "user %#v added/updated from pre-login hook response, id: %v", username, userID)
  3058. if userID == 0 {
  3059. return provider.userExists(username)
  3060. }
  3061. return u, nil
  3062. }
  3063. // ExecutePostLoginHook executes the post login hook if defined
  3064. func ExecutePostLoginHook(user *User, loginMethod, ip, protocol string, err error) {
  3065. if config.PostLoginHook == "" {
  3066. return
  3067. }
  3068. if config.PostLoginScope == 1 && err == nil {
  3069. return
  3070. }
  3071. if config.PostLoginScope == 2 && err != nil {
  3072. return
  3073. }
  3074. go func() {
  3075. status := "0"
  3076. if err == nil {
  3077. status = "1"
  3078. }
  3079. user.PrepareForRendering()
  3080. userAsJSON, err := json.Marshal(user)
  3081. if err != nil {
  3082. providerLog(logger.LevelError, "error serializing user in post login hook: %v", err)
  3083. return
  3084. }
  3085. if strings.HasPrefix(config.PostLoginHook, "http") {
  3086. var url *url.URL
  3087. url, err := url.Parse(config.PostLoginHook)
  3088. if err != nil {
  3089. providerLog(logger.LevelDebug, "Invalid post-login hook %#v", config.PostLoginHook)
  3090. return
  3091. }
  3092. q := url.Query()
  3093. q.Add("login_method", loginMethod)
  3094. q.Add("ip", ip)
  3095. q.Add("protocol", protocol)
  3096. q.Add("status", status)
  3097. url.RawQuery = q.Encode()
  3098. startTime := time.Now()
  3099. respCode := 0
  3100. resp, err := httpclient.RetryablePost(url.String(), "application/json", bytes.NewBuffer(userAsJSON))
  3101. if err == nil {
  3102. respCode = resp.StatusCode
  3103. resp.Body.Close()
  3104. }
  3105. providerLog(logger.LevelDebug, "post login hook executed for user %#v, ip %v, protocol %v, response code: %v, elapsed: %v err: %v",
  3106. user.Username, ip, protocol, respCode, time.Since(startTime), err)
  3107. return
  3108. }
  3109. ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
  3110. defer cancel()
  3111. cmd := exec.CommandContext(ctx, config.PostLoginHook)
  3112. cmd.Env = append(os.Environ(),
  3113. fmt.Sprintf("SFTPGO_LOGIND_USER=%v", string(userAsJSON)),
  3114. fmt.Sprintf("SFTPGO_LOGIND_IP=%v", ip),
  3115. fmt.Sprintf("SFTPGO_LOGIND_METHOD=%v", loginMethod),
  3116. fmt.Sprintf("SFTPGO_LOGIND_STATUS=%v", status),
  3117. fmt.Sprintf("SFTPGO_LOGIND_PROTOCOL=%v", protocol))
  3118. startTime := time.Now()
  3119. err = cmd.Run()
  3120. providerLog(logger.LevelDebug, "post login hook executed for user %#v, ip %v, protocol %v, elapsed %v err: %v",
  3121. user.Username, ip, protocol, time.Since(startTime), err)
  3122. }()
  3123. }
  3124. func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol string, cert *x509.Certificate, userAsJSON []byte) ([]byte, error) {
  3125. var tlsCert string
  3126. if cert != nil {
  3127. var err error
  3128. tlsCert, err = util.EncodeTLSCertToPem(cert)
  3129. if err != nil {
  3130. return nil, err
  3131. }
  3132. }
  3133. if strings.HasPrefix(config.ExternalAuthHook, "http") {
  3134. var result []byte
  3135. authRequest := make(map[string]string)
  3136. authRequest["username"] = username
  3137. authRequest["ip"] = ip
  3138. authRequest["password"] = password
  3139. authRequest["public_key"] = pkey
  3140. authRequest["protocol"] = protocol
  3141. authRequest["keyboard_interactive"] = keyboardInteractive
  3142. authRequest["tls_cert"] = tlsCert
  3143. if len(userAsJSON) > 0 {
  3144. authRequest["user"] = string(userAsJSON)
  3145. }
  3146. authRequestAsJSON, err := json.Marshal(authRequest)
  3147. if err != nil {
  3148. providerLog(logger.LevelError, "error serializing external auth request: %v", err)
  3149. return result, err
  3150. }
  3151. resp, err := httpclient.Post(config.ExternalAuthHook, "application/json", bytes.NewBuffer(authRequestAsJSON))
  3152. if err != nil {
  3153. providerLog(logger.LevelWarn, "error getting external auth hook HTTP response: %v", err)
  3154. return result, err
  3155. }
  3156. defer resp.Body.Close()
  3157. providerLog(logger.LevelDebug, "external auth hook executed, response code: %v", resp.StatusCode)
  3158. if resp.StatusCode != http.StatusOK {
  3159. return result, fmt.Errorf("wrong external auth http status code: %v, expected 200", resp.StatusCode)
  3160. }
  3161. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  3162. }
  3163. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  3164. defer cancel()
  3165. cmd := exec.CommandContext(ctx, config.ExternalAuthHook)
  3166. cmd.Env = append(os.Environ(),
  3167. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username),
  3168. fmt.Sprintf("SFTPGO_AUTHD_USER=%v", string(userAsJSON)),
  3169. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  3170. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password),
  3171. fmt.Sprintf("SFTPGO_AUTHD_PUBLIC_KEY=%v", pkey),
  3172. fmt.Sprintf("SFTPGO_AUTHD_PROTOCOL=%v", protocol),
  3173. fmt.Sprintf("SFTPGO_AUTHD_TLS_CERT=%v", strings.ReplaceAll(tlsCert, "\n", "\\n")),
  3174. fmt.Sprintf("SFTPGO_AUTHD_KEYBOARD_INTERACTIVE=%v", keyboardInteractive))
  3175. return cmd.Output()
  3176. }
  3177. func updateUserFromExtAuthResponse(user *User, password, pkey string) {
  3178. if password != "" {
  3179. user.Password = password
  3180. }
  3181. if pkey != "" && !util.IsStringPrefixInSlice(pkey, user.PublicKeys) {
  3182. user.PublicKeys = append(user.PublicKeys, pkey)
  3183. }
  3184. }
  3185. func doExternalAuth(username, password string, pubKey []byte, keyboardInteractive, ip, protocol string,
  3186. tlsCert *x509.Certificate,
  3187. ) (User, error) {
  3188. var user User
  3189. u, mergedUser, userAsJSON, err := getUserAndJSONForHook(username, nil)
  3190. if err != nil {
  3191. return user, err
  3192. }
  3193. if mergedUser.Filters.Hooks.ExternalAuthDisabled {
  3194. return u, nil
  3195. }
  3196. if mergedUser.isExternalAuthCached() {
  3197. return u, nil
  3198. }
  3199. pkey, err := util.GetSSHPublicKeyAsString(pubKey)
  3200. if err != nil {
  3201. return user, err
  3202. }
  3203. startTime := time.Now()
  3204. out, err := getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol, tlsCert, userAsJSON)
  3205. if err != nil {
  3206. return user, fmt.Errorf("external auth error for user %#v: %v, elapsed: %v", username, err, time.Since(startTime))
  3207. }
  3208. providerLog(logger.LevelDebug, "external auth completed for user %#v, elapsed: %v", username, time.Since(startTime))
  3209. if util.IsByteArrayEmpty(out) {
  3210. providerLog(logger.LevelDebug, "empty response from external hook, no modification requested for user %#v id: %v",
  3211. username, u.ID)
  3212. if u.ID == 0 {
  3213. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  3214. }
  3215. return u, nil
  3216. }
  3217. err = json.Unmarshal(out, &user)
  3218. if err != nil {
  3219. return user, fmt.Errorf("invalid external auth response: %v", err)
  3220. }
  3221. // an empty username means authentication failure
  3222. if user.Username == "" {
  3223. return user, ErrInvalidCredentials
  3224. }
  3225. updateUserFromExtAuthResponse(&user, password, pkey)
  3226. // some users want to map multiple login usernames with a single SFTPGo account
  3227. // for example an SFTP user logins using "user1" or "user2" and the external auth
  3228. // returns "user" in both cases, so we use the username returned from
  3229. // external auth and not the one used to login
  3230. if user.Username != username {
  3231. u, err = provider.userExists(user.Username)
  3232. }
  3233. if u.ID > 0 && err == nil {
  3234. user.ID = u.ID
  3235. user.UsedQuotaSize = u.UsedQuotaSize
  3236. user.UsedQuotaFiles = u.UsedQuotaFiles
  3237. user.UsedUploadDataTransfer = u.UsedUploadDataTransfer
  3238. user.UsedDownloadDataTransfer = u.UsedDownloadDataTransfer
  3239. user.LastQuotaUpdate = u.LastQuotaUpdate
  3240. user.LastLogin = u.LastLogin
  3241. user.CreatedAt = u.CreatedAt
  3242. user.UpdatedAt = util.GetTimeAsMsSinceEpoch(time.Now())
  3243. // preserve TOTP config and recovery codes
  3244. user.Filters.TOTPConfig = u.Filters.TOTPConfig
  3245. user.Filters.RecoveryCodes = u.Filters.RecoveryCodes
  3246. err = provider.updateUser(&user)
  3247. if err == nil {
  3248. webDAVUsersCache.swap(&user)
  3249. cachedPasswords.Add(user.Username, password)
  3250. }
  3251. return user, err
  3252. }
  3253. err = provider.addUser(&user)
  3254. if err != nil {
  3255. return user, err
  3256. }
  3257. return provider.userExists(user.Username)
  3258. }
  3259. func doPluginAuth(username, password string, pubKey []byte, ip, protocol string,
  3260. tlsCert *x509.Certificate, authScope int,
  3261. ) (User, error) {
  3262. var user User
  3263. u, mergedUser, userAsJSON, err := getUserAndJSONForHook(username, nil)
  3264. if err != nil {
  3265. return user, err
  3266. }
  3267. if mergedUser.Filters.Hooks.ExternalAuthDisabled {
  3268. return u, nil
  3269. }
  3270. if mergedUser.isExternalAuthCached() {
  3271. return u, nil
  3272. }
  3273. pkey, err := util.GetSSHPublicKeyAsString(pubKey)
  3274. if err != nil {
  3275. return user, err
  3276. }
  3277. startTime := time.Now()
  3278. out, err := plugin.Handler.Authenticate(username, password, ip, protocol, pkey, tlsCert, authScope, userAsJSON)
  3279. if err != nil {
  3280. return user, fmt.Errorf("plugin auth error for user %#v: %v, elapsed: %v, auth scope: %v",
  3281. username, err, time.Since(startTime), authScope)
  3282. }
  3283. providerLog(logger.LevelDebug, "plugin auth completed for user %#v, elapsed: %v,auth scope: %v",
  3284. username, time.Since(startTime), authScope)
  3285. if util.IsByteArrayEmpty(out) {
  3286. providerLog(logger.LevelDebug, "empty response from plugin auth, no modification requested for user %#v id: %v",
  3287. username, u.ID)
  3288. if u.ID == 0 {
  3289. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  3290. }
  3291. return u, nil
  3292. }
  3293. err = json.Unmarshal(out, &user)
  3294. if err != nil {
  3295. return user, fmt.Errorf("invalid plugin auth response: %v", err)
  3296. }
  3297. updateUserFromExtAuthResponse(&user, password, pkey)
  3298. if u.ID > 0 {
  3299. user.ID = u.ID
  3300. user.UsedQuotaSize = u.UsedQuotaSize
  3301. user.UsedQuotaFiles = u.UsedQuotaFiles
  3302. user.UsedUploadDataTransfer = u.UsedUploadDataTransfer
  3303. user.UsedDownloadDataTransfer = u.UsedDownloadDataTransfer
  3304. user.LastQuotaUpdate = u.LastQuotaUpdate
  3305. user.LastLogin = u.LastLogin
  3306. // preserve TOTP config and recovery codes
  3307. user.Filters.TOTPConfig = u.Filters.TOTPConfig
  3308. user.Filters.RecoveryCodes = u.Filters.RecoveryCodes
  3309. err = provider.updateUser(&user)
  3310. if err == nil {
  3311. webDAVUsersCache.swap(&user)
  3312. cachedPasswords.Add(user.Username, password)
  3313. }
  3314. return user, err
  3315. }
  3316. err = provider.addUser(&user)
  3317. if err != nil {
  3318. return user, err
  3319. }
  3320. return provider.userExists(user.Username)
  3321. }
  3322. func getUserAndJSONForHook(username string, oidcTokenFields *map[string]interface{}) (User, User, []byte, error) {
  3323. var userAsJSON []byte
  3324. u, err := provider.userExists(username)
  3325. if err != nil {
  3326. if _, ok := err.(*util.RecordNotFoundError); !ok {
  3327. return u, u, userAsJSON, err
  3328. }
  3329. u = User{
  3330. BaseUser: sdk.BaseUser{
  3331. ID: 0,
  3332. Username: username,
  3333. },
  3334. }
  3335. }
  3336. mergedUser := u.getACopy()
  3337. err = mergedUser.LoadAndApplyGroupSettings()
  3338. if err != nil {
  3339. return u, mergedUser, userAsJSON, err
  3340. }
  3341. u.OIDCCustomFields = oidcTokenFields
  3342. userAsJSON, err = json.Marshal(u)
  3343. if err != nil {
  3344. return u, mergedUser, userAsJSON, err
  3345. }
  3346. return u, mergedUser, userAsJSON, err
  3347. }
  3348. func isLastActivityRecent(lastActivity int64, minDelay time.Duration) bool {
  3349. lastActivityTime := util.GetTimeFromMsecSinceEpoch(lastActivity)
  3350. diff := -time.Until(lastActivityTime)
  3351. if diff < -10*time.Second {
  3352. return false
  3353. }
  3354. return diff < minDelay
  3355. }
  3356. func addGCSCredentialsToFolder(folder *vfs.BaseVirtualFolder) (bool, error) {
  3357. if folder.FsConfig.Provider != sdk.GCSFilesystemProvider {
  3358. return false, nil
  3359. }
  3360. if folder.FsConfig.GCSConfig.AutomaticCredentials > 0 {
  3361. return false, nil
  3362. }
  3363. if folder.FsConfig.GCSConfig.Credentials.IsValid() {
  3364. return false, nil
  3365. }
  3366. cred, err := os.ReadFile(folder.GetGCSCredentialsFilePath())
  3367. if err != nil {
  3368. return false, err
  3369. }
  3370. err = json.Unmarshal(cred, &folder.FsConfig.GCSConfig.Credentials)
  3371. if err != nil {
  3372. return false, err
  3373. }
  3374. return true, nil
  3375. }
  3376. func addGCSCredentialsToUser(user *User) (bool, error) {
  3377. if user.FsConfig.Provider != sdk.GCSFilesystemProvider {
  3378. return false, nil
  3379. }
  3380. if user.FsConfig.GCSConfig.AutomaticCredentials > 0 {
  3381. return false, nil
  3382. }
  3383. if user.FsConfig.GCSConfig.Credentials.IsValid() {
  3384. return false, nil
  3385. }
  3386. cred, err := os.ReadFile(user.GetGCSCredentialsFilePath())
  3387. if err != nil {
  3388. return false, err
  3389. }
  3390. err = json.Unmarshal(cred, &user.FsConfig.GCSConfig.Credentials)
  3391. if err != nil {
  3392. return false, err
  3393. }
  3394. return true, nil
  3395. }
  3396. func importGCSCredentials() error {
  3397. folders, err := provider.dumpFolders()
  3398. if err != nil {
  3399. return fmt.Errorf("unable to get folders: %w", err)
  3400. }
  3401. for idx := range folders {
  3402. folder := &folders[idx]
  3403. added, err := addGCSCredentialsToFolder(folder)
  3404. if err != nil {
  3405. return fmt.Errorf("unable to add GCS credentials to folder %#v: %w", folder.Name, err)
  3406. }
  3407. if added {
  3408. logger.InfoToConsole("importing GCS credentials for folder %#v", folder.Name)
  3409. providerLog(logger.LevelInfo, "importing GCS credentials for folder %#v", folder.Name)
  3410. if err = provider.updateFolder(folder); err != nil {
  3411. return fmt.Errorf("unable to update folder %#v: %w", folder.Name, err)
  3412. }
  3413. }
  3414. }
  3415. users, err := provider.dumpUsers()
  3416. if err != nil {
  3417. return fmt.Errorf("unable to get users: %w", err)
  3418. }
  3419. for idx := range users {
  3420. user := &users[idx]
  3421. added, err := addGCSCredentialsToUser(user)
  3422. if err != nil {
  3423. return fmt.Errorf("unable to add GCS credentials to user %#v: %w", user.Username, err)
  3424. }
  3425. if added {
  3426. logger.InfoToConsole("importing GCS credentials for user %#v", user.Username)
  3427. providerLog(logger.LevelInfo, "importing GCS credentials for user %#v", user.Username)
  3428. if err = provider.updateUser(user); err != nil {
  3429. return fmt.Errorf("unable to update user %#v: %w", user.Username, err)
  3430. }
  3431. }
  3432. }
  3433. return nil
  3434. }
  3435. func getConfigPath(name, configDir string) string {
  3436. if !util.IsFileInputValid(name) {
  3437. return ""
  3438. }
  3439. if name != "" && !filepath.IsAbs(name) {
  3440. return filepath.Join(configDir, name)
  3441. }
  3442. return name
  3443. }
  3444. func providerLog(level logger.LogLevel, format string, v ...interface{}) {
  3445. logger.Log(level, logSender, "", format, v...)
  3446. }