Skip to content

Commit 61a8674

Browse files
committed
allow links to be saved without known owner
If the current user can't be determined (either because of a legitimate error within the localapi client, or the user is coming through a subnet router and doesn't have a Tailscale IP address), and the -allow-unknown-users flag is set, then go ahead and save new links without an owner. By saving links without an owner, these unknown users can continue to modify the link, and actual Tailscale users can take ownership. Once the link is owned, it can no longer be modified by anyone other than the owner. Links that use the current user by having `{{ .User }}` in their long URL cannot be resolved by unknown users and will return an error. Fixes #60 Signed-off-by: Will Norris <[email protected]>
1 parent c7ac33d commit 61a8674

File tree

2 files changed

+55
-11
lines changed

2 files changed

+55
-11
lines changed

golink.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var (
4848
snapshot = flag.String("snapshot", "", "file path of snapshot file")
4949
hostname = flag.String("hostname", defaultHostname, "service name")
5050
resolveFromBackup = flag.String("resolve-from-backup", "", "resolve a link from snapshot file and exit")
51+
allowUnknownUsers = flag.Bool("allow-unknown-users", false, "allow unknown users to save links")
5152
)
5253

5354
var stats struct {
@@ -391,11 +392,14 @@ func serveGo(w http.ResponseWriter, r *http.Request) {
391392
stats.dirty[link.Short]++
392393
stats.mu.Unlock()
393394

394-
currentUser, _ := currentUser(r)
395-
396-
target, err := expandLink(link.Long, expandEnv{Now: time.Now().UTC(), Path: remainder, User: currentUser})
395+
login, _ := currentUser(r)
396+
target, err := expandLink(link.Long, expandEnv{Now: time.Now().UTC(), Path: remainder, user: login})
397397
if err != nil {
398398
log.Printf("expanding %q: %v", link.Long, err)
399+
if errors.Is(err, errNoUser) {
400+
http.Error(w, "link requires a valid user", http.StatusUnauthorized)
401+
return
402+
}
399403
http.Error(w, err.Error(), http.StatusInternalServerError)
400404
return
401405
}
@@ -464,9 +468,19 @@ type expandEnv struct {
464468
// "http://go/who/amelie", Path is "amelie".
465469
Path string
466470

467-
// User is the current user, if any.
471+
// user is the current user, if any.
468472
// For example, "[email protected]" or "foo@github".
469-
User string
473+
user string
474+
}
475+
476+
var errNoUser = errors.New("no user")
477+
478+
// User returns the current user, or errNoUser if there is no user.
479+
func (e expandEnv) User() (string, error) {
480+
if e.user == "" {
481+
return "", errNoUser
482+
}
483+
return e.user, nil
470484
}
471485

472486
var expandFuncMap = texttemplate.FuncMap{
@@ -511,12 +525,18 @@ func devMode() bool { return *dev != "" }
511525
// currentUser returns the Tailscale user associated with the request.
512526
// In most cases, this will be the user that owns the device that made the request.
513527
// For tagged devices, the value "tagged-devices" is returned.
528+
// If the user can't be determined (such as requests coming through a subnet router),
529+
// an error is returned unless the -allow-unknown-users flag is set.
514530
var currentUser = func(r *http.Request) (string, error) {
515531
if devMode() {
516532
return "[email protected]", nil
517533
}
518534
whois, err := localClient.WhoIs(r.Context(), r.RemoteAddr)
519535
if err != nil {
536+
if *allowUnknownUsers {
537+
// Don't report the error if we are allowing unknown users.
538+
return "", nil
539+
}
520540
return "", err
521541
}
522542
return whois.UserProfile.LoginName, nil

golink_test.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ func TestServeGo(t *testing.T) {
6666
link: "/invalid-var",
6767
wantStatus: http.StatusInternalServerError,
6868
},
69+
{
70+
name: "user link, anonymous request",
71+
link: "/me",
72+
currentUser: func(*http.Request) (string, error) { return "", nil },
73+
wantStatus: http.StatusUnauthorized,
74+
},
6975
}
7076

7177
for _, tt := range tests {
@@ -100,11 +106,12 @@ func TestServeSave(t *testing.T) {
100106
}
101107

102108
tests := []struct {
103-
name string
104-
short string
105-
long string
106-
currentUser func(*http.Request) (string, error)
107-
wantStatus int
109+
name string
110+
short string
111+
long string
112+
allowUnknownUsers bool
113+
currentUser func(*http.Request) (string, error)
114+
wantStatus int
108115
}{
109116
{
110117
name: "missing short",
@@ -138,6 +145,14 @@ func TestServeSave(t *testing.T) {
138145
currentUser: func(*http.Request) (string, error) { return "", errors.New("") },
139146
wantStatus: http.StatusInternalServerError,
140147
},
148+
{
149+
name: "allow unknown users",
150+
short: "who2",
151+
long: "http://who/",
152+
allowUnknownUsers: true,
153+
currentUser: func(*http.Request) (string, error) { return "", nil },
154+
wantStatus: http.StatusOK,
155+
},
141156
}
142157

143158
for _, tt := range tests {
@@ -150,6 +165,10 @@ func TestServeSave(t *testing.T) {
150165
})
151166
}
152167

168+
oldAllowUnknownUsers := *allowUnknownUsers
169+
*allowUnknownUsers = tt.allowUnknownUsers
170+
t.Cleanup(func() { *allowUnknownUsers = oldAllowUnknownUsers })
171+
153172
r := httptest.NewRequest("POST", "/", strings.NewReader(url.Values{
154173
"short": {tt.short},
155174
"long": {tt.long},
@@ -283,6 +302,11 @@ func TestExpandLink(t *testing.T) {
283302
284303
want: "http://host.com/[email protected]",
285304
},
305+
{
306+
name: "var-expansions-no-user",
307+
long: `http://host.com/{{.User}}`,
308+
wantErr: true,
309+
},
286310
{
287311
name: "unknown-field",
288312
long: `http://host.com/{{.Foo}}`,
@@ -320,7 +344,7 @@ func TestExpandLink(t *testing.T) {
320344
}
321345
for _, tt := range tests {
322346
t.Run(tt.name, func(t *testing.T) {
323-
got, err := expandLink(tt.long, expandEnv{Now: tt.now, Path: tt.remainder, User: tt.user})
347+
got, err := expandLink(tt.long, expandEnv{Now: tt.now, Path: tt.remainder, user: tt.user})
324348
if (err != nil) != tt.wantErr {
325349
t.Fatalf("expandLink(%q) returned error %v; want %v", tt.long, err, tt.wantErr)
326350
}

0 commit comments

Comments
 (0)