This is an automated email from the ASF dual-hosted git repository.

mani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/yunikorn-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 375eb941 [YUNIKORN-2715] Handle special characters for params like 
queue, username & groupname (#905)
375eb941 is described below

commit 375eb941273f9c5a85d412abe11fd702af0ed7d2
Author: Manikandan R <[email protected]>
AuthorDate: Thu Jul 4 13:21:39 2024 +0530

    [YUNIKORN-2715] Handle special characters for params like queue, username & 
groupname (#905)
    
    Closes: #905
    
    Signed-off-by: Manikandan R <[email protected]>
---
 pkg/webservice/handlers.go      |  44 ++++-
 pkg/webservice/handlers_test.go | 422 ++++++++++++++++++++++------------------
 2 files changed, 268 insertions(+), 198 deletions(-)

diff --git a/pkg/webservice/handlers.go b/pkg/webservice/handlers.go
index a66614dc..9bbb99bf 100644
--- a/pkg/webservice/handlers.go
+++ b/pkg/webservice/handlers.go
@@ -24,6 +24,7 @@ import (
        "io"
        "math"
        "net/http"
+       "net/url"
        "runtime"
        "sort"
        "strconv"
@@ -668,12 +669,17 @@ func getPartitionQueue(w http.ResponseWriter, r 
*http.Request) {
                return
        }
        queueName := vars.ByName("queue")
-       queueErr := validateQueue(queueName)
+       unescapedQueueName, err := url.QueryUnescape(queueName)
+       if err != nil {
+               buildJSONErrorResponse(w, err.Error(), http.StatusBadRequest)
+               return
+       }
+       queueErr := validateQueue(unescapedQueueName)
        if queueErr != nil {
                buildJSONErrorResponse(w, queueErr.Error(), 
http.StatusBadRequest)
                return
        }
-       queue := partitionContext.GetQueue(queueName)
+       queue := partitionContext.GetQueue(unescapedQueueName)
        if queue == nil {
                buildJSONErrorResponse(w, QueueDoesNotExists, 
http.StatusNotFound)
                return
@@ -737,7 +743,12 @@ func getQueueApplications(w http.ResponseWriter, r 
*http.Request) {
        }
        partition := vars.ByName("partition")
        queueName := vars.ByName("queue")
-       queueErr := validateQueue(queueName)
+       unescapedQueueName, err := url.QueryUnescape(queueName)
+       if err != nil {
+               buildJSONErrorResponse(w, err.Error(), http.StatusBadRequest)
+               return
+       }
+       queueErr := validateQueue(unescapedQueueName)
        if queueErr != nil {
                buildJSONErrorResponse(w, queueErr.Error(), 
http.StatusBadRequest)
                return
@@ -747,7 +758,7 @@ func getQueueApplications(w http.ResponseWriter, r 
*http.Request) {
                buildJSONErrorResponse(w, PartitionDoesNotExists, 
http.StatusNotFound)
                return
        }
-       queue := partitionContext.GetQueue(queueName)
+       queue := partitionContext.GetQueue(unescapedQueueName)
        if queue == nil {
                buildJSONErrorResponse(w, QueueDoesNotExists, 
http.StatusNotFound)
                return
@@ -820,6 +831,11 @@ func getApplication(w http.ResponseWriter, r 
*http.Request) {
        }
        partition := vars.ByName("partition")
        queueName := vars.ByName("queue")
+       unescapedQueueName, err := url.QueryUnescape(queueName)
+       if err != nil {
+               buildJSONErrorResponse(w, err.Error(), http.StatusBadRequest)
+               return
+       }
        application := vars.ByName("application")
        partitionContext := 
schedulerContext.GetPartitionWithoutClusterID(partition)
        if partitionContext == nil {
@@ -827,15 +843,15 @@ func getApplication(w http.ResponseWriter, r 
*http.Request) {
                return
        }
        var app *objects.Application
-       if len(queueName) == 0 {
+       if len(unescapedQueueName) == 0 {
                app = partitionContext.GetApplication(application)
        } else {
-               queueErr := validateQueue(queueName)
+               queueErr := validateQueue(unescapedQueueName)
                if queueErr != nil {
                        buildJSONErrorResponse(w, queueErr.Error(), 
http.StatusBadRequest)
                        return
                }
-               queue := partitionContext.GetQueue(queueName)
+               queue := partitionContext.GetQueue(unescapedQueueName)
                if queue == nil {
                        buildJSONErrorResponse(w, QueueDoesNotExists, 
http.StatusNotFound)
                        return
@@ -1078,7 +1094,12 @@ func getUserResourceUsage(w http.ResponseWriter, r 
*http.Request) {
                buildJSONErrorResponse(w, UserNameMissing, 
http.StatusBadRequest)
                return
        }
-       userTracker := ugm.GetUserManager().GetUserTracker(user)
+       unescapedUser, err := url.QueryUnescape(user)
+       if err != nil {
+               buildJSONErrorResponse(w, err.Error(), http.StatusBadRequest)
+               return
+       }
+       userTracker := ugm.GetUserManager().GetUserTracker(unescapedUser)
        if userTracker == nil {
                buildJSONErrorResponse(w, UserDoesNotExists, 
http.StatusNotFound)
                return
@@ -1114,7 +1135,12 @@ func getGroupResourceUsage(w http.ResponseWriter, r 
*http.Request) {
                buildJSONErrorResponse(w, GroupNameMissing, 
http.StatusBadRequest)
                return
        }
-       groupTracker := ugm.GetUserManager().GetGroupTracker(group)
+       unescapedGroupName, err := url.QueryUnescape(group)
+       if err != nil {
+               buildJSONErrorResponse(w, err.Error(), http.StatusBadRequest)
+               return
+       }
+       groupTracker := ugm.GetUserManager().GetGroupTracker(unescapedGroupName)
        if groupTracker == nil {
                buildJSONErrorResponse(w, GroupDoesNotExists, 
http.StatusNotFound)
                return
diff --git a/pkg/webservice/handlers_test.go b/pkg/webservice/handlers_test.go
index a49df1a6..169cca86 100644
--- a/pkg/webservice/handlers_test.go
+++ b/pkg/webservice/handlers_test.go
@@ -25,6 +25,7 @@ import (
        "fmt"
        "net/http"
        "net/http/httptest"
+       "net/url"
        "reflect"
        "strings"
        "testing"
@@ -278,6 +279,7 @@ const rmID = "rm-123"
 const policyGroup = "default-policy-group"
 const queueName = "root.default"
 const nodeID = "node-1"
+const invalidQueueName = "root.parent.test%Zt%23%3Art%3A%2F_ff-test"
 
 var (
        updatedExtraConf = map[string]string{
@@ -1106,74 +1108,17 @@ func TestGetPartitionQueuesHandler(t *testing.T) {
        err = json.Unmarshal(resp.outputBytes, &partitionQueuesDao)
        assert.NilError(t, err, unmarshalError)
        // assert root fields
-       assert.Equal(t, partitionQueuesDao.QueueName, configs.RootQueue)
-       assert.Equal(t, partitionQueuesDao.Status, objects.Active.String())
-       assert.Equal(t, partitionQueuesDao.Partition, configs.DefaultPartition)
-       assert.Assert(t, partitionQueuesDao.PendingResource == nil)
-       assert.Assert(t, partitionQueuesDao.MaxResource == nil)
-       assert.Assert(t, partitionQueuesDao.GuaranteedResource == nil)
-       assert.Assert(t, partitionQueuesDao.AllocatedResource == nil)
-       assert.Assert(t, partitionQueuesDao.PreemptingResource == nil)
-       assert.Assert(t, partitionQueuesDao.HeadRoom == nil)
-       assert.Assert(t, !partitionQueuesDao.IsLeaf)
-       assert.Assert(t, partitionQueuesDao.IsManaged)
-       assert.Equal(t, partitionQueuesDao.Parent, "")
-       assert.Assert(t, partitionQueuesDao.AbsUsedCapacity == nil)
-       assert.Equal(t, partitionQueuesDao.MaxRunningApps, uint64(0))
-       assert.Equal(t, partitionQueuesDao.RunningApps, uint64(0))
-       assert.Equal(t, partitionQueuesDao.CurrentPriority, configs.MinPriority)
-       assert.Assert(t, partitionQueuesDao.AllocatingAcceptedApps == nil)
-       assert.Equal(t, len(partitionQueuesDao.Properties), 1)
-       assert.Equal(t, 
partitionQueuesDao.Properties[configs.ApplicationSortPolicy], 
policies.FifoSortPolicy.String())
-       assert.DeepEqual(t, partitionQueuesDao.TemplateInfo, &templateInfo)
+       assertPartitionQueueDaoInfo(t, &partitionQueuesDao, configs.RootQueue, 
configs.DefaultPartition, nil, nil, false, true, "", &templateInfo)
 
        // assert child root.a fields
        assert.Equal(t, len(partitionQueuesDao.Children), 1)
        child := &partitionQueuesDao.Children[0]
-       assert.Equal(t, child.QueueName, "root.a")
-       assert.Equal(t, child.Status, objects.Active.String())
-       assert.Equal(t, child.Partition, "")
-       assert.Assert(t, child.PendingResource == nil)
-       assert.DeepEqual(t, child.MaxResource, maxResource.DAOMap())
-       assert.DeepEqual(t, child.GuaranteedResource, 
guaranteedResource.DAOMap())
-       assert.Assert(t, child.AllocatedResource == nil)
-       assert.Assert(t, child.PreemptingResource == nil)
-       assert.DeepEqual(t, child.HeadRoom, maxResource.DAOMap())
-       assert.Assert(t, !child.IsLeaf)
-       assert.Assert(t, child.IsManaged)
-       assert.Equal(t, child.Parent, configs.RootQueue)
-       assert.Assert(t, child.AbsUsedCapacity == nil)
-       assert.Equal(t, child.MaxRunningApps, uint64(0))
-       assert.Equal(t, child.RunningApps, uint64(0))
-       assert.Equal(t, child.CurrentPriority, configs.MinPriority)
-       assert.Assert(t, child.AllocatingAcceptedApps == nil)
-       assert.Equal(t, len(child.Properties), 1)
-       assert.Equal(t, child.Properties[configs.ApplicationSortPolicy], 
policies.FifoSortPolicy.String())
-       assert.DeepEqual(t, child.TemplateInfo, &templateInfo)
+       assertPartitionQueueDaoInfo(t, child, "root.a", "", 
maxResource.DAOMap(), guaranteedResource.DAOMap(), false, true, "root", 
&templateInfo)
 
        // assert child root.a.a1 fields
        assert.Equal(t, len(partitionQueuesDao.Children[0].Children), 1)
        child = &partitionQueuesDao.Children[0].Children[0]
-       assert.Equal(t, child.QueueName, "root.a.a1")
-       assert.Equal(t, child.Status, objects.Active.String())
-       assert.Equal(t, child.Partition, "")
-       assert.Assert(t, child.PendingResource == nil)
-       assert.DeepEqual(t, child.MaxResource, maxResource.DAOMap())
-       assert.DeepEqual(t, child.GuaranteedResource, 
guaranteedResource.DAOMap())
-       assert.Assert(t, child.AllocatedResource == nil)
-       assert.Assert(t, child.PreemptingResource == nil)
-       assert.DeepEqual(t, child.HeadRoom, maxResource.DAOMap())
-       assert.Assert(t, child.IsLeaf)
-       assert.Assert(t, child.IsManaged)
-       assert.Equal(t, child.Parent, "root.a")
-       assert.Assert(t, child.AbsUsedCapacity == nil)
-       assert.Equal(t, child.MaxRunningApps, uint64(0))
-       assert.Equal(t, child.RunningApps, uint64(0))
-       assert.Equal(t, child.CurrentPriority, configs.MinPriority)
-       assert.Assert(t, child.AllocatingAcceptedApps == nil)
-       assert.Equal(t, len(child.Properties), 1)
-       assert.Equal(t, child.Properties[configs.ApplicationSortPolicy], 
policies.FifoSortPolicy.String())
-       assert.Assert(t, child.TemplateInfo == nil)
+       assertPartitionQueueDaoInfo(t, child, "root.a.a1", "", 
maxResource.DAOMap(), guaranteedResource.DAOMap(), true, true, "root.a", nil)
 
        // test partition not exists
        req, err = http.NewRequest("GET", "/ws/v1/partition/default/queues", 
strings.NewReader(""))
@@ -1190,14 +1135,45 @@ func TestGetPartitionQueuesHandler(t *testing.T) {
        resp = &MockResponseWriter{}
        getPartitionQueues(resp, req)
        assertParamsMissing(t, resp)
+}
+
+func assertPartitionQueueDaoInfo(t *testing.T, partitionQueueDAOInfo 
*dao.PartitionQueueDAOInfo, queueName string, partition string, maxResource 
map[string]int64, gResource map[string]int64, leaf bool, isManaged bool, parent 
string, templateInfo *dao.TemplateInfo) {
+       assert.Equal(t, partitionQueueDAOInfo.QueueName, queueName)
+       assert.Equal(t, partitionQueueDAOInfo.Status, objects.Active.String())
+       assert.Equal(t, partitionQueueDAOInfo.Partition, partition)
+       assert.Assert(t, partitionQueueDAOInfo.PendingResource == nil)
+       assert.DeepEqual(t, partitionQueueDAOInfo.MaxResource, maxResource)
+       assert.DeepEqual(t, partitionQueueDAOInfo.GuaranteedResource, gResource)
+       assert.Assert(t, partitionQueueDAOInfo.AllocatedResource == nil)
+       assert.Assert(t, partitionQueueDAOInfo.PreemptingResource == nil)
+       assert.DeepEqual(t, partitionQueueDAOInfo.HeadRoom, maxResource)
+       assert.Equal(t, partitionQueueDAOInfo.IsLeaf, leaf)
+       assert.Equal(t, partitionQueueDAOInfo.IsManaged, isManaged)
+       assert.Equal(t, partitionQueueDAOInfo.Parent, parent)
+       assert.Assert(t, partitionQueueDAOInfo.AbsUsedCapacity == nil)
+       assert.Equal(t, partitionQueueDAOInfo.MaxRunningApps, uint64(0))
+       assert.Equal(t, partitionQueueDAOInfo.RunningApps, uint64(0))
+       assert.Equal(t, partitionQueueDAOInfo.CurrentPriority, 
configs.MinPriority)
+       assert.Assert(t, partitionQueueDAOInfo.AllocatingAcceptedApps == nil)
+       assert.Equal(t, len(partitionQueueDAOInfo.Properties), 1)
+       assert.Equal(t, 
partitionQueueDAOInfo.Properties[configs.ApplicationSortPolicy], 
policies.FifoSortPolicy.String())
+       assert.DeepEqual(t, partitionQueueDAOInfo.TemplateInfo, templateInfo)
+}
+
+func TestGetPartitionQueueHandler(t *testing.T) {
+       partitionQueuesHandler := "/ws/v1/partition/default/queue/"
+       queueA := "root.a"
+       setup(t, configTwoLevelQueues, 2)
+
+       NewWebApp(schedulerContext, nil)
 
        // test specific queue
        var partitionQueueDao1 dao.PartitionQueueDAOInfo
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.a", strings.NewReader(""))
+       req, err := http.NewRequest("GET", partitionQueuesHandler+queueA, 
strings.NewReader(""))
        assert.NilError(t, err, "HTTP request create failed")
        req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{httprouter.Param{Key: "partition", 
Value: "default"}, httprouter.Param{Key: "queue", Value: "root.a"}}))
        assert.NilError(t, err)
-       resp = &MockResponseWriter{}
+       resp := &MockResponseWriter{}
        getPartitionQueue(resp, req)
        err = json.Unmarshal(resp.outputBytes, &partitionQueueDao1)
        assert.NilError(t, err, unmarshalError)
@@ -1208,7 +1184,7 @@ func TestGetPartitionQueuesHandler(t *testing.T) {
 
        // test hierarchy queue
        var partitionQueueDao2 dao.PartitionQueueDAOInfo
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.a?subtree", strings.NewReader(""))
+       req, err = http.NewRequest("GET", 
partitionQueuesHandler+"root.a?subtree", strings.NewReader(""))
        assert.NilError(t, err, "HTTP request create failed")
        req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{httprouter.Param{Key: "partition", 
Value: "default"}, httprouter.Param{Key: "queue", Value: "root.a"}}))
        assert.NilError(t, err)
@@ -1223,7 +1199,7 @@ func TestGetPartitionQueuesHandler(t *testing.T) {
        assert.Equal(t, partitionQueueDao2.ChildNames[0], "root.a.a1")
 
        // test partition not exists
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.a", strings.NewReader(""))
+       req, err = http.NewRequest("GET", partitionQueuesHandler+queueA, 
strings.NewReader(""))
        assert.NilError(t, err, "HTTP request create failed")
        req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{httprouter.Param{Key: "partition", 
Value: "notexists"}}))
        assert.NilError(t, err)
@@ -1232,14 +1208,14 @@ func TestGetPartitionQueuesHandler(t *testing.T) {
        assertPartitionNotExists(t, resp)
 
        // test params name missing
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.a", strings.NewReader(""))
+       req, err = http.NewRequest("GET", partitionQueuesHandler+queueA, 
strings.NewReader(""))
        assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getPartitionQueue(resp, req)
        assertParamsMissing(t, resp)
 
        // test invalid queue name
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.a", strings.NewReader(""))
+       req, err = http.NewRequest("GET", partitionQueuesHandler+queueA, 
strings.NewReader(""))
        assert.NilError(t, err, "HTTP request create failed")
        req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{httprouter.Param{Key: "partition", 
Value: "default"}, httprouter.Param{Key: "queue", Value: "root.notexists!"}}))
        assert.NilError(t, err)
@@ -1248,13 +1224,45 @@ func TestGetPartitionQueuesHandler(t *testing.T) {
        assertQueueInvalid(t, resp, "root.notexists!", "notexists!")
 
        // test queue is not exists
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.a", strings.NewReader(""))
+       req, err = http.NewRequest("GET", partitionQueuesHandler+queueA, 
strings.NewReader(""))
        assert.NilError(t, err, "HTTP request create failed")
        req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{httprouter.Param{Key: "partition", 
Value: "default"}, httprouter.Param{Key: "queue", Value: "notexists"}}))
        assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getPartitionQueue(resp, req)
        assertQueueNotExists(t, resp)
+
+       // test queue name with special characters escaped properly
+       queueName := url.QueryEscape("root.parent.test@t#:rt:/_ff-test")
+       req, err = http.NewRequest("GET", partitionQueuesHandler+queueName, 
strings.NewReader(""))
+       assert.NilError(t, err, "HTTP request create failed")
+       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
+               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
+               httprouter.Param{Key: "queue", Value: queueName},
+       }))
+       resp = &MockResponseWriter{}
+       getPartitionQueue(resp, req)
+       assertQueueNotExists(t, resp)
+
+       // test queue name with special characters escaped not properly, catch 
error at request level
+       _, err = http.NewRequest("GET", 
partitionQueuesHandler+invalidQueueName, strings.NewReader(""))
+       assert.ErrorContains(t, err, "invalid URL escape")
+
+       // test queue name with special characters escaped not properly, catch 
error while un escaping queue name
+       req, err = http.NewRequest("GET", partitionQueuesHandler+queueA, 
strings.NewReader(""))
+       assert.NilError(t, err, "HTTP request create failed")
+       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
+               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
+               httprouter.Param{Key: "queue", Value: invalidQueueName},
+       }))
+       resp = &MockResponseWriter{}
+       getPartitionQueue(resp, req)
+       var errInfo dao.YAPIError
+       err = json.Unmarshal(resp.outputBytes, &errInfo)
+       assert.NilError(t, err, unmarshalError)
+       assert.Equal(t, http.StatusBadRequest, resp.statusCode, statusCodeError)
+       assert.Equal(t, errInfo.Message, "invalid URL escape \"%Zt\"", 
jsonMessageError)
+       assert.Equal(t, errInfo.StatusCode, http.StatusBadRequest)
 }
 
 func TestGetClusterInfo(t *testing.T) {
@@ -1408,10 +1416,13 @@ func addAppWithUserGroup(t *testing.T, id string, part 
*scheduler.PartitionConte
 }
 
 func TestGetQueueApplicationsHandler(t *testing.T) {
+       handlerURL := "/ws/v1/partition/default/queue/"
+       handlerSuffix := "/applications"
+       defaultQueue := "root.default"
        part := setup(t, configDefault, 1)
 
        // add an application
-       app := addApp(t, "app-1", part, "root.default", false)
+       app := addApp(t, "app-1", part, defaultQueue, false)
 
        // add placeholder to test PlaceholderDAOInfo
        tg := "tg-1"
@@ -1431,13 +1442,8 @@ func TestGetQueueApplicationsHandler(t *testing.T) {
        NewWebApp(schedulerContext, nil)
 
        var req *http.Request
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/applications", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "queue", Value: "root.default"},
-       }))
-       assert.NilError(t, err, "Get Queue Applications Handler request failed")
+       req, err = createRequest(t, handlerURL+defaultQueue+handlerSuffix, 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
defaultQueue})
+       assert.NilError(t, err)
        resp := &MockResponseWriter{}
        var appsDao []*dao.ApplicationDAOInfo
        getQueueApplications(resp, req)
@@ -1461,39 +1467,24 @@ func TestGetQueueApplicationsHandler(t *testing.T) {
 
        // test nonexistent partition
        var req1 *http.Request
-       req1, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/applications", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req1 = req1.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: "notexists"},
-               httprouter.Param{Key: "queue", Value: "root.default"},
-       }))
-       assert.NilError(t, err, "Get Queue Applications Handler request failed")
+       req1, err = createRequest(t, handlerURL+defaultQueue+handlerSuffix, 
map[string]string{"partition": "notexists", "queue": defaultQueue})
+       assert.NilError(t, err)
        resp1 := &MockResponseWriter{}
        getQueueApplications(resp1, req1)
        assertPartitionNotExists(t, resp1)
 
        // test nonexistent queue
        var req2 *http.Request
-       req2, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/applications", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req2 = req2.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "queue", Value: "notexists"},
-       }))
-       assert.NilError(t, err, "Get Queue Applications Handler request failed")
+       req2, err = createRequest(t, handlerURL+defaultQueue+handlerSuffix, 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
"notexists"})
+       assert.NilError(t, err)
        resp2 := &MockResponseWriter{}
        getQueueApplications(resp2, req2)
        assertQueueNotExists(t, resp2)
 
        // test queue without applications
        var req3 *http.Request
