diff --git a/filemanager.go b/filemanager.go index b5826498..e3ac8d32 100644 --- a/filemanager.go +++ b/filemanager.go @@ -39,128 +39,139 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err ) for i := range f.Configs { - if httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) { - c = &f.Configs[i] + // Checks if this Path should be handled by File Manager. + if !httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) { + return f.Next.ServeHTTP(w, r) + } - if r.Method == http.MethodGet && httpserver.Path(r.URL.Path).Matches(c.BaseURL+assets.BaseURL) { + c = &f.Configs[i] + + // Checks if the URL matches the Assets URL. Returns the asset if the + // method is GET and Status Forbidden otherwise. + if httpserver.Path(r.URL.Path).Matches(c.BaseURL + assets.BaseURL) { + if r.Method == http.MethodGet { return assets.Serve(w, r, c) } - username, _, _ := r.BasicAuth() + return http.StatusForbidden, nil + } - if _, ok := c.Users[username]; ok { - user = c.Users[username] - } else { - user = c.User - } - - if strings.HasPrefix(r.URL.Path, c.WebDavURL) { - if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.WebDavURL)) { - return http.StatusForbidden, nil - } - - switch r.Method { - case "PROPPATCH", "MOVE", "PATCH", "PUT", "DELETE": - if !user.AllowEdit { - return http.StatusForbidden, nil - } - case "MKCOL", "COPY": - if !user.AllowNew { - return http.StatusForbidden, nil - } - } - - if r.Method == http.MethodPut { - _, err = processPUT(w, r, c, user, fi) - if err != nil { - return http.StatusInternalServerError, err - } - } - - c.Handler.ServeHTTP(w, r) - return 0, nil - } - - if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.BaseURL)) { - if r.Method == http.MethodGet { - return page.PrintErrorHTML( - w, - http.StatusForbidden, - e.New("You don't have permission to access this page."), - ) - } + // Obtains the user + username, _, _ := r.BasicAuth() + if _, ok := c.Users[username]; ok { + user = c.Users[username] + } else { + user = c.User + } + // Checks if the request URL is for the WebDav server + if strings.HasPrefix(r.URL.Path, c.WebDavURL) { + // Checks for user permissions relatively to this PATH + if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.WebDavURL)) { return http.StatusForbidden, nil } + switch r.Method { + case "PROPPATCH", "MOVE", "PATCH", "PUT", "DELETE": + if !user.AllowEdit { + return http.StatusForbidden, nil + } + case "MKCOL", "COPY": + if !user.AllowNew { + return http.StatusForbidden, nil + } + } + + // Preprocess the PUT request if it's the case + if r.Method == http.MethodPut { + _, err = processPUT(w, r, c, user, fi) + if err != nil { + return http.StatusInternalServerError, err + } + } + + c.Handler.ServeHTTP(w, r) + return 0, nil + } + + // Checks if the User is allowed to access this file + if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.BaseURL)) { if r.Method == http.MethodGet { - // Gets the information of the directory/file - fi, code, err = file.GetInfo(r.URL, c, user) - if err != nil { - if r.Method == http.MethodGet { - return page.PrintErrorHTML(w, code, err) - } - return code, err - } + return page.PrintErrorHTML( + w, http.StatusForbidden, + e.New("You don't have permission to access this page."), + ) + } - // If it's a dir and the path doesn't end with a trailing slash, - // redirect the user. - if fi.IsDir() && !strings.HasSuffix(r.URL.Path, "/") { - http.Redirect(w, r, c.AddrPath+r.URL.Path+"/", http.StatusTemporaryRedirect) - return 0, nil - } + return http.StatusForbidden, nil + } - // Generate anti security token. - c.GenerateToken() - - if !fi.IsDir() { - query := r.URL.Query() - if val, ok := query["raw"]; ok && val[0] == "true" { - r.URL.Path = strings.Replace(r.URL.Path, c.BaseURL, c.WebDavURL, 1) - c.Handler.ServeHTTP(w, r) - return 0, nil - } - - if val, ok := query["download"]; ok && val[0] == "true" { - w.Header().Set("Content-Disposition", "attachment; filename="+fi.Name()) - r.URL.Path = strings.Replace(r.URL.Path, c.BaseURL, c.WebDavURL, 1) - c.Handler.ServeHTTP(w, r) - return 0, nil - } - } - - code, err := fi.ServeHTTP(w, r, c, user) - if err != nil { + if r.Method == http.MethodGet { + // Gets the information of the directory/file + fi, code, err = file.GetInfo(r.URL, c, user) + if err != nil { + if r.Method == http.MethodGet { return page.PrintErrorHTML(w, code, err) } return code, err } - if r.Method == http.MethodPost { - // TODO: How to exclude web dav clients? :/ - // Security measures against CSRF attacks. - if !c.CheckToken(r) { - return http.StatusForbidden, nil + // If it's a dir and the path doesn't end with a trailing slash, + // redirect the user. + if fi.IsDir() && !strings.HasSuffix(r.URL.Path, "/") { + http.Redirect(w, r, c.AddrPath+r.URL.Path+"/", http.StatusTemporaryRedirect) + return 0, nil + } + + // Generate anti security token. + c.GenerateToken() + + if !fi.IsDir() { + query := r.URL.Query() + webdav := false + + if val, ok := query["raw"]; ok && val[0] == "true" { + webdav = true } - /* TODO: search commands. USE PROPFIND? - // Search and git commands. - if r.Header.Get("Search") == "true" { + if val, ok := query["download"]; ok && val[0] == "true" { + w.Header().Set("Content-Disposition", "attachment; filename="+fi.Name()) + webdav = true + } - } */ - - // VCS commands. - if r.Header.Get("Command") != "" { - if !user.AllowCommands { - return http.StatusUnauthorized, nil - } - - return command(w, r, c, user) + if webdav { + r.URL.Path = strings.Replace(r.URL.Path, c.BaseURL, c.WebDavURL, 1) + c.Handler.ServeHTTP(w, r) + return 0, nil } } - return http.StatusNotImplemented, nil + code, err := fi.ServeHTTP(w, r, c, user) + if err != nil { + return page.PrintErrorHTML(w, code, err) + } + return code, err } + + if r.Method == http.MethodPost { + // TODO: This anti CSCF measure is not being applied to requests + // to the WebDav URL namespace. Anyone has ideas? + if !c.CheckToken(r) { + return http.StatusForbidden, nil + } + + // VCS commands. + if r.Header.Get("Command") != "" { + if !user.AllowCommands { + return http.StatusUnauthorized, nil + } + + return command(w, r, c, user) + } + } + + return http.StatusNotImplemented, nil + } return f.Next.ServeHTTP(w, r)