diff --git a/dataprovider/dataprovider.go b/dataprovider/dataprovider.go index fc2f7282..c7f17019 100644 --- a/dataprovider/dataprovider.go +++ b/dataprovider/dataprovider.go @@ -505,7 +505,7 @@ func validatePermissions(user *User) error { return &ValidationError{err: fmt.Sprintf("permissions for the root dir \"/\" must be set")} } for dir, perms := range user.Permissions { - if len(perms) == 0 { + if len(perms) == 0 && dir == "/" { return &ValidationError{err: fmt.Sprintf("no permissions granted for the directory: %#v", dir)} } for _, p := range perms { @@ -520,6 +520,9 @@ func validatePermissions(user *User) error { if !path.IsAbs(cleanedDir) { return &ValidationError{err: fmt.Sprintf("cannot set permissions for non absolute path: %#v", dir)} } + if dir != cleanedDir && cleanedDir == "/" { + return &ValidationError{err: fmt.Sprintf("cannot set permissions for invalid subdirectory: %#v is an alias for \"/\"", dir)} + } if utils.IsStringInSlice(PermAny, perms) { permissions[cleanedDir] = []string{PermAny} } else { diff --git a/httpd/httpd_test.go b/httpd/httpd_test.go index 4eb30264..d42c25f3 100644 --- a/httpd/httpd_test.go +++ b/httpd/httpd_test.go @@ -285,11 +285,18 @@ func TestAddUserInvalidPerms(t *testing.T) { t.Errorf("unexpected error adding user with invalid perms: %v", err) } // permissions for root dir are mandatory + u.Permissions["/"] = []string{} u.Permissions["/somedir"] = []string{dataprovider.PermAny} _, _, err = httpd.AddUser(u, http.StatusBadRequest) if err != nil { t.Errorf("unexpected error adding user with no root dir perms: %v", err) } + u.Permissions["/"] = []string{dataprovider.PermAny} + u.Permissions["/subdir/.."] = []string{dataprovider.PermAny} + _, _, err = httpd.AddUser(u, http.StatusBadRequest) + if err != nil { + t.Errorf("unexpected error adding user with invalid dir perms: %v", err) + } } func TestAddUserInvalidFilters(t *testing.T) { @@ -407,6 +414,14 @@ func TestUpdateUser(t *testing.T) { if err != nil { t.Errorf("unable to update user: %v", err) } + user.Permissions["/subdir"] = []string{} + user, _, err = httpd.UpdateUser(user, http.StatusOK) + if err != nil { + t.Errorf("unable to update user: %v", err) + } + if len(user.Permissions["/subdir"]) > 0 { + t.Errorf("unexpected subdir permissions, must be empty") + } _, err = httpd.RemoveUser(user, http.StatusOK) if err != nil { t.Errorf("unable to remove: %v", err) @@ -1136,12 +1151,18 @@ func TestUserPermissionsMock(t *testing.T) { if err != nil { t.Errorf("Error get user: %v", err) } - user.Permissions["/somedir"] = []string{} + user.Permissions["/somedir"] = []string{"invalid"} userAsJSON = getUserAsJSON(t, user) req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON)) rr = executeRequest(req) checkResponseCode(t, http.StatusBadRequest, rr.Code) delete(user.Permissions, "/somedir") + user.Permissions["/somedir/.."] = []string{dataprovider.PermAny} + userAsJSON = getUserAsJSON(t, user) + req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON)) + rr = executeRequest(req) + checkResponseCode(t, http.StatusBadRequest, rr.Code) + delete(user.Permissions, "/somedir/..") user.Permissions["not_abs_path"] = []string{dataprovider.PermAny} userAsJSON = getUserAsJSON(t, user) req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON)) diff --git a/httpd/web.go b/httpd/web.go index bed8c7df..ee7c6f70 100644 --- a/httpd/web.go +++ b/httpd/web.go @@ -200,7 +200,7 @@ func getUserPermissionsFromPostFields(r *http.Request) map[string][]string { perms = append(perms, cleanedPerm) } } - if len(dir) > 0 && len(perms) > 0 { + if len(dir) > 0 { permissions[dir] = perms } } diff --git a/scripts/sftpgo_api_cli.py b/scripts/sftpgo_api_cli.py index 5e8ba16b..9fc8cc19 100755 --- a/scripts/sftpgo_api_cli.py +++ b/scripts/sftpgo_api_cli.py @@ -111,7 +111,7 @@ class SFTPGoApiRequests: directory = value else: values = [v.strip() for v in value.split(',') if v.strip()] - if directory and values: + if directory: permissions.update({directory:values}) return permissions diff --git a/sftpd/sftpd_test.go b/sftpd/sftpd_test.go index 38f517b2..4b7b2ce3 100644 --- a/sftpd/sftpd_test.go +++ b/sftpd/sftpd_test.go @@ -3250,6 +3250,17 @@ func TestUserPerms(t *testing.T) { } } +func TestUserEmptySubDirPerms(t *testing.T) { + user := getTestUser(true) + user.Permissions = make(map[string][]string) + user.Permissions["/emptyperms"] = []string{} + for _, p := range dataprovider.ValidPerms { + if user.HasPerm(p, "/emptyperms") { + t.Errorf("unexpected permission %#v for dir /emptyperms", p) + } + } +} + func TestUserFiltersIPMaskConditions(t *testing.T) { user := getTestUser(true) // with no filter login must be allowed even if the remoteIP is invalid