dataprovider.go 131 KB

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