internal/policy/names.go

39535a8e16d8dfc0189bce59511cfb0d7d019fb5
gitbay/internal/policy/names.go history · blame · raw

67 lines · 2258 bytes

 1// Package policy holds access-control and naming rules.
 2package policy
 3
 4import (
 5	"fmt"
 6	"regexp"
 7)
 8
 9// reservedNames are forbidden as usernames and org names because they are, or
10// will be, top-level web routes (the UI serves /<owner>/<name>). Any change to
11// the httpd mux's top-level routes must be reflected here; the httpd package
12// asserts this in its tests.
13var reservedNames = map[string]bool{
14	"admin":       true,
15	"api":         true,
16	"archive":     true,
17	"explore":     true,
18	"favicon.svg": true,
19	"gitbay":      true, // vanity go-import path on gitbay.org
20	"gitbay-bot":  true, // authors dependency-update issues
21	"login":       true,
22	"logout":      true,
23	"new":         true,
24	"privacy":     true,
25	"raw":         true,
26	"register":    true,
27	"settings":    true,
28	"static":      true,
29}
30
31// namePat matches valid user, org, and repo names: lowercase alphanumerics,
32// dot, dash, underscore; must start with an alphanumeric. Dots are further
33// restricted by ValidateName to avoid "." / ".." and ".git" suffixes.
34var namePat = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,62}$`)
35
36// ValidateOwnerName checks a username or org name.
37func ValidateOwnerName(name string) error {
38	if err := ValidateName(name); err != nil {
39		return err
40	}
41	if reservedNames[name] {
42		return fmt.Errorf("name %q is reserved", name)
43	}
44	return nil
45}
46
47// ValidateName checks a repo name (reserved words are allowed for repos;
48// routes are namespaced under the owner).
49func ValidateName(name string) error {
50	if !namePat.MatchString(name) {
51		return fmt.Errorf("invalid name %q: lowercase letters, digits, '.', '-', '_' only; must start with a letter or digit; max 63 chars", name)
52	}
53	if name == "." || name == ".." {
54		return fmt.Errorf("invalid name %q", name)
55	}
56	if len(name) > 4 && name[len(name)-4:] == ".git" {
57		return fmt.Errorf("invalid name %q: must not end in .git", name)
58	}
59	if len(name) > 5 && name[len(name)-5:] == ".wiki" {
60		return fmt.Errorf("invalid name %q: .wiki names are reserved for wiki companion repositories", name)
61	}
62	return nil
63}
64
65// Reserved reports whether name is a reserved route word. Exported so the
66// httpd tests can assert route/reserved-list agreement.
67func Reserved(name string) bool { return reservedNames[name] }