Skip to content

Commit

Permalink
fix: refactoring previous fix in attempt to address ongoing issues
Browse files Browse the repository at this point in the history
See #211 and #215
  • Loading branch information
hacdias committed Jan 15, 2025
1 parent 59c5f13 commit 6aeb3f8
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 24 deletions.
20 changes: 7 additions & 13 deletions lib/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package lib

import (
"net/http"
"net/url"
"os"
"strings"

Expand Down Expand Up @@ -116,21 +115,16 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
zap.L().Info("user authorized", zap.String("username", username), zap.String("remote_address", remoteAddr))
}

// Cleanup destination header if it's present by stripping out the prefix
// and only keeping the path.
if destination := r.Header.Get("Destination"); destination != "" {
u, err := url.Parse(destination)
if err == nil {
destination = strings.TrimPrefix(u.Path, user.Prefix)
if !strings.HasPrefix(destination, "/") {
destination = "/" + destination
}
r.Header.Set("Destination", destination)
}
// Convert the HTTP request into an internal request type
req, err := newRequest(r, h.user.Prefix)
if err != nil {
zap.L().Info("invalid request path or destination", zap.Error(err))
http.Error(w, "Invalid request path or destination", http.StatusBadRequest)
return
}

// Checks for user permissions relatively to this PATH.
allowed := user.Allowed(r, func(filename string) bool {
allowed := user.Allowed(req, func(filename string) bool {
_, err := user.FileSystem.Stat(r.Context(), filename)
return !os.IsNotExist(err)
})
Expand Down
21 changes: 10 additions & 11 deletions lib/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package lib
import (
"errors"
"fmt"
"net/http"
"path/filepath"
"regexp"
"strings"
Expand Down Expand Up @@ -51,12 +50,12 @@ type UserPermissions struct {
}

// Allowed checks if the user has permission to access a directory/file
func (p UserPermissions) Allowed(r *http.Request, fileExists func(string) bool) bool {
func (p UserPermissions) Allowed(r *request, fileExists func(string) bool) bool {
// For COPY and MOVE requests, we first check the permissions for the destination
// path. As soon as a rule matches and does not allow the operation at the destination,
// we fail immediately. If no rule matches, we check the global permissions.
if r.Method == "COPY" || r.Method == "MOVE" {
dst := r.Header.Get("Destination")
if r.method == "COPY" || r.method == "MOVE" {
dst := r.destination

for i := len(p.Rules) - 1; i >= 0; i-- {
if p.Rules[i].Matches(dst) {
Expand All @@ -77,7 +76,7 @@ func (p UserPermissions) Allowed(r *http.Request, fileExists func(string) bool)
// Go through rules beginning from the last one, and check the permissions at
// the source. The first matched rule returns.
for i := len(p.Rules) - 1; i >= 0; i-- {
if p.Rules[i].Matches(r.URL.Path) {
if p.Rules[i].Matches(r.path) {
return p.Rules[i].Permissions.Allowed(r, fileExists)
}
}
Expand Down Expand Up @@ -142,8 +141,8 @@ func (p *Permissions) UnmarshalText(data []byte) error {

// Allowed returns whether this permission set has permissions to execute this
// request in the source directory. This applies to all requests with all methods.
func (p Permissions) Allowed(r *http.Request, fileExists func(string) bool) bool {
switch r.Method {
func (p Permissions) Allowed(r *request, fileExists func(string) bool) bool {
switch r.method {
case "GET", "HEAD", "OPTIONS", "POST", "PROPFIND":
// Note: POST backend implementation just returns the same thing as GET.
return p.Read
Expand All @@ -152,7 +151,7 @@ func (p Permissions) Allowed(r *http.Request, fileExists func(string) bool) bool
case "PROPPATCH":
return p.Update
case "PUT":
if fileExists(r.URL.Path) {
if fileExists(r.path) {
return p.Update
} else {
return p.Create
Expand All @@ -172,10 +171,10 @@ func (p Permissions) Allowed(r *http.Request, fileExists func(string) bool) bool

// AllowedDestination returns whether this permissions set has permissions to execute this
// request in the destination directory. This only applies for COPY and MOVE requests.
func (p Permissions) AllowedDestination(r *http.Request, fileExists func(string) bool) bool {
switch r.Method {
func (p Permissions) AllowedDestination(r *request, fileExists func(string) bool) bool {
switch r.method {
case "COPY", "MOVE":
if fileExists(r.Header.Get("Destination")) {
if fileExists(r.destination) {
return p.Update
} else {
return p.Create
Expand Down
57 changes: 57 additions & 0 deletions lib/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package lib

import (
"errors"
"net/http"
"net/url"
"strings"
)

type request struct {
method string
path string
destination string
}

func newRequest(r *http.Request, prefix string) (*request, error) {
ctx := &request{
method: r.Method,
}

if destination := r.Header.Get("Destination"); destination != "" {
u, err := url.Parse(destination)
if err != nil {
return nil, errors.New("invalid destination header")
}

if prefix != "" {
destination = strings.TrimPrefix(u.Path, prefix)
if len(destination) >= len(u.Path) {
return nil, errors.New("invalid url prefix")
}
}

if !strings.HasPrefix(destination, "/") {
destination = "/" + destination
}

ctx.destination = destination
}

path := r.URL.Path

if prefix != "" {
path = strings.TrimPrefix(r.URL.Path, prefix)
if len(path) >= len(r.URL.Path) {
return nil, errors.New("invalid url prefix")
}
}

if !strings.HasPrefix(path, "/") {
path = "/" + path
}

ctx.path = path

return ctx, nil
}

0 comments on commit 6aeb3f8

Please sign in to comment.