Browse Source

ipn/{ipnauth, ipnserver}: extend the ipnauth.Actor interface with a CheckProfileAccess method

The implementations define it to verify whether the actor has the requested access to a login profile.

Updates #14823

Signed-off-by: Nick Khyl <[email protected]>
Nick Khyl 1 year ago
parent
commit
081595de63
4 changed files with 27 additions and 1 deletions
  1. 8 0
      ipn/ipnauth/access.go
  2. 4 0
      ipn/ipnauth/actor.go
  3. 7 1
      ipn/ipnauth/test_actor.go
  4. 8 0
      ipn/ipnserver/actor.go

+ 8 - 0
ipn/ipnauth/access.go

@@ -0,0 +1,8 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+package ipnauth
+
+// ProfileAccess is a bitmask representing the requested, required, or granted
+// access rights to an [ipn.LoginProfile].
+type ProfileAccess uint32

+ 4 - 0
ipn/ipnauth/actor.go

@@ -27,6 +27,10 @@ type Actor interface {
 	// a connected LocalAPI client. Otherwise, it returns a zero value and false.
 	ClientID() (_ ClientID, ok bool)
 
+	// CheckProfileAccess checks whether the actor has the requested access rights
+	// to the specified Tailscale profile. It returns an error if the access is denied.
+	CheckProfileAccess(profile ipn.LoginProfileView, requestedAccess ProfileAccess) error
+
 	// IsLocalSystem reports whether the actor is the Windows' Local System account.
 	//
 	// Deprecated: this method exists for compatibility with the current (as of 2024-08-27)

+ 7 - 1
ipn/ipnauth/test_actor.go

@@ -4,6 +4,8 @@
 package ipnauth
 
 import (
+	"errors"
+
 	"tailscale.com/ipn"
 )
 
@@ -17,7 +19,6 @@ type TestActor struct {
 	CID         ClientID          // non-zero if the actor represents a connected LocalAPI client
 	LocalSystem bool              // whether the actor represents the special Local System account on Windows
 	LocalAdmin  bool              // whether the actor has local admin access
-
 }
 
 // UserID implements [Actor].
@@ -29,6 +30,11 @@ func (a *TestActor) Username() (string, error) { return a.Name, a.NameErr }
 // ClientID implements [Actor].
 func (a *TestActor) ClientID() (_ ClientID, ok bool) { return a.CID, a.CID != NoClientID }
 
+// CheckProfileAccess implements [Actor].
+func (a *TestActor) CheckProfileAccess(profile ipn.LoginProfileView, _ ProfileAccess) error {
+	return errors.New("profile access denied")
+}
+
 // IsLocalSystem implements [Actor].
 func (a *TestActor) IsLocalSystem() bool { return a.LocalSystem }
 

+ 8 - 0
ipn/ipnserver/actor.go

@@ -58,6 +58,14 @@ func newActor(logf logger.Logf, c net.Conn) (*actor, error) {
 	return &actor{logf: logf, ci: ci, clientID: clientID, isLocalSystem: connIsLocalSystem(ci)}, nil
 }
 
+// CheckProfileAccess implements [ipnauth.Actor].
+func (a *actor) CheckProfileAccess(profile ipn.LoginProfileView, requestedAccess ipnauth.ProfileAccess) error {
+	if profile.LocalUserID() != a.UserID() {
+		return errors.New("the target profile does not belong to the user")
+	}
+	return errors.New("the requested operation is not allowed")
+}
+
 // IsLocalSystem implements [ipnauth.Actor].
 func (a *actor) IsLocalSystem() bool {
 	return a.isLocalSystem