Only allow simple names when creating workers, VMs, etc. and escape paths in API client (#129)

* Controller: only allow simple names when creating workers, VMs, etc.

* Client: escape paths

* simplename: allow ':' character
This commit is contained in:
Nikolay Edigaryev 2023-09-22 22:51:43 +04:00 committed by GitHub
parent 2adb17c3c4
commit 8c62df0eba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 13 deletions

View File

@ -4,6 +4,7 @@ import (
"errors"
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
"github.com/cirruslabs/orchard/internal/responder"
"github.com/cirruslabs/orchard/internal/simplename"
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
@ -23,7 +24,11 @@ func (controller *Controller) createServiceAccount(ctx *gin.Context) responder.R
}
if serviceAccount.Name == "" {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("service account name is empty"))
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("service account name is empty"))
} else if err := simplename.Validate(serviceAccount.Name); err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("service account %v", err))
}
// validate roles

View File

@ -4,6 +4,7 @@ import (
"errors"
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
"github.com/cirruslabs/orchard/internal/responder"
"github.com/cirruslabs/orchard/internal/simplename"
"github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
@ -25,6 +26,9 @@ func (controller *Controller) createVM(ctx *gin.Context) responder.Responder {
if vm.Name == "" {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("VM name is empty"))
} else if err := simplename.Validate(vm.Name); err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("VM name %v", err))
}
if vm.Image == "" {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("VM image is empty"))

View File

@ -4,6 +4,7 @@ import (
"errors"
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
"github.com/cirruslabs/orchard/internal/responder"
"github.com/cirruslabs/orchard/internal/simplename"
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/gin-gonic/gin"
"net/http"
@ -23,6 +24,9 @@ func (controller *Controller) createWorker(ctx *gin.Context) responder.Responder
if worker.Name == "" {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("worker name is empty"))
} else if err := simplename.Validate(worker.Name); err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("worker name %v", err))
}
currentTime := time.Now()

View File

@ -0,0 +1,31 @@
package simplename
import (
"errors"
)
var ErrNotASimpleName = errors.New("name contains restricted characters, please only use [A-Za-z0-9:-_.]")
func Validate(s string) error {
for _, ch := range s {
if ch >= 'a' && ch <= 'z' {
continue
}
if ch >= 'A' && ch <= 'Z' {
continue
}
if ch >= '0' && ch <= '9' {
continue
}
if ch == ':' || ch == '-' || ch == '_' || ch == '.' {
continue
}
return ErrNotASimpleName
}
return nil
}

View File