-       req3, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.noapps/applications", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req3 = req3.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "queue", Value: "root.noapps"},
-       }))
-       assert.NilError(t, err, "Get Queue Applications Handler request failed")
+       req3, err = createRequest(t, handlerURL+"root.noapps"+handlerSuffix, 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
"root.noapps"})
+       assert.NilError(t, err)
        resp3 := &MockResponseWriter{}
        var appsDao3 []*dao.ApplicationDAOInfo
        getQueueApplications(resp3, req3)
@@ -1502,11 +1493,49 @@ func TestGetQueueApplicationsHandler(t *testing.T) {
        assert.Equal(t, len(appsDao3), 0)
 
        // test missing params name
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/applications", 
strings.NewReader(""))
-       assert.NilError(t, err, "Get Queue Applications Handler request failed")
+       req, err = createRequest(t, handlerURL+queueName+handlerSuffix, 
map[string]string{})
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getQueueApplications(resp, req)
        assertParamsMissing(t, resp)
+
+       // test queue name with special characters escaped properly
+       queueName := url.QueryEscape("root.parent.test@t#:rt:/_ff-test")
+       req, err = createRequest(t, handlerURL+queueName+handlerSuffix, 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
queueName})
+       assert.NilError(t, err)
+       resp = &MockResponseWriter{}
+       getQueueApplications(resp, req)
+       assertQueueNotExists(t, resp)
+
+       // test queue name with special characters escaped not properly, catch 
error at request level
+       _, err = http.NewRequest("GET", 
handlerURL+invalidQueueName+handlerSuffix, strings.NewReader(""))
+       assert.ErrorContains(t, err, "invalid URL escape")
+
+       // test queue name with special characters escaped not properly, catch 
error while un escaping queue name
+       req, err = createRequest(t, handlerURL+defaultQueue+handlerSuffix, 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
invalidQueueName})
+       assert.NilError(t, err)
+       resp = &MockResponseWriter{}
+       getQueueApplications(resp, req)
+       var errInfo dao.YAPIError
+       err = json.Unmarshal(resp.outputBytes, &errInfo)
+       assert.NilError(t, err, unmarshalError)
+       assert.Equal(t, http.StatusBadRequest, resp.statusCode, statusCodeError)
+       assert.Equal(t, errInfo.Message, "invalid URL escape \"%Zt\"", 
jsonMessageError)
+       assert.Equal(t, errInfo.StatusCode, http.StatusBadRequest)
+}
+
+func createRequest(t *testing.T, url string, paramsMap map[string]string) 
(*http.Request, error) {
+       var err error
+       var req *http.Request
+       req, err = http.NewRequest("GET", url, strings.NewReader(""))
+       assert.NilError(t, err, "Handler request create failed")
+       var params httprouter.Params
+       for k, v := range paramsMap {
+               param := httprouter.Param{Key: k, Value: v}
+               params = append(params, param)
+       }
+       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, params))
+       return req, err
 }
 
 func checkLegalGetAppsRequest(t *testing.T, url string, params 
httprouter.Params, expected []*dao.ApplicationDAOInfo) {
@@ -1613,14 +1642,8 @@ func TestGetApplicationHandler(t *testing.T) {
        NewWebApp(schedulerContext, nil)
 
        var req *http.Request
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "queue", Value: "root.default"},
-               httprouter.Param{Key: "application", Value: "app-1"},
-       }))
-       assert.NilError(t, err, "Get Application Handler request failed")
+       req, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
"root.default", "application": "app-1"})
+       assert.NilError(t, err)
        resp := &MockResponseWriter{}
        var appsDao *dao.ApplicationDAOInfo
        getApplication(resp, req)
