[misc] Move shared components to shared directory (#4286)

Moved the following directories:

```
  - management/client → shared/management/client
  - management/domain → shared/management/domain
  - management/proto → shared/management/proto
  - signal/client → shared/signal/client
  - signal/proto → shared/signal/proto
  - relay/client → shared/relay/client
  - relay/auth → shared/relay/auth
```

and adjusted import paths
This commit is contained in:
Viktor Liu
2025-08-05 15:22:58 +02:00
committed by GitHub
parent 3d3c4c5844
commit 1d5e871bdf
172 changed files with 181 additions and 152 deletions

View File

@@ -0,0 +1,45 @@
package domain
import (
"strings"
"golang.org/x/net/idna"
)
// Domain represents a punycode-encoded domain string.
// This should only be converted from a string when the string already is in punycode, otherwise use FromString.
type Domain string
// String converts the Domain to a non-punycode string.
// For an infallible conversion, use SafeString.
func (d Domain) String() (string, error) {
unicode, err := idna.ToUnicode(string(d))
if err != nil {
return "", err
}
return unicode, nil
}
// SafeString converts the Domain to a non-punycode string, falling back to the punycode string if conversion fails.
func (d Domain) SafeString() string {
str, err := d.String()
if err != nil {
return string(d)
}
return str
}
// PunycodeString returns the punycode representation of the Domain.
// This should only be used if a punycode domain is expected but only a string is supported.
func (d Domain) PunycodeString() string {
return string(d)
}
// FromString creates a Domain from a string, converting it to punycode.
func FromString(s string) (Domain, error) {
ascii, err := idna.ToASCII(s)
if err != nil {
return "", err
}
return Domain(strings.ToLower(ascii)), nil
}

View File

@@ -0,0 +1 @@
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=

View File

@@ -0,0 +1,108 @@
package domain
import (
"sort"
"strings"
)
// List is a slice of punycode-encoded domain strings.
type List []Domain
// ToStringList converts a List to a slice of string.
func (d List) ToStringList() ([]string, error) {
var list []string
for _, domain := range d {
s, err := domain.String()
if err != nil {
return nil, err
}
list = append(list, s)
}
return list, nil
}
// ToPunycodeList converts the List to a slice of Punycode-encoded domain strings.
func (d List) ToPunycodeList() []string {
var list []string
for _, domain := range d {
list = append(list, string(domain))
}
return list
}
// ToSafeStringList converts the List to a slice of non-punycode strings.
// If a domain cannot be converted, the original string is used.
func (d List) ToSafeStringList() []string {
var list []string
for _, domain := range d {
list = append(list, domain.SafeString())
}
return list
}
// String converts List to a comma-separated string.
func (d List) String() (string, error) {
list, err := d.ToStringList()
if err != nil {
return "", err
}
return strings.Join(list, ", "), nil
}
// SafeString converts List to a comma-separated non-punycode string.
// If a domain cannot be converted, the original string is used.
func (d List) SafeString() string {
str, err := d.String()
if err != nil {
return d.PunycodeString()
}
return str
}
// PunycodeString converts the List to a comma-separated string of Punycode-encoded domains.
func (d List) PunycodeString() string {
return strings.Join(d.ToPunycodeList(), ", ")
}
func (d List) Equal(domains List) bool {
if len(d) != len(domains) {
return false
}
sort.Slice(d, func(i, j int) bool {
return d[i] < d[j]
})
sort.Slice(domains, func(i, j int) bool {
return domains[i] < domains[j]
})
for i, domain := range d {
if domain != domains[i] {
return false
}
}
return true
}
// FromStringList creates a DomainList from a slice of string.
func FromStringList(s []string) (List, error) {
var dl List
for _, domain := range s {
d, err := FromString(domain)
if err != nil {
return nil, err
}
dl = append(dl, d)
}
return dl, nil
}
// FromPunycodeList creates a List from a slice of Punycode-encoded domain strings.
func FromPunycodeList(s []string) List {
var dl List
for _, domain := range s {
dl = append(dl, Domain(strings.ToLower(domain)))
}
return dl
}

View File

@@ -0,0 +1,49 @@
package domain
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_EqualReturnsTrueForIdenticalLists(t *testing.T) {
list1 := List{"domain1", "domain2", "domain3"}
list2 := List{"domain1", "domain2", "domain3"}
assert.True(t, list1.Equal(list2))
}
func Test_EqualReturnsFalseForDifferentLengths(t *testing.T) {
list1 := List{"domain1", "domain2"}
list2 := List{"domain1", "domain2", "domain3"}
assert.False(t, list1.Equal(list2))
}
func Test_EqualReturnsFalseForDifferentElements(t *testing.T) {
list1 := List{"domain1", "domain2", "domain3"}
list2 := List{"domain1", "domain4", "domain3"}
assert.False(t, list1.Equal(list2))
}
func Test_EqualReturnsTrueForUnsortedIdenticalLists(t *testing.T) {
list1 := List{"domain3", "domain1", "domain2"}
list2 := List{"domain1", "domain2", "domain3"}
assert.True(t, list1.Equal(list2))
}
func Test_EqualReturnsFalseForEmptyAndNonEmptyList(t *testing.T) {
list1 := List{}
list2 := List{"domain1"}
assert.False(t, list1.Equal(list2))
}
func Test_EqualReturnsTrueForBothEmptyLists(t *testing.T) {
list1 := List{}
list2 := List{}
assert.True(t, list1.Equal(list2))
}

View File

@@ -0,0 +1,56 @@
package domain
import (
"fmt"
"regexp"
"strings"
)
const maxDomains = 32
var domainRegex = regexp.MustCompile(`^(?:\*\.)?(?:(?:xn--)?[a-zA-Z0-9_](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?\.)*(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$`)
// ValidateDomains checks if each domain in the list is valid and returns a punycode-encoded DomainList.
func ValidateDomains(domains []string) (List, error) {
if len(domains) == 0 {
return nil, fmt.Errorf("domains list is empty")
}
if len(domains) > maxDomains {
return nil, fmt.Errorf("domains list exceeds maximum allowed domains: %d", maxDomains)
}
var domainList List
for _, d := range domains {
// handles length and idna conversion
punycode, err := FromString(d)
if err != nil {
return domainList, fmt.Errorf("convert domain to punycode: %s: %w", d, err)
}
if !domainRegex.MatchString(string(punycode)) {
return domainList, fmt.Errorf("invalid domain format: %s", d)
}
domainList = append(domainList, punycode)
}
return domainList, nil
}
// ValidateDomainsList checks if each domain in the list is valid
func ValidateDomainsList(domains []string) error {
if len(domains) == 0 {
return nil
}
if len(domains) > maxDomains {
return fmt.Errorf("domains list exceeds maximum allowed domains: %d", maxDomains)
}
for _, d := range domains {
d := strings.ToLower(d)
if !domainRegex.MatchString(d) {
return fmt.Errorf("invalid domain format: %s", d)
}
}
return nil
}

View File

@@ -0,0 +1,185 @@
package domain
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestValidateDomains(t *testing.T) {
tests := []struct {
name string
domains []string
expected List
wantErr bool
}{
{
name: "Empty list",
domains: nil,
expected: nil,
wantErr: true,
},
{
name: "Valid ASCII domain",
domains: []string{"sub.ex-ample.com"},
expected: List{"sub.ex-ample.com"},
wantErr: false,
},
{
name: "Valid Unicode domain",
domains: []string{"münchen.de"},
expected: List{"xn--mnchen-3ya.de"},
wantErr: false,
},
{
name: "Valid Unicode, all labels",
domains: []string{"中国.中国.中国"},
expected: List{"xn--fiqs8s.xn--fiqs8s.xn--fiqs8s"},
wantErr: false,
},
{
name: "With underscores",
domains: []string{"_jabber._tcp.gmail.com"},
expected: List{"_jabber._tcp.gmail.com"},
wantErr: false,
},
{
name: "Invalid domain format",
domains: []string{"-example.com"},
expected: nil,
wantErr: true,
},
{
name: "Invalid domain format 2",
domains: []string{"example.com-"},
expected: nil,
wantErr: true,
},
{
name: "Multiple domains valid and invalid",
domains: []string{"google.com", "invalid,nbdomain.com", "münchen.de"},
expected: List{"google.com"},
wantErr: true,
},
{
name: "Valid wildcard domain",
domains: []string{"*.example.com"},
expected: List{"*.example.com"},
wantErr: false,
},
{
name: "Wildcard with dot domain",
domains: []string{".*.example.com"},
expected: nil,
wantErr: true,
},
{
name: "Wildcard with dot domain",
domains: []string{".*.example.com"},
expected: nil,
wantErr: true,
},
{
name: "Invalid wildcard domain",
domains: []string{"a.*.example.com"},
expected: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ValidateDomains(tt.domains)
assert.Equal(t, tt.wantErr, err != nil)
assert.Equal(t, got, tt.expected)
})
}
}
func TestValidateDomainsList(t *testing.T) {
validDomains := make([]string, maxDomains)
for i := range maxDomains {
validDomains[i] = fmt.Sprintf("example%d.com", i)
}
tests := []struct {
name string
domains []string
wantErr bool
}{
{
name: "Empty list",
domains: nil,
wantErr: false,
},
{
name: "Single valid ASCII domain",
domains: []string{"sub.ex-ample.com"},
wantErr: false,
},
{
name: "Underscores in labels",
domains: []string{"_jabber._tcp.gmail.com"},
wantErr: false,
},
{
// Unlike ValidateDomains (which converts to punycode),
// ValidateDomainsStrSlice will fail on non-ASCII domain chars.
name: "Unicode domain fails (no punycode conversion)",
domains: []string{"münchen.de"},
wantErr: true,
},
{
name: "Invalid domain format - leading dash",
domains: []string{"-example.com"},
wantErr: true,
},
{
name: "Invalid domain format - trailing dash",
domains: []string{"example-.com"},
wantErr: true,
},
{
name: "Multiple domains with a valid one, then invalid",
domains: []string{"google.com", "invalid_domain.com-"},
wantErr: true,
},
{
name: "Valid wildcard domain",
domains: []string{"*.example.com"},
wantErr: false,
},
{
name: "Wildcard with leading dot - invalid",
domains: []string{".*.example.com"},
wantErr: true,
},
{
name: "Invalid wildcard with multiple asterisks",
domains: []string{"a.*.example.com"},
wantErr: true,
},
{
name: "Exactly maxDomains items (valid)",
domains: validDomains,
wantErr: false,
},
{
name: "Exceeds maxDomains items",
domains: append(validDomains, "extra.com"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateDomainsList(tt.domains)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}