@ -0,0 +1,17 @@
package simplename_test
import (
"github.com/cirruslabs/orchard/internal/simplename"
"github.com/stretchr/testify/require"
"testing"
)
func TestValidate(t *testing.T) {
require.NoError(t, simplename.Validate("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz:-_."))
require.NoError(t, simplename.Validate("vm-1"))
require.NoError(t, simplename.Validate("vm_2"))
require.NoError(t, simplename.Validate("host.local"))
require.Error(t, simplename.Validate("vm%"), "special characters")
require.Error(t, simplename.Validate("😐"), "non-ASCII characters")
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/cirruslabs/orchard/pkg/resource/v1"
"net/http"
"net/url"
)
type ServiceAccountsService struct {
@ -36,7 +37,8 @@ func (service *ServiceAccountsService) List(ctx context.Context) ([]v1.ServiceAc
func (service *ServiceAccountsService) Get(ctx context.Context, name string) (*v1.ServiceAccount, error) {
var serviceAccount v1.ServiceAccount
err := service.client.request(ctx, http.MethodGet, fmt.Sprintf("service-accounts/%s", name),
err := service.client.request(ctx, http.MethodGet,
fmt.Sprintf("service-accounts/%s", url.PathEscape(name)),
nil, &serviceAccount, nil)
if err != nil {
return nil, err
@ -46,7 +48,8 @@ func (service *ServiceAccountsService) Get(ctx context.Context, name string) (*v
}
func (service *ServiceAccountsService) Update(ctx context.Context, serviceAccount *v1.ServiceAccount) error {
err := service.client.request(ctx, http.MethodPut, fmt.Sprintf("service-accounts/%s", serviceAccount.Name),
err := service.client.request(ctx, http.MethodPut,
fmt.Sprintf("service-accounts/%s", url.PathEscape(serviceAccount.Name)),
serviceAccount, nil, nil)
if err != nil {
return err
@ -62,7 +65,8 @@ func (service *ServiceAccountsService) Delete(ctx context.Context, name string,
params["force"] = "true"
}
err := service.client.request(ctx, http.MethodDelete, fmt.Sprintf("service-accounts/%s", name),
err := service.client.request(ctx, http.MethodDelete,
fmt.Sprintf("service-accounts/%s", url.PathEscape(name)),
nil, nil, params)
if err != nil {
return err

View File

@ -6,6 +6,7 @@ import (
"github.com/cirruslabs/orchard/pkg/resource/v1"
"net"
"net/http"
"net/url"
"strconv"
)
@ -57,7 +58,7 @@ func (service *VMsService) List(ctx context.Context) ([]v1.VM, error) {
func (service *VMsService) Get(ctx context.Context, name string) (*v1.VM, error) {
var vm v1.VM
err := service.client.request(ctx, http.MethodGet, fmt.Sprintf("vms/%s", name),
err := service.client.request(ctx, http.MethodGet, fmt.Sprintf("vms/%s", url.PathEscape(name)),
nil, &vm, nil)
if err != nil {
return nil, err
@ -68,7 +69,7 @@ func (service *VMsService) Get(ctx context.Context, name string) (*v1.VM, error)
func (service *VMsService) Update(ctx context.Context, vm v1.VM) (*v1.VM, error) {
var updatedVM v1.VM
err := service.client.request(ctx, http.MethodPut, fmt.Sprintf("vms/%s", vm.Name),
err := service.client.request(ctx, http.MethodPut, fmt.Sprintf("vms/%s", url.PathEscape(vm.Name)),
vm, &updatedVM, nil)
if err != nil {
return &updatedVM, err
@ -78,7 +79,7 @@ func (service *VMsService) Update(ctx context.Context, vm v1.VM) (*v1.VM, error)
}
func (service *VMsService) Delete(ctx context.Context, name string) error {
err := service.client.request(ctx, http.MethodDelete, fmt.Sprintf("vms/%s", name),
err := service.client.request(ctx, http.MethodDelete, fmt.Sprintf("vms/%s", url.PathEscape(name)),
nil, nil, nil)
if err != nil {
return err
@ -93,7 +94,7 @@ func (service *VMsService) PortForward(
port uint16,
waitSeconds uint16,
) (net.Conn, error) {
return service.client.wsRequest(ctx, fmt.Sprintf("vms/%s/port-forward", name),
return service.client.wsRequest(ctx, fmt.Sprintf("vms/%s/port-forward", url.PathEscape(name)),
map[string]string{
"port": strconv.FormatUint(uint64(port), 10),
"wait": strconv.FormatUint(uint64(waitSeconds), 10),
@ -101,12 +102,12 @@ func (service *VMsService) PortForward(
}
func (service *VMsService) StreamEvents(name string) *EventStreamer {
return NewEventStreamer(service.client, fmt.Sprintf("vms/%s/events", name))
return NewEventStreamer(service.client, fmt.Sprintf("vms/%s/events", url.PathEscape(name)))
}
func (service *VMsService) Logs(ctx context.Context, name string) (lines []string, err error) {
var events []v1.Event
err = service.client.request(ctx, http.MethodGet, fmt.Sprintf("vms/%s/events", name),
err = service.client.request(ctx, http.MethodGet, fmt.Sprintf("vms/%s/events", url.PathEscape(name)),
nil, &events, nil)
if err != nil {
return

View File

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/cirruslabs/orchard/pkg/resource/v1"
"net/http"
"net/url"
)
type WorkersService struct {
@ -36,7 +37,7 @@ func (service *WorkersService) List(ctx context.Context) ([]v1.Worker, error) {
func (service *WorkersService) Get(ctx context.Context, name string) (*v1.Worker, error) {
var worker v1.Worker
err := service.client.request(ctx, http.MethodGet, fmt.Sprintf("workers/%s", name),
err := service.client.request(ctx, http.MethodGet, fmt.Sprintf("workers/%s", url.PathEscape(name)),
nil, &worker, nil)
if err != nil {
return nil, err
@ -46,7 +47,7 @@ func (service *WorkersService) Get(ctx context.Context, name string) (*v1.Worker
}
func (service *WorkersService) Update(ctx context.Context, worker v1.Worker) (*v1.Worker, error) {
err := service.client.request(ctx, http.MethodPut, fmt.Sprintf("workers/%s", worker.Name),
err := service.client.request(ctx, http.MethodPut, fmt.Sprintf("workers/%s", url.PathEscape(worker.Name)),
worker, &worker, nil)
if err != nil {
return nil, err
@ -56,7 +57,7 @@ func (service *WorkersService) Update(ctx context.Context, worker v1.Worker) (*v
}
func (service *WorkersService) Delete(ctx context.Context, name string) error {
err := service.client.request(ctx, http.MethodDelete, fmt.Sprintf("workers/%s", name),
err := service.client.request(ctx, http.MethodDelete, fmt.Sprintf("workers/%s", url.PathEscape(name)),
nil, nil, nil)
if err != nil {
return err