@@ -1635,55 +1658,32 @@ func TestGetApplicationHandler(t *testing.T) {
 
        // test nonexistent partition
        var req1 *http.Request
-       req1, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req1 = req1.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: "notexists"},
-               httprouter.Param{Key: "queue", Value: "root.default"},
-               httprouter.Param{Key: "application", Value: "app-1"},
-       }))
-       assert.NilError(t, err, "Get Application Handler request failed")
+       req1, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
map[string]string{"partition": "notexists", "queue": "root.default", 
"application": "app-1"})
+       assert.NilError(t, err)
        resp1 := &MockResponseWriter{}
        getApplication(resp1, req1)
        assertPartitionNotExists(t, resp1)
 
        // test nonexistent queue
        var req2 *http.Request
-       req2, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req2 = req2.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "queue", Value: "notexists"},
-               httprouter.Param{Key: "application", Value: "app-1"},
-       }))
-       assert.NilError(t, err, "Get Application Handler request failed")
+       req2, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
"notexists", "application": "app-1"})
+       assert.NilError(t, err)
        resp2 := &MockResponseWriter{}
        getApplication(resp2, req2)
        assertQueueNotExists(t, resp2)
 
        // test nonexistent application
        var req3 *http.Request
-       req3, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.noapps/application/app-1", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req3 = req3.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "queue", Value: "root.noapps"},
-               httprouter.Param{Key: "application", Value: "app-1"},
-       }))
-       assert.NilError(t, err, "Get Application Handler request failed")
+       req3, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.noapps/application/app-1", 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
"root.noapps", "application": "app-1"})
+       assert.NilError(t, err)
        resp3 := &MockResponseWriter{}
        getApplication(resp3, req3)
        assertApplicationNotExists(t, resp3)
 
        // test without queue
        var req4 *http.Request
