ipn/ipnlocal,others: add tailnet display name to user profile

This PR adds the new editable display name for a tailnet which
will be the preferred display in client UIs and CLIs when
fast user switching between profiles.

Updates #9286

Signed-off-by: Marwan Sulaiman <marwan@tailscale.com>
marwan/displayname
Marwan Sulaiman 6 months ago
parent b819f66eb1
commit 8092eaed80

@ -59,9 +59,13 @@ func listProfiles(ctx context.Context) error {
if prof.ID == curP.ID {
name += "*"
}
tailnet := prof.NetworkProfile.DomainName
if prof.NetworkProfile.DisplayName != "" {
tailnet = prof.NetworkProfile.DisplayName
}
printRow(
string(prof.ID),
prof.NetworkProfile.DomainName,
tailnet,
name,
)
}

@ -83,6 +83,7 @@ type mapSession struct {
lastSSHPolicy *tailcfg.SSHPolicy
collectServices bool
lastDomain string
lastDisplayName string
lastDomainAuditLogID string
lastHealth []string
lastPopBrowserURL string
@ -312,6 +313,9 @@ func (ms *mapSession) updateStateFromResponse(resp *tailcfg.MapResponse) {
if resp.Domain != "" {
ms.lastDomain = resp.Domain
}
if resp.DisplayName != "" {
ms.lastDisplayName = resp.DisplayName
}
if resp.DomainDataPlaneAuditLogID != "" {
ms.lastDomainAuditLogID = resp.DomainDataPlaneAuditLogID
}
@ -756,6 +760,7 @@ func (ms *mapSession) netmap() *netmap.NetworkMap {
Peers: peerViews,
UserProfiles: make(map[tailcfg.UserID]tailcfg.UserProfile),
Domain: ms.lastDomain,
DisplayName: ms.lastDisplayName,
DomainAuditLogID: ms.lastDomainAuditLogID,
DNS: *ms.lastDNSConfig,
PacketFilter: ms.lastParsedPacketFilter,

@ -1119,17 +1119,22 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
// Until recently, we did not store the account's tailnet name. So check if this is the case,
// and backfill it on incoming status update.
if b.pm.requiresBackfill() && st.NetMap != nil && st.NetMap.Domain != "" {
prefsChanged = true
var np ipn.NetworkProfile
if st.NetMap != nil {
np = ipn.NetworkProfile{
MagicDNSName: st.NetMap.MagicDNSSuffix(),
DomainName: st.NetMap.DomainName(),
DisplayName: st.NetMap.GetDisplayName(),
}
if b.pm.requiresBackfill(np) {
prefsChanged = true
}
}
// Perform all mutations of prefs based on the netmap here.
if prefsChanged {
// Prefs will be written out if stale; this is not safe unless locked or cloned.
if err := b.pm.SetPrefs(prefs.View(), ipn.NetworkProfile{
MagicDNSName: st.NetMap.MagicDNSSuffix(),
DomainName: st.NetMap.DomainName(),
}); err != nil {
if err := b.pm.SetPrefs(prefs.View(), np); err != nil {
b.logf("Failed to save new controlclient state: %v", err)
}
}
@ -1188,6 +1193,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
if err := b.pm.SetPrefs(p, ipn.NetworkProfile{
MagicDNSName: st.NetMap.MagicDNSSuffix(),
DomainName: st.NetMap.DomainName(),
DisplayName: st.NetMap.GetDisplayName(),
}); err != nil {
b.logf("Failed to save new controlclient state: %v", err)
}
@ -1618,6 +1624,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
if err := b.pm.SetPrefs(pv, ipn.NetworkProfile{
MagicDNSName: b.netMap.MagicDNSSuffix(),
DomainName: b.netMap.DomainName(),
DisplayName: b.netMap.GetDisplayName(),
}); err != nil {
b.logf("failed to save UpdatePrefs state: %v", err)
}
@ -2527,6 +2534,7 @@ func (b *LocalBackend) migrateStateLocked(prefs *ipn.Prefs) (err error) {
if err := b.pm.SetPrefs(prefs.View(), ipn.NetworkProfile{
MagicDNSName: b.netMap.MagicDNSSuffix(),
DomainName: b.netMap.DomainName(),
DisplayName: b.netMap.GetDisplayName(),
}); err != nil {
return fmt.Errorf("store.WriteState: %v", err)
}
@ -3111,6 +3119,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) ipn
if err := b.pm.SetPrefs(prefs, ipn.NetworkProfile{
MagicDNSName: b.netMap.MagicDNSSuffix(),
DomainName: b.netMap.DomainName(),
DisplayName: b.netMap.GetDisplayName(),
}); err != nil {
b.logf("failed to save new controlclient state: %v", err)
}

@ -581,6 +581,7 @@ func (b *LocalBackend) NetworkLockForceLocalDisable() error {
if err := b.pm.SetPrefs(newPrefs.View(), ipn.NetworkProfile{
MagicDNSName: b.netMap.MagicDNSSuffix(),
DomainName: b.netMap.DomainName(),
DisplayName: b.netMap.GetDisplayName(),
}); err != nil {
return fmt.Errorf("saving prefs: %w", err)
}

@ -608,10 +608,10 @@ func (pm *profileManager) migrateFromLegacyPrefs() error {
return nil
}
func (pm *profileManager) requiresBackfill() bool {
func (pm *profileManager) requiresBackfill(np ipn.NetworkProfile) bool {
return pm != nil &&
pm.currentProfile != nil &&
pm.currentProfile.NetworkProfile.RequiresBackfill()
pm.currentProfile.NetworkProfile.RequiresBackfill(np)
}
var (

@ -795,16 +795,14 @@ type WindowsUserID string
type NetworkProfile struct {
MagicDNSName string
DomainName string
DisplayName string
}
// RequiresBackfill returns whether this object does not have all the data
// expected. This is because this struct is a later addition to LoginProfile and
// this method can be checked to see if it's been backfilled to the current
// expectation or not. Note that for now, it just checks if the struct is empty.
// In the future, if we have new optional fields, this method can be changed to
// do more explicit checks to return whether it's apt for a backfill or not.
func (n NetworkProfile) RequiresBackfill() bool {
return n == NetworkProfile{}
// RequiresBackfill returns whether the new NetworkProfile has changed
// from the already existing one and therefore requires updating the
// local storage.
func (n NetworkProfile) RequiresBackfill(np NetworkProfile) bool {
return n != np
}
// LoginProfile represents a single login profile as managed

@ -1782,6 +1782,9 @@ type MapResponse struct {
// If empty, the value is unchanged.
Domain string `json:",omitempty"`
// DisplayName is the user editable version of the Domain.
DisplayName string `json:",omitempty"`
// CollectServices reports whether this node's Tailnet has
// requested that info about services be included in HostInfo.
// If unset, the most recent non-empty MapResponse value in

@ -68,6 +68,8 @@ type NetworkMap struct {
// Domain is the current Tailnet name.
Domain string
// DisplayName is the user editable version of the Domain.
DisplayName string `json:",omitempty"`
// DomainAuditLogID is an audit log ID provided by control and
// only populated if the domain opts into data-plane audit logging.
@ -187,6 +189,15 @@ func (nm *NetworkMap) DomainName() string {
return nm.Domain
}
// DisplayName returns the name of the NetworkMap's current tailnet user
// editable display name. If the map is nil, it returns an empty string.
func (nm *NetworkMap) GetDisplayName() string {
if nm == nil {
return ""
}
return nm.DisplayName
}
// SelfCapabilities returns SelfNode.Capabilities if nm and nm.SelfNode are
// non-nil. This is a method so we can use it in envknob/logknob without a
// circular dependency.

@ -45,7 +45,7 @@ func TestMapResponseContainsNonPatchFields(t *testing.T) {
var want bool
switch f.Name {
case "MapSessionHandle", "Seq", "KeepAlive", "PingRequest", "PopBrowserURL", "ControlTime":
case "MapSessionHandle", "Seq", "KeepAlive", "PingRequest", "PopBrowserURL", "ControlTime", "DisplayName":
// There are meta fields that apply to all MapResponse values.
// They should be ignored.
want = false

Loading…
Cancel
Save