diff --git a/integration-test/collections/console_rps_apis.postman_collection.json b/integration-test/collections/console_rps_apis.postman_collection.json index 95c5c3add..c4cb5155b 100644 --- a/integration-test/collections/console_rps_apis.postman_collection.json +++ b/integration-test/collections/console_rps_apis.postman_collection.json @@ -1694,6 +1694,51 @@ }, "response": [] }, + { + "name": "Create CIRA Config - invalid configName with special char %", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {\r", + " pm.response.to.have.status(400);\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n\t\"configName\": \"cira%config\",\r\n\t\"mpsServerAddress\": \"192.168.8.50\",\r\n\t\"mpsPort\": 4433,\r\n\t\"username\": \"admin\",\r\n\t\"commonName\": \"192.168.8.50\",\r\n\t\"serverAddressFormat\": 3,\r\n\t\"authMethod\": 2,\r\n\t\"mpsRootCertificate\": \"rootcert\",\r\n\t\"proxyDetails\": \"\"\r\n}" + }, + "url": { + "raw": "{{protocol}}://{{host}}/api/v1/admin/ciraconfigs", + "protocol": "{{protocol}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v1", + "admin", + "ciraconfigs" + ] + } + }, + "response": [] + }, { "name": "Create Profile \"profile6\" - CCM invalid TLS & CIRA", "event": [ diff --git a/internal/controller/httpapi/router.go b/internal/controller/httpapi/router.go index 3138f28b4..440102247 100644 --- a/internal/controller/httpapi/router.go +++ b/internal/controller/httpapi/router.go @@ -5,6 +5,8 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" "github.com/prometheus/client_golang/prometheus/promhttp" ginprometheus "github.com/zsais/go-gin-prometheus" @@ -12,6 +14,7 @@ import ( v1 "github.com/device-management-toolkit/console/internal/controller/httpapi/v1" v2 "github.com/device-management-toolkit/console/internal/controller/httpapi/v2" openapi "github.com/device-management-toolkit/console/internal/controller/openapi" + dto "github.com/device-management-toolkit/console/internal/entity/dto/v1" "github.com/device-management-toolkit/console/internal/usecase" "github.com/device-management-toolkit/console/pkg/logger" ) @@ -59,6 +62,13 @@ func NewRouter(handler *gin.Engine, l logger.Interface, t usecase.Usecases, cfg protected = handler.Group("/api", login.JWTAuthMiddleware()) } + // Register custom validators once + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + if err := v.RegisterValidation("alphanumhyphenunderscore", dto.ValidateAlphaNumHyphenUnderscore); err != nil { + l.Error("failed to register custom validation: " + err.Error()) + } + } + // Routers h2 := protected.Group("/v1") { diff --git a/internal/controller/httpapi/v1/domains.go b/internal/controller/httpapi/v1/domains.go index 98d272778..8cb753b9f 100644 --- a/internal/controller/httpapi/v1/domains.go +++ b/internal/controller/httpapi/v1/domains.go @@ -4,8 +4,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" - "github.com/go-playground/validator/v10" "github.com/device-management-toolkit/console/internal/entity/dto/v1" "github.com/device-management-toolkit/console/internal/usecase/domains" @@ -23,15 +21,6 @@ type domainRoutes struct { func NewDomainRoutes(handler *gin.RouterGroup, t domains.Feature, l logger.Interface) { r := &domainRoutes{t, l} - if binding.Validator != nil { - if v, ok := binding.Validator.Engine().(*validator.Validate); ok { - if err := v.RegisterValidation("alphanumhyphenunderscore", dto.ValidateAlphaNumHyphenUnderscore); err != nil { - validationErr := ErrValidationDomains.Wrap("NewDomainRoutes", "RegisterValidation", err) - l.Error(validationErr, "failed to register alphanumhyphenunderscore validation") - } - } - } - h := handler.Group("/domains") { h.GET("", r.get) diff --git a/internal/entity/dto/v1/ciraconfig.go b/internal/entity/dto/v1/ciraconfig.go index ccb9bad65..0a59d276d 100644 --- a/internal/entity/dto/v1/ciraconfig.go +++ b/internal/entity/dto/v1/ciraconfig.go @@ -12,7 +12,7 @@ type CIRAConfigCountResponse struct { } type CIRAConfig struct { - ConfigName string `json:"configName" example:"My CIRA Config"` + ConfigName string `json:"configName" binding:"required,alphanumhyphenunderscore" example:"ciraconfig"` MPSAddress string `json:"mpsServerAddress" binding:"required,url|ipv4|ipv6" example:"https://example.com"` MPSPort int `json:"mpsPort" binding:"required,gt=1024,lt=49151" example:"4433"` Username string `json:"username" binding:"required,alphanum" example:"my_username"` diff --git a/internal/entity/dto/v1/ciraconfig_test.go b/internal/entity/dto/v1/ciraconfig_test.go new file mode 100644 index 000000000..76f8f6935 --- /dev/null +++ b/internal/entity/dto/v1/ciraconfig_test.go @@ -0,0 +1,82 @@ +package dto + +import ( + "testing" + + "github.com/go-playground/validator/v10" + "github.com/stretchr/testify/assert" +) + +func TestCIRAConfig_ConfigName_AlphaNumHyphenUnderscore(t *testing.T) { + t.Parallel() + + validate := validator.New() + err := validate.RegisterValidation("alphanumhyphenunderscore", ValidateAlphaNumHyphenUnderscore) + assert.NoError(t, err) + + tests := []struct { + name string + input string + wantErr bool + }{ + { + name: "valid alphanumeric", + input: "ciraconfig1", + wantErr: false, + }, + { + name: "valid with hyphen", + input: "cira-config", + wantErr: false, + }, + { + name: "valid with underscore", + input: "cira_config", + wantErr: false, + }, + { + name: "valid mixed", + input: "my-cira-config_1", + wantErr: false, + }, + { + name: "invalid with spaces", + input: "cira config", + wantErr: true, + }, + { + name: "invalid with special chars", + input: "cira@config!", + wantErr: true, + }, + { + name: "invalid with dots", + input: "cira.config", + wantErr: true, + }, + { + name: "invalid empty", + input: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + type testStruct struct { + ConfigName string `validate:"alphanumhyphenunderscore"` + } + + s := testStruct{ConfigName: tt.input} + err := validate.Struct(s) + + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +}