internal/policy/names.go

0caaaedf8fa3d930b6b4fb83e249b1e0e3265c0a
gitbay/internal/policy/names.go history · blame · raw

68 lines · 2225 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	"bookmarks":     true,
18	"explore":       true,
19	"favicon.svg":   true,
20	"gitbay":        true, // vanity go-import path on gitbay.org
21	"gitbay-bot":    true, // authors dependency-update issues
22	"healthz":       true,
23	"login":         true,
24	"logout":        true,
25	"new":           true,
26	"notifications": true,
27	"privacy":       true,
28	"raw":           true,
29	"register":      true,
30	"search":        true,
31	"settings":      true,
32	"static":        true,
33}
34
35// namePat matches valid user, org, and repo names: lowercase alphanumerics,
36// dot, dash, underscore; must start with an alphanumeric. Dots are further
37// restricted by ValidateName to avoid "." / ".." and ".git" suffixes.
38var namePat = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,62}$`)
39
40// ValidateOwnerName checks a username or org name.
41func ValidateOwnerName(name string) error {
42	if err := ValidateName(name); err != nil {
43		return err
44	}
45	if reservedNames[name] {
46		return fmt.Errorf("name %q is reserved", name)
47	}
48	return nil
49}
50
51// ValidateName checks a repo name (reserved words are allowed for repos;
52// routes are namespaced under the owner).
53func ValidateName(name string) error {
54	if !namePat.MatchString(name) {
55		return fmt.Errorf("invalid name %q: lowercase letters, digits, '.', '-', '_' only; must start with a letter or digit; max 63 chars", name)
56	}
57	if name == "." || name == ".." {
58		return fmt.Errorf("invalid name %q", name)
59	}
60	if len(name) > 4 && name[len(name)-4:] == ".git" {
61		return fmt.Errorf("invalid name %q: must not end in .git", name)
62	}
63	return nil
64}
65
66// Reserved reports whether name is a reserved route word. Exported so the
67// httpd tests can assert route/reserved-list agreement.
68func Reserved(name string) bool { return reservedNames[name] }