dataprovider.go 129 KB

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