-       req4, err = http.NewRequest("GET", 
"/ws/v1/partition/default/application/app-1", strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req4 = req4.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "application", Value: "app-1"},
-       }))
-       assert.NilError(t, err, "Get Application Handler request failed")
+       req4, err = createRequest(t, 
"/ws/v1/partition/default/application/app-1", map[string]string{"partition": 
partitionNameWithoutClusterID, "application": "app-1"})
+       assert.NilError(t, err)
        resp4 := &MockResponseWriter{}
        var appsDao4 *dao.ApplicationDAOInfo
        getApplication(resp4, req4)
@@ -1692,24 +1692,42 @@ func TestGetApplicationHandler(t *testing.T) {
 
        // test invalid queue name
        var req5 *http.Request
-       req5, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req5 = req5.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "partition", Value: 
partitionNameWithoutClusterID},
-               httprouter.Param{Key: "queue", Value: "root.test.test123!"},
-               httprouter.Param{Key: "application", Value: "app-1"},
-       }))
-       assert.NilError(t, err, "Get Application Handler request failed")
+       req5, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
"root.test.test123!", "application": "app-1"})
+       assert.NilError(t, err)
        resp5 := &MockResponseWriter{}
        getApplication(resp5, req5)
        assertQueueInvalid(t, resp5, "root.test.test123!", "test123!")
 
        // test missing params name
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
strings.NewReader(""))
-       assert.NilError(t, err, "Get Application Handler request failed")
+       req, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
map[string]string{})
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getApplication(resp, req)
        assertParamsMissing(t, resp)
