feat(namespace): add normalization function for namespace

This commit is contained in:
Adrien Raffin-Caboisse 2022-02-22 12:45:50 +01:00
parent 69cdfbb56f
commit 92ffac625e
No known key found for this signature in database
GPG key ID: 7FB60532DEBEAD6A
5 changed files with 120 additions and 190 deletions

View file

@ -2,7 +2,10 @@ package headscale
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
@ -16,8 +19,11 @@ const (
errNamespaceExists = Error("Namespace already exists")
errNamespaceNotFound = Error("Namespace not found")
errNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found")
errInvalidNamespaceName = Error("Invalid namespace name")
)
var normalizeNamespaceRegex = regexp.MustCompile("[^a-z0-9-.]+")
// Namespace is the way Headscale implements the concept of users in Tailscale
//
// At the end of the day, users in Tailscale are some kind of 'bubbles' or namespaces
@ -30,7 +36,12 @@ type Namespace struct {
// CreateNamespace creates a new Namespace. Returns error if could not be created
// or another namespace already exists.
func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
var err error
namespace := Namespace{}
name, err = NormalizeNamespaceName(name)
if err != nil {
return nil, err
}
if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil {
return nil, errNamespaceExists
}
@ -50,6 +61,10 @@ func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
// DestroyNamespace destroys a Namespace. Returns error if the Namespace does
// not exist or if there are machines associated with it.
func (h *Headscale) DestroyNamespace(name string) error {
name, err := NormalizeNamespaceName(name)
if err != nil {
return err
}
namespace, err := h.GetNamespace(name)
if err != nil {
return errNamespaceNotFound
@ -84,10 +99,15 @@ func (h *Headscale) DestroyNamespace(name string) error {
// RenameNamespace renames a Namespace. Returns error if the Namespace does
// not exist or if another Namespace exists with the new name.
func (h *Headscale) RenameNamespace(oldName, newName string) error {
var err error
oldNamespace, err := h.GetNamespace(oldName)
if err != nil {
return err
}
newName, err = NormalizeNamespaceName(newName)
if err != nil {
return err
}
_, err = h.GetNamespace(newName)
if err == nil {
return errNamespaceExists
@ -108,6 +128,10 @@ func (h *Headscale) RenameNamespace(oldName, newName string) error {
// GetNamespace fetches a namespace by name.
func (h *Headscale) GetNamespace(name string) (*Namespace, error) {
namespace := Namespace{}
name, err := NormalizeNamespaceName(name)
if err != nil {
return nil, err
}
if result := h.db.First(&namespace, "name = ?", name); errors.Is(
result.Error,
gorm.ErrRecordNotFound,
@ -130,6 +154,10 @@ func (h *Headscale) ListNamespaces() ([]Namespace, error) {
// ListMachinesInNamespace gets all the nodes in a given namespace.
func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) {
name, err := NormalizeNamespaceName(name)
if err != nil {
return nil, err
}
namespace, err := h.GetNamespace(name)
if err != nil {
return nil, err
@ -145,6 +173,10 @@ func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) {
// ListSharedMachinesInNamespace returns all the machines that are shared to the specified namespace.
func (h *Headscale) ListSharedMachinesInNamespace(name string) ([]Machine, error) {
name, err := NormalizeNamespaceName(name)
if err != nil {
return nil, err
}
namespace, err := h.GetNamespace(name)
if err != nil {
return nil, err
@ -170,6 +202,10 @@ func (h *Headscale) ListSharedMachinesInNamespace(name string) ([]Machine, error
// SetMachineNamespace assigns a Machine to a namespace.
func (h *Headscale) SetMachineNamespace(machine *Machine, namespaceName string) error {
namespaceName, err := NormalizeNamespaceName(namespaceName)
if err != nil {
return err
}
namespace, err := h.GetNamespace(namespaceName)
if err != nil {
return err
@ -233,3 +269,24 @@ func (n *Namespace) toProto() *v1.Namespace {
CreatedAt: timestamppb.New(n.CreatedAt),
}
}
// NormalizeNamespaceName will replace forbidden chars in namespace
// it can also return an error if the namespace doesn't respect RFC 952 and 1123
func NormalizeNamespaceName(name string) (string, error) {
name = strings.ToLower(name)
name = strings.ReplaceAll(name, "@", ".")
name = strings.ReplaceAll(name, "'", "")
name = normalizeNamespaceRegex.ReplaceAllString(name, "-")
for _, elt := range strings.Split(name, ".") {
if len(elt) > 63 {
return "", fmt.Errorf(
"label %v is more than 63 chars: %w",
elt,
errInvalidNamespaceName,
)
}
}
return name, nil
}