1package httpd
2
3import (
4	"context"
5	"crypto/tls"
6	"crypto/x509"
7	"errors"
8	"fmt"
9	"log"
10	"net"
11	"net/http"
12	"strings"
13	"time"
14
15	"github.com/go-chi/chi/v5"
16	"github.com/go-chi/chi/v5/middleware"
17	"github.com/go-chi/jwtauth/v5"
18	"github.com/go-chi/render"
19	"github.com/lestrrat-go/jwx/jwa"
20	"github.com/rs/cors"
21	"github.com/rs/xid"
22
23	"github.com/drakkan/sftpgo/v2/common"
24	"github.com/drakkan/sftpgo/v2/dataprovider"
25	"github.com/drakkan/sftpgo/v2/logger"
26	"github.com/drakkan/sftpgo/v2/mfa"
27	"github.com/drakkan/sftpgo/v2/sdk"
28	"github.com/drakkan/sftpgo/v2/smtp"
29	"github.com/drakkan/sftpgo/v2/util"
30	"github.com/drakkan/sftpgo/v2/version"
31)
32
33var (
34	compressor      = middleware.NewCompressor(5)
35	xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto")
36)
37
38type httpdServer struct {
39	binding           Binding
40	staticFilesPath   string
41	openAPIPath       string
42	enableWebAdmin    bool
43	enableWebClient   bool
44	renderOpenAPI     bool
45	router            *chi.Mux
46	tokenAuth         *jwtauth.JWTAuth
47	signingPassphrase string
48	cors              CorsConfig
49}
50
51func newHttpdServer(b Binding, staticFilesPath, signingPassphrase string, cors CorsConfig,
52	openAPIPath string,
53) *httpdServer {
54	if openAPIPath == "" {
55		b.RenderOpenAPI = false
56	}
57	return &httpdServer{
58		binding:           b,
59		staticFilesPath:   staticFilesPath,
60		openAPIPath:       openAPIPath,
61		enableWebAdmin:    b.EnableWebAdmin,
62		enableWebClient:   b.EnableWebClient,
63		renderOpenAPI:     b.RenderOpenAPI,
64		signingPassphrase: signingPassphrase,
65		cors:              cors,
66	}
67}
68
69func (s *httpdServer) listenAndServe() error {
70	s.initializeRouter()
71	httpServer := &http.Server{
72		Handler:           s.router,
73		ReadHeaderTimeout: 30 * time.Second,
74		ReadTimeout:       60 * time.Second,
75		WriteTimeout:      60 * time.Second,
76		IdleTimeout:       60 * time.Second,
77		MaxHeaderBytes:    1 << 16, // 64KB
78		ErrorLog:          log.New(&logger.StdLoggerWrapper{Sender: logSender}, "", 0),
79	}
80	if certMgr != nil && s.binding.EnableHTTPS {
81		config := &tls.Config{
82			GetCertificate:           certMgr.GetCertificateFunc(),
83			MinVersion:               tls.VersionTLS12,
84			CipherSuites:             util.GetTLSCiphersFromNames(s.binding.TLSCipherSuites),
85			PreferServerCipherSuites: true,
86		}
87		logger.Debug(logSender, "", "configured TLS cipher suites for binding %#v: %v", s.binding.GetAddress(),
88			config.CipherSuites)
89		httpServer.TLSConfig = config
90		if s.binding.ClientAuthType == 1 {
91			httpServer.TLSConfig.ClientCAs = certMgr.GetRootCAs()
92			httpServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
93			httpServer.TLSConfig.VerifyConnection = s.verifyTLSConnection
94		}
95		return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, true, logSender)
96	}
97	return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, false, logSender)
98}
99
100func (s *httpdServer) verifyTLSConnection(state tls.ConnectionState) error {
101	if certMgr != nil {
102		var clientCrt *x509.Certificate
103		var clientCrtName string
104		if len(state.PeerCertificates) > 0 {
105			clientCrt = state.PeerCertificates[0]
106			clientCrtName = clientCrt.Subject.String()
107		}
108		if len(state.VerifiedChains) == 0 {
109			logger.Warn(logSender, "", "TLS connection cannot be verified: unable to get verification chain")
110			return errors.New("TLS connection cannot be verified: unable to get verification chain")
111		}
112		for _, verifiedChain := range state.VerifiedChains {
113			var caCrt *x509.Certificate
114			if len(verifiedChain) > 0 {
115				caCrt = verifiedChain[len(verifiedChain)-1]
116			}
117			if certMgr.IsRevoked(clientCrt, caCrt) {
118				logger.Debug(logSender, "", "tls handshake error, client certificate %#v has been revoked", clientCrtName)
119				return common.ErrCrtRevoked
120			}
121		}
122	}
123
124	return nil
125}
126
127func (s *httpdServer) refreshCookie(next http.Handler) http.Handler {
128	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
129		s.checkCookieExpiration(w, r)
130		next.ServeHTTP(w, r)
131	})
132}
133
134func (s *httpdServer) renderClientLoginPage(w http.ResponseWriter, error string) {
135	data := loginPage{
136		CurrentURL: webClientLoginPath,
137		Version:    version.Get().Version,
138		Error:      error,
139		CSRFToken:  createCSRFToken(),
140		StaticURL:  webStaticFilesPath,
141	}
142	if s.binding.showAdminLoginURL() {
143		data.AltLoginURL = webLoginPath
144	}
145	if smtp.IsEnabled() {
146		data.ForgotPwdURL = webClientForgotPwdPath
147	}
148	renderClientTemplate(w, templateClientLogin, data)
149}
150
151func (s *httpdServer) handleClientWebLogin(w http.ResponseWriter, r *http.Request) {
152	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
153	if !dataprovider.HasAdmin() {
154		http.Redirect(w, r, webAdminSetupPath, http.StatusFound)
155		return
156	}
157	s.renderClientLoginPage(w, "")
158}
159
160func (s *httpdServer) handleWebClientLoginPost(w http.ResponseWriter, r *http.Request) {
161	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
162
163	if err := r.ParseForm(); err != nil {
164		s.renderClientLoginPage(w, err.Error())
165		return
166	}
167	ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
168	username := r.Form.Get("username")
169	password := r.Form.Get("password")
170	if username == "" || password == "" {
171		updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}}, ipAddr, common.ErrNoCredentials)
172		s.renderClientLoginPage(w, "Invalid credentials")
173		return
174	}
175	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
176		updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}}, ipAddr, err)
177		s.renderClientLoginPage(w, err.Error())
178		return
179	}
180
181	if err := common.Config.ExecutePostConnectHook(ipAddr, common.ProtocolHTTP); err != nil {
182		s.renderClientLoginPage(w, fmt.Sprintf("access denied by post connect hook: %v", err))
183		return
184	}
185
186	user, err := dataprovider.CheckUserAndPass(username, password, ipAddr, common.ProtocolHTTP)
187	if err != nil {
188		updateLoginMetrics(&user, ipAddr, err)
189		s.renderClientLoginPage(w, dataprovider.ErrInvalidCredentials.Error())
190		return
191	}
192	connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, xid.New().String())
193	if err := checkHTTPClientUser(&user, r, connectionID); err != nil {
194		updateLoginMetrics(&user, ipAddr, err)
195		s.renderClientLoginPage(w, err.Error())
196		return
197	}
198
199	defer user.CloseFs() //nolint:errcheck
200	err = user.CheckFsRoot(connectionID)
201	if err != nil {
202		logger.Warn(logSender, connectionID, "unable to check fs root: %v", err)
203		updateLoginMetrics(&user, ipAddr, common.ErrInternalFailure)
204		s.renderClientLoginPage(w, err.Error())
205		return
206	}
207	s.loginUser(w, r, &user, connectionID, ipAddr, false, s.renderClientLoginPage)
208}
209
210func (s *httpdServer) handleWebClientPasswordResetPost(w http.ResponseWriter, r *http.Request) {
211	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
212	err := r.ParseForm()
213	if err != nil {
214		renderClientResetPwdPage(w, err.Error())
215		return
216	}
217	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
218		renderClientForbiddenPage(w, r, err.Error())
219		return
220	}
221	_, user, err := handleResetPassword(r, r.Form.Get("code"), r.Form.Get("password"), false)
222	if err != nil {
223		if e, ok := err.(*util.ValidationError); ok {
224			renderClientResetPwdPage(w, e.GetErrorString())
225			return
226		}
227		renderClientResetPwdPage(w, err.Error())
228		return
229	}
230	connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, xid.New().String())
231	if err := checkHTTPClientUser(user, r, connectionID); err != nil {
232		renderClientResetPwdPage(w, fmt.Sprintf("Password reset successfully but unable to login: %v", err.Error()))
233		return
234	}
235
236	defer user.CloseFs() //nolint:errcheck
237	err = user.CheckFsRoot(connectionID)
238	if err != nil {
239		logger.Warn(logSender, connectionID, "unable to check fs root: %v", err)
240		renderClientResetPwdPage(w, fmt.Sprintf("Password reset successfully but unable to login: %v", err.Error()))
241		return
242	}
243	ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
244	s.loginUser(w, r, user, connectionID, ipAddr, false, renderClientResetPwdPage)
245}
246
247func (s *httpdServer) handleWebClientTwoFactorRecoveryPost(w http.ResponseWriter, r *http.Request) {
248	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
249	claims, err := getTokenClaims(r)
250	if err != nil {
251		renderNotFoundPage(w, r, nil)
252		return
253	}
254	if err := r.ParseForm(); err != nil {
255		renderClientTwoFactorRecoveryPage(w, err.Error())
256		return
257	}
258	username := claims.Username
259	recoveryCode := r.Form.Get("recovery_code")
260	if username == "" || recoveryCode == "" {
261		renderClientTwoFactorRecoveryPage(w, "Invalid credentials")
262		return
263	}
264	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
265		renderClientTwoFactorRecoveryPage(w, err.Error())
266		return
267	}
268	user, err := dataprovider.UserExists(username)
269	if err != nil {
270		renderClientTwoFactorRecoveryPage(w, "Invalid credentials")
271		return
272	}
273	if !user.Filters.TOTPConfig.Enabled || !util.IsStringInSlice(common.ProtocolHTTP, user.Filters.TOTPConfig.Protocols) {
274		renderClientTwoFactorPage(w, "Two factory authentication is not enabled")
275		return
276	}
277	for idx, code := range user.Filters.RecoveryCodes {
278		if err := code.Secret.Decrypt(); err != nil {
279			renderClientInternalServerErrorPage(w, r, fmt.Errorf("unable to decrypt recovery code: %w", err))
280			return
281		}
282		if code.Secret.GetPayload() == recoveryCode {
283			if code.Used {
284				renderClientTwoFactorRecoveryPage(w, "This recovery code was already used")
285				return
286			}
287			user.Filters.RecoveryCodes[idx].Used = true
288			err = dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSelf, util.GetIPFromRemoteAddress(r.RemoteAddr))
289			if err != nil {
290				logger.Warn(logSender, "", "unable to set the recovery code %#v as used: %v", recoveryCode, err)
291				renderClientInternalServerErrorPage(w, r, errors.New("unable to set the recovery code as used"))
292				return
293			}
294			connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, xid.New().String())
295			s.loginUser(w, r, &user, connectionID, util.GetIPFromRemoteAddress(r.RemoteAddr), true,
296				renderClientTwoFactorRecoveryPage)
297			return
298		}
299	}
300	renderClientTwoFactorRecoveryPage(w, "Invalid recovery code")
301}
302
303func (s *httpdServer) handleWebClientTwoFactorPost(w http.ResponseWriter, r *http.Request) {
304	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
305	claims, err := getTokenClaims(r)
306	if err != nil {
307		renderNotFoundPage(w, r, nil)
308		return
309	}
310	if err := r.ParseForm(); err != nil {
311		renderClientTwoFactorPage(w, err.Error())
312		return
313	}
314	username := claims.Username
315	passcode := r.Form.Get("passcode")
316	if username == "" || passcode == "" {
317		renderClientTwoFactorPage(w, "Invalid credentials")
318		return
319	}
320	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
321		renderClientTwoFactorPage(w, err.Error())
322		return
323	}
324	user, err := dataprovider.UserExists(username)
325	if err != nil {
326		renderClientTwoFactorPage(w, "Invalid credentials")
327		return
328	}
329	if !user.Filters.TOTPConfig.Enabled || !util.IsStringInSlice(common.ProtocolHTTP, user.Filters.TOTPConfig.Protocols) {
330		renderClientTwoFactorPage(w, "Two factory authentication is not enabled")
331		return
332	}
333	err = user.Filters.TOTPConfig.Secret.Decrypt()
334	if err != nil {
335		renderClientInternalServerErrorPage(w, r, err)
336		return
337	}
338	match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, passcode,
339		user.Filters.TOTPConfig.Secret.GetPayload())
340	if !match || err != nil {
341		renderClientTwoFactorPage(w, "Invalid authentication code")
342		return
343	}
344	connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, xid.New().String())
345	s.loginUser(w, r, &user, connectionID, util.GetIPFromRemoteAddress(r.RemoteAddr), true, renderClientTwoFactorPage)
346}
347
348func (s *httpdServer) handleWebAdminTwoFactorRecoveryPost(w http.ResponseWriter, r *http.Request) {
349	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
350	claims, err := getTokenClaims(r)
351	if err != nil {
352		renderNotFoundPage(w, r, nil)
353		return
354	}
355	if err := r.ParseForm(); err != nil {
356		renderTwoFactorRecoveryPage(w, err.Error())
357		return
358	}
359	username := claims.Username
360	recoveryCode := r.Form.Get("recovery_code")
361	if username == "" || recoveryCode == "" {
362		renderTwoFactorRecoveryPage(w, "Invalid credentials")
363		return
364	}
365	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
366		renderTwoFactorRecoveryPage(w, err.Error())
367		return
368	}
369	admin, err := dataprovider.AdminExists(username)
370	if err != nil {
371		renderTwoFactorRecoveryPage(w, "Invalid credentials")
372		return
373	}
374	if !admin.Filters.TOTPConfig.Enabled {
375		renderTwoFactorRecoveryPage(w, "Two factory authentication is not enabled")
376		return
377	}
378	for idx, code := range admin.Filters.RecoveryCodes {
379		if err := code.Secret.Decrypt(); err != nil {
380			renderInternalServerErrorPage(w, r, fmt.Errorf("unable to decrypt recovery code: %w", err))
381			return
382		}
383		if code.Secret.GetPayload() == recoveryCode {
384			if code.Used {
385				renderTwoFactorRecoveryPage(w, "This recovery code was already used")
386				return
387			}
388			admin.Filters.RecoveryCodes[idx].Used = true
389			err = dataprovider.UpdateAdmin(&admin, dataprovider.ActionExecutorSelf, util.GetIPFromRemoteAddress(r.RemoteAddr))
390			if err != nil {
391				logger.Warn(logSender, "", "unable to set the recovery code %#v as used: %v", recoveryCode, err)
392				renderInternalServerErrorPage(w, r, errors.New("unable to set the recovery code as used"))
393				return
394			}
395			s.loginAdmin(w, r, &admin, true, renderTwoFactorRecoveryPage)
396			return
397		}
398	}
399	renderTwoFactorRecoveryPage(w, "Invalid recovery code")
400}
401
402func (s *httpdServer) handleWebAdminTwoFactorPost(w http.ResponseWriter, r *http.Request) {
403	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
404	claims, err := getTokenClaims(r)
405	if err != nil {
406		renderNotFoundPage(w, r, nil)
407		return
408	}
409	if err := r.ParseForm(); err != nil {
410		renderTwoFactorPage(w, err.Error())
411		return
412	}
413	username := claims.Username
414	passcode := r.Form.Get("passcode")
415	if username == "" || passcode == "" {
416		renderTwoFactorPage(w, "Invalid credentials")
417		return
418	}
419	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
420		renderTwoFactorPage(w, err.Error())
421		return
422	}
423	admin, err := dataprovider.AdminExists(username)
424	if err != nil {
425		renderTwoFactorPage(w, "Invalid credentials")
426		return
427	}
428	if !admin.Filters.TOTPConfig.Enabled {
429		renderTwoFactorPage(w, "Two factory authentication is not enabled")
430		return
431	}
432	err = admin.Filters.TOTPConfig.Secret.Decrypt()
433	if err != nil {
434		renderInternalServerErrorPage(w, r, err)
435		return
436	}
437	match, err := mfa.ValidateTOTPPasscode(admin.Filters.TOTPConfig.ConfigName, passcode,
438		admin.Filters.TOTPConfig.Secret.GetPayload())
439	if !match || err != nil {
440		renderTwoFactorPage(w, "Invalid authentication code")
441		return
442	}
443	s.loginAdmin(w, r, &admin, true, renderTwoFactorPage)
444}
445
446func (s *httpdServer) handleWebAdminLoginPost(w http.ResponseWriter, r *http.Request) {
447	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
448	if err := r.ParseForm(); err != nil {
449		s.renderAdminLoginPage(w, err.Error())
450		return
451	}
452	username := r.Form.Get("username")
453	password := r.Form.Get("password")
454	if username == "" || password == "" {
455		s.renderAdminLoginPage(w, "Invalid credentials")
456		return
457	}
458	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
459		s.renderAdminLoginPage(w, err.Error())
460		return
461	}
462	admin, err := dataprovider.CheckAdminAndPass(username, password, util.GetIPFromRemoteAddress(r.RemoteAddr))
463	if err != nil {
464		s.renderAdminLoginPage(w, err.Error())
465		return
466	}
467	s.loginAdmin(w, r, &admin, false, s.renderAdminLoginPage)
468}
469
470func (s *httpdServer) renderAdminLoginPage(w http.ResponseWriter, error string) {
471	data := loginPage{
472		CurrentURL: webLoginPath,
473		Version:    version.Get().Version,
474		Error:      error,
475		CSRFToken:  createCSRFToken(),
476		StaticURL:  webStaticFilesPath,
477	}
478	if s.binding.showClientLoginURL() {
479		data.AltLoginURL = webClientLoginPath
480	}
481	if smtp.IsEnabled() {
482		data.ForgotPwdURL = webAdminForgotPwdPath
483	}
484	renderAdminTemplate(w, templateLogin, data)
485}
486
487func (s *httpdServer) handleWebAdminLogin(w http.ResponseWriter, r *http.Request) {
488	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
489	if !dataprovider.HasAdmin() {
490		http.Redirect(w, r, webAdminSetupPath, http.StatusFound)
491		return
492	}
493	s.renderAdminLoginPage(w, "")
494}
495
496func (s *httpdServer) handleWebAdminPasswordResetPost(w http.ResponseWriter, r *http.Request) {
497	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
498	err := r.ParseForm()
499	if err != nil {
500		renderResetPwdPage(w, err.Error())
501		return
502	}
503	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
504		renderForbiddenPage(w, r, err.Error())
505		return
506	}
507	admin, _, err := handleResetPassword(r, r.Form.Get("code"), r.Form.Get("password"), true)
508	if err != nil {
509		if e, ok := err.(*util.ValidationError); ok {
510			renderResetPwdPage(w, e.GetErrorString())
511			return
512		}
513		renderResetPwdPage(w, err.Error())
514		return
515	}
516
517	s.loginAdmin(w, r, admin, false, renderResetPwdPage)
518}
519
520func (s *httpdServer) handleWebAdminSetupPost(w http.ResponseWriter, r *http.Request) {
521	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
522	if dataprovider.HasAdmin() {
523		renderBadRequestPage(w, r, errors.New("an admin user already exists"))
524		return
525	}
526	err := r.ParseForm()
527	if err != nil {
528		renderAdminSetupPage(w, r, "", err.Error())
529		return
530	}
531	if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
532		renderForbiddenPage(w, r, err.Error())
533		return
534	}
535	username := r.Form.Get("username")
536	password := r.Form.Get("password")
537	confirmPassword := r.Form.Get("confirm_password")
538	if username == "" {
539		renderAdminSetupPage(w, r, username, "Please set a username")
540		return
541	}
542	if password == "" {
543		renderAdminSetupPage(w, r, username, "Please set a password")
544		return
545	}
546	if password != confirmPassword {
547		renderAdminSetupPage(w, r, username, "Passwords mismatch")
548		return
549	}
550	admin := dataprovider.Admin{
551		Username:    username,
552		Password:    password,
553		Status:      1,
554		Permissions: []string{dataprovider.PermAdminAny},
555	}
556	err = dataprovider.AddAdmin(&admin, username, util.GetIPFromRemoteAddress(r.RemoteAddr))
557	if err != nil {
558		renderAdminSetupPage(w, r, username, err.Error())
559		return
560	}
561	s.loginAdmin(w, r, &admin, false, nil)
562}
563
564func (s *httpdServer) loginUser(
565	w http.ResponseWriter, r *http.Request, user *dataprovider.User, connectionID, ipAddr string,
566	isSecondFactorAuth bool, errorFunc func(w http.ResponseWriter, error string),
567) {
568	c := jwtTokenClaims{
569		Username:    user.Username,
570		Permissions: user.Filters.WebClient,
571		Signature:   user.GetSignature(),
572	}
573
574	audience := tokenAudienceWebClient
575	if user.Filters.TOTPConfig.Enabled && util.IsStringInSlice(common.ProtocolHTTP, user.Filters.TOTPConfig.Protocols) &&
576		user.CanManageMFA() && !isSecondFactorAuth {
577		audience = tokenAudienceWebClientPartial
578	}
579
580	err := c.createAndSetCookie(w, r, s.tokenAuth, audience)
581	if err != nil {
582		logger.Warn(logSender, connectionID, "unable to set user login cookie %v", err)
583		updateLoginMetrics(user, ipAddr, common.ErrInternalFailure)
584		errorFunc(w, err.Error())
585		return
586	}
587	if isSecondFactorAuth {
588		invalidateToken(r)
589	}
590	if audience == tokenAudienceWebClientPartial {
591		http.Redirect(w, r, webClientTwoFactorPath, http.StatusFound)
592		return
593	}
594	updateLoginMetrics(user, ipAddr, err)
595	dataprovider.UpdateLastLogin(user)
596	http.Redirect(w, r, webClientFilesPath, http.StatusFound)
597}
598
599func (s *httpdServer) loginAdmin(
600	w http.ResponseWriter, r *http.Request, admin *dataprovider.Admin,
601	isSecondFactorAuth bool, errorFunc func(w http.ResponseWriter, error string),
602) {
603	c := jwtTokenClaims{
604		Username:    admin.Username,
605		Permissions: admin.Permissions,
606		Signature:   admin.GetSignature(),
607	}
608
609	audience := tokenAudienceWebAdmin
610	if admin.Filters.TOTPConfig.Enabled && admin.CanManageMFA() && !isSecondFactorAuth {
611		audience = tokenAudienceWebAdminPartial
612	}
613
614	err := c.createAndSetCookie(w, r, s.tokenAuth, audience)
615	if err != nil {
616		logger.Warn(logSender, "", "unable to set admin login cookie %v", err)
617		if errorFunc == nil {
618			renderAdminSetupPage(w, r, admin.Username, err.Error())
619			return
620		}
621		errorFunc(w, err.Error())
622		return
623	}
624	if isSecondFactorAuth {
625		invalidateToken(r)
626	}
627	if audience == tokenAudienceWebAdminPartial {
628		http.Redirect(w, r, webAdminTwoFactorPath, http.StatusFound)
629		return
630	}
631	dataprovider.UpdateAdminLastLogin(admin)
632	http.Redirect(w, r, webUsersPath, http.StatusFound)
633}
634
635func (s *httpdServer) logout(w http.ResponseWriter, r *http.Request) {
636	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
637	invalidateToken(r)
638	sendAPIResponse(w, r, nil, "Your token has been invalidated", http.StatusOK)
639}
640
641func (s *httpdServer) getUserToken(w http.ResponseWriter, r *http.Request) {
642	r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
643	ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
644	username, password, ok := r.BasicAuth()
645	if !ok {
646		updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}}, ipAddr, common.ErrNoCredentials)
647		w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
648		sendAPIResponse(w, r, nil, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
649		return
650	}
651	if username == "" || password == "" {
652		updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}}, ipAddr, common.ErrNoCredentials)
653		w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
654		sendAPIResponse(w, r, nil, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
655		return
656	}
657	if err := common.Config.ExecutePostConnectHook(ipAddr, common.ProtocolHTTP); err != nil {
658		sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
659		return
660	}
661	user, err := dataprovider.CheckUserAndPass(username, password, ipAddr, common.ProtocolHTTP)
662	if err != nil {
663		w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
664		updateLoginMetrics(&user, ipAddr, err)
665		sendAPIResponse(w, r, dataprovider.ErrInvalidCredentials, http.StatusText(http.StatusUnauthorized),
666			http.StatusUnauthorized)
667		return
668	}
669	connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, xid.New().String())
670	if err := checkHTTPClientUser(&user, r, connectionID); err != nil {
671		updateLoginMetrics(&user, ipAddr, err)
672		sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
673		return
674	}
675
676	if user.Filters.TOTPConfig.Enabled && util.IsStringInSlice(common.ProtocolHTTP, user.Filters.TOTPConfig.Protocols) {
677		passcode := r.Header.Get(otpHeaderCode)
678		if passcode == "" {
679			logger.Debug(logSender, "", "TOTP enabled for user %#v and not passcode provided, authentication refused", user.Username)
680			w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
681			updateLoginMetrics(&user, ipAddr, dataprovider.ErrInvalidCredentials)
682			sendAPIResponse(w, r, dataprovider.ErrInvalidCredentials, http.StatusText(http.StatusUnauthorized),
683				http.StatusUnauthorized)
684			return
685		}
686		err = user.Filters.TOTPConfig.Secret.Decrypt()
687		if err != nil {
688			updateLoginMetrics(&user, ipAddr, common.ErrInternalFailure)
689			sendAPIResponse(w, r, fmt.Errorf("unable to decrypt TOTP secret: %w", err), http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
690			return
691		}
692		match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, passcode,
693			user.Filters.TOTPConfig.Secret.GetPayload())
694		if !match || err != nil {
695			logger.Debug(logSender, "invalid passcode for user %#v, match? %v, err: %v", user.Username, match, err)
696			w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
697			updateLoginMetrics(&user, ipAddr, dataprovider.ErrInvalidCredentials)
698			sendAPIResponse(w, r, dataprovider.ErrInvalidCredentials, http.StatusText(http.StatusUnauthorized),
699				http.StatusUnauthorized)
700			return
701		}
702	}
703
704	defer user.CloseFs() //nolint:errcheck
705	err = user.CheckFsRoot(connectionID)
706	if err != nil {
707		logger.Warn(logSender, connectionID, "unable to check fs root: %v", err)
708		updateLoginMetrics(&user, ipAddr, common.ErrInternalFailure)
709		sendAPIResponse(w, r, err, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
710		return
711	}
712
713	s.generateAndSendUserToken(w, r, ipAddr, user)
714}
715
716func (s *httpdServer) generateAndSendUserToken(w http.ResponseWriter, r *http.Request, ipAddr string, user dataprovider.User) {
717	c := jwtTokenClaims{
718		Username:    user.Username,
719		Permissions: user.Filters.WebClient,
720		Signature:   user.GetSignature(),
721	}
722
723	resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPIUser)
724
725	if err != nil {
726		updateLoginMetrics(&user, ipAddr, common.ErrInternalFailure)
727		sendAPIResponse(w, r, err, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
728		return
729	}
730	updateLoginMetrics(&user, ipAddr, err)
731	dataprovider.UpdateLastLogin(&user)
732
733	render.JSON(w, r, resp)
734}
735
736func (s *httpdServer) getToken(w http.ResponseWriter, r *http.Request) {
737	username, password, ok := r.BasicAuth()
738	if !ok {
739		w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
740		sendAPIResponse(w, r, nil, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
741		return
742	}
743	admin, err := dataprovider.CheckAdminAndPass(username, password, util.GetIPFromRemoteAddress(r.RemoteAddr))
744	if err != nil {
745		w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
746		sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
747		return
748	}
749	if admin.Filters.TOTPConfig.Enabled {
750		passcode := r.Header.Get(otpHeaderCode)
751		if passcode == "" {
752			logger.Debug(logSender, "", "TOTP enabled for admin %#v and not passcode provided, authentication refused", admin.Username)
753			w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
754			sendAPIResponse(w, r, dataprovider.ErrInvalidCredentials, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
755			return
756		}
757		err = admin.Filters.TOTPConfig.Secret.Decrypt()
758		if err != nil {
759			sendAPIResponse(w, r, fmt.Errorf("unable to decrypt TOTP secret: %w", err),
760				http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
761			return
762		}
763		match, err := mfa.ValidateTOTPPasscode(admin.Filters.TOTPConfig.ConfigName, passcode,
764			admin.Filters.TOTPConfig.Secret.GetPayload())
765		if !match || err != nil {
766			logger.Debug(logSender, "invalid passcode for admin %#v, match? %v, err: %v", admin.Username, match, err)
767			w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
768			sendAPIResponse(w, r, dataprovider.ErrInvalidCredentials, http.StatusText(http.StatusUnauthorized),
769				http.StatusUnauthorized)
770			return
771		}
772	}
773
774	s.generateAndSendToken(w, r, admin)
775}
776
777func (s *httpdServer) generateAndSendToken(w http.ResponseWriter, r *http.Request, admin dataprovider.Admin) {
778	c := jwtTokenClaims{
779		Username:    admin.Username,
780		Permissions: admin.Permissions,
781		Signature:   admin.GetSignature(),
782	}
783
784	resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPI)
785
786	if err != nil {
787		sendAPIResponse(w, r, err, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
788		return
789	}
790
791	dataprovider.UpdateAdminLastLogin(&admin)
792	render.JSON(w, r, resp)
793}
794
795func (s *httpdServer) checkCookieExpiration(w http.ResponseWriter, r *http.Request) {
796	token, claims, err := jwtauth.FromContext(r.Context())
797	if err != nil {
798		return
799	}
800	tokenClaims := jwtTokenClaims{}
801	tokenClaims.Decode(claims)
802	if tokenClaims.Username == "" || tokenClaims.Signature == "" {
803		return
804	}
805	if time.Until(token.Expiration()) > tokenRefreshThreshold {
806		return
807	}
808	if util.IsStringInSlice(tokenAudienceWebClient, token.Audience()) {
809		s.refreshClientToken(w, r, tokenClaims)
810	} else {
811		s.refreshAdminToken(w, r, tokenClaims)
812	}
813}
814
815func (s *httpdServer) refreshClientToken(w http.ResponseWriter, r *http.Request, tokenClaims jwtTokenClaims) {
816	user, err := dataprovider.UserExists(tokenClaims.Username)
817	if err != nil {
818		return
819	}
820	if user.GetSignature() != tokenClaims.Signature {
821		logger.Debug(logSender, "", "signature mismatch for user %#v, unable to refresh cookie", user.Username)
822		return
823	}
824	if err := checkHTTPClientUser(&user, r, xid.New().String()); err != nil {
825		logger.Debug(logSender, "", "unable to refresh cookie for user %#v: %v", user.Username, err)
826		return
827	}
828
829	tokenClaims.Permissions = user.Filters.WebClient
830	logger.Debug(logSender, "", "cookie refreshed for user %#v", user.Username)
831	tokenClaims.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebClient) //nolint:errcheck
832}
833
834func (s *httpdServer) refreshAdminToken(w http.ResponseWriter, r *http.Request, tokenClaims jwtTokenClaims) {
835	admin, err := dataprovider.AdminExists(tokenClaims.Username)
836	if err != nil {
837		return
838	}
839	if admin.Status != 1 {
840		logger.Debug(logSender, "", "admin %#v is disabled, unable to refresh cookie", admin.Username)
841		return
842	}
843	if admin.GetSignature() != tokenClaims.Signature {
844		logger.Debug(logSender, "", "signature mismatch for admin %#v, unable to refresh cookie", admin.Username)
845		return
846	}
847	if !admin.CanLoginFromIP(util.GetIPFromRemoteAddress(r.RemoteAddr)) {
848		logger.Debug(logSender, "", "admin %#v cannot login from %v, unable to refresh cookie", admin.Username, r.RemoteAddr)
849		return
850	}
851	tokenClaims.Permissions = admin.Permissions
852	logger.Debug(logSender, "", "cookie refreshed for admin %#v", admin.Username)
853	tokenClaims.createAndSetCookie(w, r, s.tokenAuth, tokenAudienceWebAdmin) //nolint:errcheck
854}
855
856func (s *httpdServer) updateContextFromCookie(r *http.Request) *http.Request {
857	token, _, err := jwtauth.FromContext(r.Context())
858	if token == nil || err != nil {
859		_, err = r.Cookie("jwt")
860		if err != nil {
861			return r
862		}
863		token, err = jwtauth.VerifyRequest(s.tokenAuth, r, jwtauth.TokenFromCookie)
864		ctx := jwtauth.NewContext(r.Context(), token, err)
865		return r.WithContext(ctx)
866	}
867	return r
868}
869
870func (s *httpdServer) checkConnection(next http.Handler) http.Handler {
871	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
872		ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
873		ip := net.ParseIP(ipAddr)
874		if ip != nil {
875			for _, allow := range s.binding.allowHeadersFrom {
876				if allow(ip) {
877					parsedIP := util.GetRealIP(r)
878					if parsedIP != "" {
879						ipAddr = parsedIP
880						r.RemoteAddr = ipAddr
881					}
882					if forwardedProto := r.Header.Get(xForwardedProto); forwardedProto != "" {
883						ctx := context.WithValue(r.Context(), forwardedProtoKey, forwardedProto)
884						r = r.WithContext(ctx)
885					}
886					break
887				}
888			}
889		}
890
891		common.Connections.AddClientConnection(ipAddr)
892		defer common.Connections.RemoveClientConnection(ipAddr)
893
894		if !common.Connections.IsNewConnectionAllowed(ipAddr) {
895			logger.Log(logger.LevelDebug, common.ProtocolHTTP, "", "connection refused, configured limit reached")
896			s.sendForbiddenResponse(w, r, "configured connections limit reached")
897			return
898		}
899		if common.IsBanned(ipAddr) {
900			s.sendForbiddenResponse(w, r, "your IP address is banned")
901			return
902		}
903		if delay, err := common.LimitRate(common.ProtocolHTTP, ipAddr); err != nil {
904			delay += 499999999 * time.Nanosecond
905			w.Header().Set("Retry-After", fmt.Sprintf("%.0f", delay.Seconds()))
906			w.Header().Set("X-Retry-In", delay.String())
907			s.sendTooManyRequestResponse(w, r, err)
908			return
909		}
910
911		next.ServeHTTP(w, r)
912	})
913}
914
915func (s *httpdServer) sendTooManyRequestResponse(w http.ResponseWriter, r *http.Request, err error) {
916	if (s.enableWebAdmin || s.enableWebClient) && isWebRequest(r) {
917		r = s.updateContextFromCookie(r)
918		if s.enableWebClient && (isWebClientRequest(r) || !s.enableWebAdmin) {
919			renderClientMessagePage(w, r, http.StatusText(http.StatusTooManyRequests), "Rate limit exceeded",
920				http.StatusTooManyRequests, err, "")
921			return
922		}
923		renderMessagePage(w, r, http.StatusText(http.StatusTooManyRequests), "Rate limit exceeded", http.StatusTooManyRequests,
924			err, "")
925		return
926	}
927	sendAPIResponse(w, r, err, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
928}
929
930func (s *httpdServer) sendForbiddenResponse(w http.ResponseWriter, r *http.Request, message string) {
931	if (s.enableWebAdmin || s.enableWebClient) && isWebRequest(r) {
932		r = s.updateContextFromCookie(r)
933		if s.enableWebClient && (isWebClientRequest(r) || !s.enableWebAdmin) {
934			renderClientForbiddenPage(w, r, message)
935			return
936		}
937		renderForbiddenPage(w, r, message)
938		return
939	}
940	sendAPIResponse(w, r, errors.New(message), message, http.StatusForbidden)
941}
942
943func (s *httpdServer) redirectToWebPath(w http.ResponseWriter, r *http.Request, webPath string) {
944	if dataprovider.HasAdmin() {
945		http.Redirect(w, r, webPath, http.StatusMovedPermanently)
946		return
947	}
948	if s.enableWebAdmin {
949		http.Redirect(w, r, webAdminSetupPath, http.StatusFound)
950	}
951}
952
953func (s *httpdServer) isStaticFileURL(r *http.Request) bool {
954	var urlPath string
955	rctx := chi.RouteContext(r.Context())
956	if rctx != nil && rctx.RoutePath != "" {
957		urlPath = rctx.RoutePath
958	} else {
959		urlPath = r.URL.Path
960	}
961	return !strings.HasPrefix(urlPath, webOpenAPIPath) && !strings.HasPrefix(urlPath, webStaticFilesPath)
962}
963
964func (s *httpdServer) initializeRouter() {
965	s.tokenAuth = jwtauth.New(jwa.HS256.String(), getSigningKey(s.signingPassphrase), nil)
966	s.router = chi.NewRouter()
967
968	s.router.Use(middleware.RequestID)
969	s.router.Use(s.checkConnection)
970	s.router.Use(logger.NewStructuredLogger(logger.GetLogger()))
971	s.router.Use(recoverer)
972	if s.cors.Enabled {
973		c := cors.New(cors.Options{
974			AllowedOrigins:   s.cors.AllowedOrigins,
975			AllowedMethods:   s.cors.AllowedMethods,
976			AllowedHeaders:   s.cors.AllowedHeaders,
977			ExposedHeaders:   s.cors.ExposedHeaders,
978			MaxAge:           s.cors.MaxAge,
979			AllowCredentials: s.cors.AllowCredentials,
980		})
981		s.router.Use(c.Handler)
982	}
983	s.router.Use(middleware.GetHead)
984	// StripSlashes causes infinite redirects at the root path if used with http.FileServer
985	s.router.Use(middleware.Maybe(middleware.StripSlashes, s.isStaticFileURL))
986
987	s.router.NotFound(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
988		r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
989		if (s.enableWebAdmin || s.enableWebClient) && isWebRequest(r) {
990			r = s.updateContextFromCookie(r)
991			if s.enableWebClient && (isWebClientRequest(r) || !s.enableWebAdmin) {
992				renderClientNotFoundPage(w, r, nil)
993				return
994			}
995			renderNotFoundPage(w, r, nil)
996			return
997		}
998		sendAPIResponse(w, r, nil, http.StatusText(http.StatusNotFound), http.StatusNotFound)
999	}))
1000
1001	s.router.Get(healthzPath, func(w http.ResponseWriter, r *http.Request) {
1002		render.PlainText(w, r, "ok")
1003	})
1004
1005	// share API exposed to external users
1006	s.router.Get(sharesPath+"/{id}", downloadFromShare)
1007	s.router.Post(sharesPath+"/{id}", uploadToShare)
1008
1009	s.router.Get(tokenPath, s.getToken)
1010	s.router.Post(adminPath+"/{username}/forgot-password", forgotAdminPassword)
1011	s.router.Post(adminPath+"/{username}/reset-password", resetAdminPassword)
1012	s.router.Post(userPath+"/{username}/forgot-password", forgotUserPassword)
1013	s.router.Post(userPath+"/{username}/reset-password", resetUserPassword)
1014
1015	s.router.Group(func(router chi.Router) {
1016		router.Use(checkAPIKeyAuth(s.tokenAuth, dataprovider.APIKeyScopeAdmin))
1017		router.Use(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromHeader))
1018		router.Use(jwtAuthenticatorAPI)
1019
1020		router.Get(versionPath, func(w http.ResponseWriter, r *http.Request) {
1021			r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1022			render.JSON(w, r, version.Get())
1023		})
1024
1025		router.With(forbidAPIKeyAuthentication).Get(logoutPath, s.logout)
1026		router.With(forbidAPIKeyAuthentication).Get(adminProfilePath, getAdminProfile)
1027		router.With(forbidAPIKeyAuthentication).Put(adminProfilePath, updateAdminProfile)
1028		router.With(forbidAPIKeyAuthentication).Put(adminPwdPath, changeAdminPassword)
1029		// compatibility layer to remove in v2.2
1030		router.With(forbidAPIKeyAuthentication).Put(adminPwdCompatPath, changeAdminPassword)
1031		// admin TOTP APIs
1032		router.With(forbidAPIKeyAuthentication).Get(adminTOTPConfigsPath, getTOTPConfigs)
1033		router.With(forbidAPIKeyAuthentication).Post(adminTOTPGeneratePath, generateTOTPSecret)
1034		router.With(forbidAPIKeyAuthentication).Post(adminTOTPValidatePath, validateTOTPPasscode)
1035		router.With(forbidAPIKeyAuthentication).Post(adminTOTPSavePath, saveTOTPConfig)
1036		router.With(forbidAPIKeyAuthentication).Get(admin2FARecoveryCodesPath, getRecoveryCodes)
1037		router.With(forbidAPIKeyAuthentication).Post(admin2FARecoveryCodesPath, generateRecoveryCodes)
1038
1039		router.With(checkPerm(dataprovider.PermAdminViewServerStatus)).
1040			Get(serverStatusPath, func(w http.ResponseWriter, r *http.Request) {
1041				r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1042				render.JSON(w, r, getServicesStatus())
1043			})
1044
1045		router.With(checkPerm(dataprovider.PermAdminViewConnections)).
1046			Get(activeConnectionsPath, func(w http.ResponseWriter, r *http.Request) {
1047				r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1048				render.JSON(w, r, common.Connections.GetStats())
1049			})
1050
1051		router.With(checkPerm(dataprovider.PermAdminCloseConnections)).
1052			Delete(activeConnectionsPath+"/{connectionID}", handleCloseConnection)
1053		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotaScanPath, getUsersQuotaScans)
1054		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/users/scans", getUsersQuotaScans)
1055		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotaScanPath, startUserQuotaScanCompat)
1056		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/users/{username}/scan", startUserQuotaScan)
1057		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotaScanVFolderPath, getFoldersQuotaScans)
1058		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/folders/scans", getFoldersQuotaScans)
1059		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotaScanVFolderPath, startFolderQuotaScanCompat)
1060		router.With(checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/folders/{name}/scan", startFolderQuotaScan)
1061		router.With(checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath, getUsers)
1062		router.With(checkPerm(dataprovider.PermAdminAddUsers)).Post(userPath, addUser)
1063		router.With(checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath+"/{username}", getUserByUsername)
1064		router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Put(userPath+"/{username}", updateUser)
1065		router.With(checkPerm(dataprovider.PermAdminDeleteUsers)).Delete(userPath+"/{username}", deleteUser)
1066		router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Put(userPath+"/{username}/2fa/disable", disableUser2FA)
1067		router.With(checkPerm(dataprovider.PermAdminViewUsers)).Get(folderPath, getFolders)
1068		router.With(checkPerm(dataprovider.PermAdminViewUsers)).Get(folderPath+"/{name}", getFolderByName)
1069		router.With(checkPerm(dataprovider.PermAdminAddUsers)).Post(folderPath, addFolder)
1070		router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Put(folderPath+"/{name}", updateFolder)
1071		router.With(checkPerm(dataprovider.PermAdminDeleteUsers)).Delete(folderPath+"/{name}", deleteFolder)
1072		router.With(checkPerm(dataprovider.PermAdminManageSystem)).Get(dumpDataPath, dumpData)
1073		router.With(checkPerm(dataprovider.PermAdminManageSystem)).Get(loadDataPath, loadData)
1074		router.With(checkPerm(dataprovider.PermAdminManageSystem)).Post(loadDataPath, loadDataFromRequest)
1075		router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Put(updateUsedQuotaPath, updateUserQuotaUsageCompat)
1076		router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/users/{username}/usage", updateUserQuotaUsage)
1077		router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Put(updateFolderUsedQuotaPath, updateFolderQuotaUsageCompat)
1078		router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/folders/{name}/usage", updateFolderQuotaUsage)
1079		router.With(checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts, getDefenderHosts)
1080		router.With(checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts+"/{id}", getDefenderHostByID)
1081		router.With(checkPerm(dataprovider.PermAdminManageDefender)).Delete(defenderHosts+"/{id}", deleteDefenderHostByID)
1082		router.With(checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderBanTime, getBanTime)
1083		router.With(checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderScore, getScore)
1084		router.With(checkPerm(dataprovider.PermAdminManageDefender)).Post(defenderUnban, unban)
1085		router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath, getAdmins)
1086		router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Post(adminPath, addAdmin)
1087		router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath+"/{username}", getAdminByUsername)
1088		router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Put(adminPath+"/{username}", updateAdmin)
1089		router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Delete(adminPath+"/{username}", deleteAdmin)
1090		router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Put(adminPath+"/{username}/2fa/disable", disableAdmin2FA)
1091		router.With(checkPerm(dataprovider.PermAdminRetentionChecks)).Get(retentionChecksPath, getRetentionChecks)
1092		router.With(checkPerm(dataprovider.PermAdminRetentionChecks)).Post(retentionBasePath+"/{username}/check",
1093			startRetentionCheck)
1094		router.With(checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
1095			Get(fsEventsPath, searchFsEvents)
1096		router.With(checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
1097			Get(providerEventsPath, searchProviderEvents)
1098		router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
1099			Get(apiKeysPath, getAPIKeys)
1100		router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
1101			Post(apiKeysPath, addAPIKey)
1102		router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
1103			Get(apiKeysPath+"/{id}", getAPIKeyByID)
1104		router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
1105			Put(apiKeysPath+"/{id}", updateAPIKey)
1106		router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
1107			Delete(apiKeysPath+"/{id}", deleteAPIKey)
1108	})
1109
1110	s.router.Get(userTokenPath, s.getUserToken)
1111
1112	s.router.Group(func(router chi.Router) {
1113		router.Use(checkAPIKeyAuth(s.tokenAuth, dataprovider.APIKeyScopeUser))
1114		router.Use(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromHeader))
1115		router.Use(jwtAuthenticatorAPIUser)
1116
1117		router.With(forbidAPIKeyAuthentication).Get(userLogoutPath, s.logout)
1118		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
1119			Put(userPwdPath, changeUserPassword)
1120		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
1121			Get(userPublicKeysPath, getUserPublicKeys)
1122		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
1123			Put(userPublicKeysPath, setUserPublicKeys)
1124		router.With(forbidAPIKeyAuthentication).Get(userProfilePath, getUserProfile)
1125		router.With(forbidAPIKeyAuthentication).Put(userProfilePath, updateUserProfile)
1126		// user TOTP APIs
1127		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientMFADisabled)).
1128			Get(userTOTPConfigsPath, getTOTPConfigs)
1129		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientMFADisabled)).
1130			Post(userTOTPGeneratePath, generateTOTPSecret)
1131		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientMFADisabled)).
1132			Post(userTOTPValidatePath, validateTOTPPasscode)
1133		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientMFADisabled)).
1134			Post(userTOTPSavePath, saveTOTPConfig)
1135		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientMFADisabled)).
1136			Get(user2FARecoveryCodesPath, getRecoveryCodes)
1137		router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientMFADisabled)).
1138			Post(user2FARecoveryCodesPath, generateRecoveryCodes)
1139
1140		// compatibility layer to remove in v2.3
1141		router.With(compressor.Handler).Get(userFolderPath, readUserFolder)
1142		router.Get(userFilePath, getUserFile)
1143
1144		router.With(compressor.Handler).Get(userDirsPath, readUserFolder)
1145		router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled)).Post(userDirsPath, createUserDir)
1146		router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled)).Patch(userDirsPath, renameUserDir)
1147		router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled)).Delete(userDirsPath, deleteUserDir)
1148		router.Get(userFilesPath, getUserFile)
1149		router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled)).Post(userFilesPath, uploadUserFiles)
1150		router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled)).Patch(userFilesPath, renameUserFile)
1151		router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled)).Delete(userFilesPath, deleteUserFile)
1152		router.Post(userStreamZipPath, getUserFilesAsZipStream)
1153		router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).Get(userSharesPath, getShares)
1154		router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).Post(userSharesPath, addShare)
1155		router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).Get(userSharesPath+"/{id}", getShareByID)
1156		router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).Put(userSharesPath+"/{id}", updateShare)
1157		router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).Delete(userSharesPath+"/{id}", deleteShare)
1158	})
1159
1160	if s.renderOpenAPI {
1161		s.router.Group(func(router chi.Router) {
1162			router.Use(compressor.Handler)
1163			fileServer(router, webOpenAPIPath, http.Dir(s.openAPIPath))
1164		})
1165	}
1166
1167	if s.enableWebAdmin || s.enableWebClient {
1168		s.router.Group(func(router chi.Router) {
1169			router.Use(compressor.Handler)
1170			fileServer(router, webStaticFilesPath, http.Dir(s.staticFilesPath))
1171		})
1172		if s.enableWebClient {
1173			s.router.Get(webRootPath, func(w http.ResponseWriter, r *http.Request) {
1174				r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1175				s.redirectToWebPath(w, r, webClientLoginPath)
1176			})
1177			s.router.Get(webBasePath, func(w http.ResponseWriter, r *http.Request) {
1178				r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1179				s.redirectToWebPath(w, r, webClientLoginPath)
1180			})
1181		} else {
1182			s.router.Get(webRootPath, func(w http.ResponseWriter, r *http.Request) {
1183				r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1184				s.redirectToWebPath(w, r, webLoginPath)
1185			})
1186			s.router.Get(webBasePath, func(w http.ResponseWriter, r *http.Request) {
1187				r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1188				s.redirectToWebPath(w, r, webLoginPath)
1189			})
1190		}
1191	}
1192
1193	if s.enableWebClient {
1194		s.router.Get(webBaseClientPath, func(w http.ResponseWriter, r *http.Request) {
1195			r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
1196			http.Redirect(w, r, webClientLoginPath, http.StatusMovedPermanently)
1197		})
1198		s.router.Get(webClientLoginPath, s.handleClientWebLogin)
1199		s.router.Post(webClientLoginPath, s.handleWebClientLoginPost)
1200		s.router.Get(webClientForgotPwdPath, handleWebClientForgotPwd)
1201		s.router.Post(webClientForgotPwdPath, handleWebClientForgotPwdPost)
1202		s.router.Get(webClientResetPwdPath, handleWebClientPasswordReset)
1203		s.router.Post(webClientResetPwdPath, s.handleWebClientPasswordResetPost)
1204		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1205			jwtAuthenticatorPartial(tokenAudienceWebClientPartial)).
1206			Get(webClientTwoFactorPath, handleWebClientTwoFactor)
1207		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1208			jwtAuthenticatorPartial(tokenAudienceWebClientPartial)).
1209			Post(webClientTwoFactorPath, s.handleWebClientTwoFactorPost)
1210		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1211			jwtAuthenticatorPartial(tokenAudienceWebClientPartial)).
1212			Get(webClientTwoFactorRecoveryPath, handleWebClientTwoFactorRecovery)
1213		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1214			jwtAuthenticatorPartial(tokenAudienceWebClientPartial)).
1215			Post(webClientTwoFactorRecoveryPath, s.handleWebClientTwoFactorRecoveryPost)
1216		// share API exposed to external users
1217		s.router.Get(webClientPubSharesPath+"/{id}", downloadFromShare)
1218		s.router.Post(webClientPubSharesPath+"/{id}", uploadToShare)
1219
1220		s.router.Group(func(router chi.Router) {
1221			router.Use(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie))
1222			router.Use(jwtAuthenticatorWebClient)
1223
1224			router.Get(webClientLogoutPath, handleWebClientLogout)
1225			router.With(s.refreshCookie).Get(webClientFilesPath, handleClientGetFiles)
1226			router.With(s.refreshCookie).Get(webClientViewPDFPath, handleClientViewPDF)
1227			router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
1228				Post(webClientFilesPath, uploadUserFiles)
1229			router.With(s.refreshCookie).Get(webClientEditFilePath, handleClientEditFile)
1230			router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
1231				Patch(webClientFilesPath, renameUserFile)
1232			router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
1233				Delete(webClientFilesPath, deleteUserFile)
1234			router.With(compressor.Handler, s.refreshCookie).Get(webClientDirsPath, handleClientGetDirContents)
1235			router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
1236				Post(webClientDirsPath, createUserDir)
1237			router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
1238				Patch(webClientDirsPath, renameUserDir)
1239			router.With(checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
1240				Delete(webClientDirsPath, deleteUserDir)
1241			router.With(s.refreshCookie).Get(webClientDownloadZipPath, handleWebClientDownloadZip)
1242			router.With(s.refreshCookie).Get(webClientProfilePath, handleClientGetProfile)
1243			router.Post(webClientProfilePath, handleWebClientProfilePost)
1244			router.With(checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
1245				Get(webChangeClientPwdPath, handleWebClientChangePwd)
1246			router.With(checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
1247				Post(webChangeClientPwdPath, handleWebClientChangePwdPost)
1248			router.With(checkHTTPUserPerm(sdk.WebClientMFADisabled), s.refreshCookie).
1249				Get(webClientMFAPath, handleWebClientMFA)
1250			router.With(checkHTTPUserPerm(sdk.WebClientMFADisabled), verifyCSRFHeader).
1251				Post(webClientTOTPGeneratePath, generateTOTPSecret)
1252			router.With(checkHTTPUserPerm(sdk.WebClientMFADisabled), verifyCSRFHeader).
1253				Post(webClientTOTPValidatePath, validateTOTPPasscode)
1254			router.With(checkHTTPUserPerm(sdk.WebClientMFADisabled), verifyCSRFHeader).
1255				Post(webClientTOTPSavePath, saveTOTPConfig)
1256			router.With(checkHTTPUserPerm(sdk.WebClientMFADisabled), verifyCSRFHeader, s.refreshCookie).
1257				Get(webClientRecoveryCodesPath, getRecoveryCodes)
1258			router.With(checkHTTPUserPerm(sdk.WebClientMFADisabled), verifyCSRFHeader).
1259				Post(webClientRecoveryCodesPath, generateRecoveryCodes)
1260			router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled), s.refreshCookie).
1261				Get(webClientSharesPath, handleClientGetShares)
1262			router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled), s.refreshCookie).
1263				Get(webClientSharePath, handleClientAddShareGet)
1264			router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).Post(webClientSharePath,
1265				handleClientAddSharePost)
1266			router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled), s.refreshCookie).
1267				Get(webClientSharePath+"/{id}", handleClientUpdateShareGet)
1268			router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled)).
1269				Post(webClientSharePath+"/{id}", handleClientUpdateSharePost)
1270			router.With(checkHTTPUserPerm(sdk.WebClientSharesDisabled), verifyCSRFHeader).
1271				Delete(webClientSharePath+"/{id}", deleteShare)
1272		})
1273	}
1274
1275	if s.enableWebAdmin {
1276		s.router.Get(webBaseAdminPath, func(w http.ResponseWriter, r *http.Request) {
1277			r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
1278			s.redirectToWebPath(w, r, webLoginPath)
1279		})
1280		s.router.Get(webLoginPath, s.handleWebAdminLogin)
1281		s.router.Post(webLoginPath, s.handleWebAdminLoginPost)
1282		s.router.Get(webAdminSetupPath, handleWebAdminSetupGet)
1283		s.router.Post(webAdminSetupPath, s.handleWebAdminSetupPost)
1284		s.router.Get(webAdminForgotPwdPath, handleWebAdminForgotPwd)
1285		s.router.Post(webAdminForgotPwdPath, handleWebAdminForgotPwdPost)
1286		s.router.Get(webAdminResetPwdPath, handleWebAdminPasswordReset)
1287		s.router.Post(webAdminResetPwdPath, s.handleWebAdminPasswordResetPost)
1288		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1289			jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
1290			Get(webAdminTwoFactorPath, handleWebAdminTwoFactor)
1291		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1292			jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
1293			Post(webAdminTwoFactorPath, s.handleWebAdminTwoFactorPost)
1294		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1295			jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
1296			Get(webAdminTwoFactorRecoveryPath, handleWebAdminTwoFactorRecovery)
1297		s.router.With(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie),
1298			jwtAuthenticatorPartial(tokenAudienceWebAdminPartial)).
1299			Post(webAdminTwoFactorRecoveryPath, s.handleWebAdminTwoFactorRecoveryPost)
1300
1301		s.router.Group(func(router chi.Router) {
1302			router.Use(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromCookie))
1303			router.Use(jwtAuthenticatorWebAdmin)
1304
1305			router.Get(webLogoutPath, handleWebLogout)
1306			router.With(s.refreshCookie).Get(webAdminProfilePath, handleWebAdminProfile)
1307			router.Post(webAdminProfilePath, handleWebAdminProfilePost)
1308			router.With(s.refreshCookie).Get(webChangeAdminPwdPath, handleWebAdminChangePwd)
1309			router.Post(webChangeAdminPwdPath, handleWebAdminChangePwdPost)
1310
1311			router.With(s.refreshCookie).Get(webAdminMFAPath, handleWebAdminMFA)
1312			router.With(verifyCSRFHeader).Post(webAdminTOTPGeneratePath, generateTOTPSecret)
1313			router.With(verifyCSRFHeader).Post(webAdminTOTPValidatePath, validateTOTPPasscode)
1314			router.With(verifyCSRFHeader).Post(webAdminTOTPSavePath, saveTOTPConfig)
1315			router.With(verifyCSRFHeader, s.refreshCookie).Get(webAdminRecoveryCodesPath, getRecoveryCodes)
1316			router.With(verifyCSRFHeader).Post(webAdminRecoveryCodesPath, generateRecoveryCodes)
1317
1318			router.With(checkPerm(dataprovider.PermAdminViewUsers), s.refreshCookie).
1319				Get(webUsersPath, handleGetWebUsers)
1320			router.With(checkPerm(dataprovider.PermAdminAddUsers), s.refreshCookie).
1321				Get(webUserPath, handleWebAddUserGet)
1322			router.With(checkPerm(dataprovider.PermAdminChangeUsers), s.refreshCookie).
1323				Get(webUserPath+"/{username}", handleWebUpdateUserGet)
1324			router.With(checkPerm(dataprovider.PermAdminAddUsers)).Post(webUserPath, handleWebAddUserPost)
1325			router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Post(webUserPath+"/{username}", handleWebUpdateUserPost)
1326			router.With(checkPerm(dataprovider.PermAdminViewConnections), s.refreshCookie).
1327				Get(webConnectionsPath, handleWebGetConnections)
1328			router.With(checkPerm(dataprovider.PermAdminViewUsers), s.refreshCookie).
1329				Get(webFoldersPath, handleWebGetFolders)
1330			router.With(checkPerm(dataprovider.PermAdminAddUsers), s.refreshCookie).
1331				Get(webFolderPath, handleWebAddFolderGet)
1332			router.With(checkPerm(dataprovider.PermAdminAddUsers)).Post(webFolderPath, handleWebAddFolderPost)
1333			router.With(checkPerm(dataprovider.PermAdminViewServerStatus), s.refreshCookie).
1334				Get(webStatusPath, handleWebGetStatus)
1335			router.With(checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
1336				Get(webAdminsPath, handleGetWebAdmins)
1337			router.With(checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
1338				Get(webAdminPath, handleWebAddAdminGet)
1339			router.With(checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
1340				Get(webAdminPath+"/{username}", handleWebUpdateAdminGet)
1341			router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Post(webAdminPath, handleWebAddAdminPost)
1342			router.With(checkPerm(dataprovider.PermAdminManageAdmins)).Post(webAdminPath+"/{username}",
1343				handleWebUpdateAdminPost)
1344			router.With(checkPerm(dataprovider.PermAdminManageAdmins), verifyCSRFHeader).
1345				Delete(webAdminPath+"/{username}", deleteAdmin)
1346			router.With(checkPerm(dataprovider.PermAdminCloseConnections), verifyCSRFHeader).
1347				Delete(webConnectionsPath+"/{connectionID}", handleCloseConnection)
1348			router.With(checkPerm(dataprovider.PermAdminChangeUsers), s.refreshCookie).
1349				Get(webFolderPath+"/{name}", handleWebUpdateFolderGet)
1350			router.With(checkPerm(dataprovider.PermAdminChangeUsers)).Post(webFolderPath+"/{name}",
1351				handleWebUpdateFolderPost)
1352			router.With(checkPerm(dataprovider.PermAdminDeleteUsers), verifyCSRFHeader).
1353				Delete(webFolderPath+"/{name}", deleteFolder)
1354			router.With(checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader).
1355				Post(webScanVFolderPath+"/{name}", startFolderQuotaScan)
1356			router.With(checkPerm(dataprovider.PermAdminDeleteUsers), verifyCSRFHeader).
1357				Delete(webUserPath+"/{username}", deleteUser)
1358			router.With(checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader).
1359				Post(webQuotaScanPath+"/{username}", startUserQuotaScan)
1360			router.With(checkPerm(dataprovider.PermAdminManageSystem)).Get(webMaintenancePath, handleWebMaintenance)
1361			router.With(checkPerm(dataprovider.PermAdminManageSystem)).Get(webBackupPath, dumpData)
1362			router.With(checkPerm(dataprovider.PermAdminManageSystem)).Post(webRestorePath, handleWebRestore)
1363			router.With(checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).
1364				Get(webTemplateUser, handleWebTemplateUserGet)
1365			router.With(checkPerm(dataprovider.PermAdminManageSystem)).Post(webTemplateUser, handleWebTemplateUserPost)
1366			router.With(checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).
1367				Get(webTemplateFolder, handleWebTemplateFolderGet)
1368			router.With(checkPerm(dataprovider.PermAdminManageSystem)).Post(webTemplateFolder, handleWebTemplateFolderPost)
1369			router.With(checkPerm(dataprovider.PermAdminViewDefender)).Get(webDefenderPath, handleWebDefenderPage)
1370			router.With(checkPerm(dataprovider.PermAdminViewDefender)).Get(webDefenderHostsPath, getDefenderHosts)
1371			router.With(checkPerm(dataprovider.PermAdminManageDefender)).Delete(webDefenderHostsPath+"/{id}",
1372				deleteDefenderHostByID)
1373		})
1374	}
1375}
1376