+
+       // test queue name with special characters escaped properly
+       queueName := url.QueryEscape("root.parent.test@t#:rt:/_ff-test")
+       req, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
queueName})
+       assert.NilError(t, err)
+       resp = &MockResponseWriter{}
+       getApplication(resp, req)
+       assertQueueNotExists(t, resp)
+
+       // test queue name with special characters escaped not properly, catch 
error at request level
+       _, err = http.NewRequest("GET", 
"/ws/v1/partition/default/queue/"+invalidQueueName+"/application/app-1", 
strings.NewReader(""))
+       assert.ErrorContains(t, err, "invalid URL escape")
+
+       // test queue name with special characters escaped not properly, catch 
error while un escaping queue name
+       req, err = createRequest(t, 
"/ws/v1/partition/default/queue/root.default/application/app-1", 
map[string]string{"partition": partitionNameWithoutClusterID, "queue": 
invalidQueueName})
+       assert.NilError(t, err)
+       resp = &MockResponseWriter{}
+       getApplication(resp, req)
+       var errInfo dao.YAPIError
+       err = json.Unmarshal(resp.outputBytes, &errInfo)
+       assert.NilError(t, err, unmarshalError)
+       assert.Equal(t, http.StatusBadRequest, resp.statusCode, statusCodeError)
+       assert.Equal(t, errInfo.Message, "invalid URL escape \"%Zt\"", 
jsonMessageError)
+       assert.Equal(t, errInfo.StatusCode, http.StatusBadRequest)
 }
 
 func assertParamsMissing(t *testing.T, resp *MockResponseWriter) {
@@ -1882,39 +1900,25 @@ func TestFullStateDumpPath(t *testing.T) {
        verifyStateDumpJSON(t, &aggregated, 1)
 }
 
-func TestSpecificUserAndGroupResourceUsage(t *testing.T) {
+func TestSpecificUserResourceUsage(t *testing.T) {
        prepareUserAndGroupContext(t, groupsLimitsConfig)
        // Test user name is missing
-       req, err := http.NewRequest("GET", 
"/ws/v1/partition/default/usage/user/", strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "group", Value: "testgroup"},
-       }))
-       assert.NilError(t, err, "Get User Resource Usage Handler request 
failed")
+       req, err := createRequest(t, "/ws/v1/partition/default/usage/user/", 
map[string]string{"group": "testgroup"})
+       assert.NilError(t, err)
        resp := &MockResponseWriter{}
        getUserResourceUsage(resp, req)
        assertUserNameMissing(t, resp)
 
        // Test group name is missing
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/group/", strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "user", Value: "testuser"},
-               httprouter.Param{Key: "group", Value: ""},
-       }))
-       assert.NilError(t, err, "Get Group Resource Usage Handler request 
failed")
+       req, err = createRequest(t, "/ws/v1/partition/default/usage/group/", 
map[string]string{"user": "testuser"})
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getGroupResourceUsage(resp, req)
        assertGroupNameMissing(t, resp)
 
        // Test existed user query
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/user/", strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "user", Value: "testuser"},
-               httprouter.Param{Key: "group", Value: "testgroup"},
-       }))
-       assert.NilError(t, err, "Get User Resource Usage Handler request 
failed")
+       req, err = createRequest(t, "/ws/v1/partition/default/usage/user/", 
map[string]string{"user": "testuser", "group": "testgroup"})
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getUserResourceUsage(resp, req)
        assertUserExists(t, resp,
@@ -1936,25 +1940,44 @@ func TestSpecificUserAndGroupResourceUsage(t 
*testing.T) {
                })
 
        // Test non-existing user query
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/user/", strings.NewReader(""))
-       assert.NilError(t, err, "HTTP request create failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "user", Value: "testNonExistingUser"},
-               httprouter.Param{Key: "group", Value: "testgroup"},
-       }))
-       assert.NilError(t, err, "Get User Resource Usage Handler request 
failed")
+       req, err = createRequest(t, "/ws/v1/partition/default/usage/user/", 
map[string]string{"user": "testNonExistingUser", "group": "testgroup"})
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getUserResourceUsage(resp, req)
        assertUserNotExists(t, resp)
 
-       // Test existed group query
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/group/", strings.NewReader(""))
-       assert.NilError(t, err, "Get Group Resource Usage Handler request 
failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "user", Value: "testuser"},
-               httprouter.Param{Key: "group", Value: "testgroup"},
-       }))
+       // Test username with special characters escaped properly
+       validUser := url.QueryEscape("test_a-b_c@#d@do:mai/n.com")
+       req, err = createRequest(t, "/ws/v1/partition/default/usage/user/", 
map[string]string{"user": validUser, "group": "testgroup"})
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
+       getUserResourceUsage(resp, req)
+       assertUserNotExists(t, resp)
+
+       // Test username with special characters not escaped properly, catch 
error at request level
+       invalidUser := "test_a-b_c%Zt@#d@do:mai/n.com"
+       _, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/user/"+invalidUser, strings.NewReader(""))
+       assert.ErrorContains(t, err, "invalid URL escape")
+
+       // Test username with special characters not escaped properly, catch 
error while un escaping username
+       req, err = createRequest(t, "/ws/v1/partition/default/usage/user/test", 
map[string]string{"user": invalidUser, "group": "testgroup"})
+       assert.NilError(t, err)
+       resp = &MockResponseWriter{}
+       getUserResourceUsage(resp, req)
+       var errInfo dao.YAPIError
+       err = json.Unmarshal(resp.outputBytes, &errInfo)
+       assert.NilError(t, err, unmarshalError)
+       assert.Equal(t, http.StatusBadRequest, resp.statusCode, statusCodeError)
+       assert.Equal(t, errInfo.Message, "invalid URL escape \"%Zt\"", 
jsonMessageError)
+       assert.Equal(t, errInfo.StatusCode, http.StatusBadRequest)
+}
+
+func TestSpecificGroupResourceUsage(t *testing.T) {
+       prepareUserAndGroupContext(t, groupsLimitsConfig)
+       // Test existed group query
+       req, err := createRequest(t, "/ws/v1/partition/default/usage/group", 
map[string]string{"user": "testuser", "group": "testgroup"})
+       assert.NilError(t, err)
+       resp := &MockResponseWriter{}
        getGroupResourceUsage(resp, req)
        assertGroupExists(t, resp,
                &dao.GroupResourceUsageDAOInfo{
@@ -1976,22 +1999,43 @@ func TestSpecificUserAndGroupResourceUsage(t 
*testing.T) {
                })
 
        // Test non-existing group query
-       req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/group/", strings.NewReader(""))
-       assert.NilError(t, err, "Get Group Resource Usage Handler request 
failed")
-       req = req.WithContext(context.WithValue(req.Context(), 
httprouter.ParamsKey, httprouter.Params{
-               httprouter.Param{Key: "user", Value: "testuser"},
-               httprouter.Param{Key: "group", Value: "testNonExistingGroup"},
-       }))
+       req, err = createRequest(t, "/ws/v1/partition/default/usage/group", 
map[string]string{"user": "testuser", "group": "testNonExistingGroup"})
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getGroupResourceUsage(resp, req)
        assertGroupNotExists(t, resp)
 
        // Test params name missing
        req, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/group/", strings.NewReader(""))
-       assert.NilError(t, err, "Get Group Resource Usage Handler request 
failed")
+       assert.NilError(t, err)
        resp = &MockResponseWriter{}
        getGroupResourceUsage(resp, req)
        assertParamsMissing(t, resp)
+
+       // Test group name with special characters escaped properly
+       validGroup := url.QueryEscape("test_a-b_c@#d@do:mai/n.com")
+       req, err = createRequest(t, "/ws/v1/partition/default/usage/group", 
map[string]string{"user": "testuser", "group": validGroup})
+       assert.NilError(t, err)
+       resp = &MockResponseWriter{}
+       getGroupResourceUsage(resp, req)
+       assertGroupNotExists(t, resp)
+
+       // Test group name with special characters not escaped properly, catch 
error at request level
+       invalidGroup := "test_a-b_c%Zt@#d@do:mai/n.com"
+       _, err = http.NewRequest("GET", 
"/ws/v1/partition/default/usage/group/"+invalidGroup, strings.NewReader(""))
+       assert.ErrorContains(t, err, "invalid URL escape")
+
+       // Test group name with special characters not escaped properly, catch 
error while un escaping group name
+       req, err = createRequest(t, 
"/ws/v1/partition/default/usage/group/test", map[string]string{"user": 
"testuser", "group": invalidGroup})
+       assert.NilError(t, err)
+       resp = &MockResponseWriter{}
+       getGroupResourceUsage(resp, req)
+       var errInfo dao.YAPIError
+       err = json.Unmarshal(resp.outputBytes, &errInfo)
+       assert.NilError(t, err, unmarshalError)
+       assert.Equal(t, http.StatusBadRequest, resp.statusCode, statusCodeError)
+       assert.Equal(t, errInfo.Message, "invalid URL escape \"%Zt\"", 
jsonMessageError)
+       assert.Equal(t, errInfo.StatusCode, http.StatusBadRequest)
 }
 
 func TestUsersAndGroupsResourceUsage(t *testing.T) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to