From fa2fbaf3aa6663bd658b0d1fb9b6d58b13a6f00f Mon Sep 17 00:00:00 2001 From: Maisem Ali Date: Tue, 26 Jul 2022 20:48:38 -0700 Subject: [PATCH] tailcfg: add views for SSHRule and SSHPrincipal Signed-off-by: Maisem Ali --- tailcfg/tailcfg.go | 2 +- tailcfg/tailcfg_clone.go | 78 +++++++++++++++++++++- tailcfg/tailcfg_view.go | 136 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 213 insertions(+), 3 deletions(-) diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index b37293325..e81082f3d 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -4,7 +4,7 @@ package tailcfg -//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode --clonefunc +//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal --clonefunc import ( "encoding/hex" diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 1523f1766..10201c7b6 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -326,9 +326,67 @@ var _DERPNodeCloneNeedsRegeneration = DERPNode(struct { STUNTestIP string }{}) +// Clone makes a deep copy of SSHRule. +// The result aliases no memory with the original. +func (src *SSHRule) Clone() *SSHRule { + if src == nil { + return nil + } + dst := new(SSHRule) + *dst = *src + if dst.RuleExpires != nil { + dst.RuleExpires = new(time.Time) + *dst.RuleExpires = *src.RuleExpires + } + dst.Principals = make([]*SSHPrincipal, len(src.Principals)) + for i := range dst.Principals { + dst.Principals[i] = src.Principals[i].Clone() + } + if dst.SSHUsers != nil { + dst.SSHUsers = map[string]string{} + for k, v := range src.SSHUsers { + dst.SSHUsers[k] = v + } + } + if dst.Action != nil { + dst.Action = new(SSHAction) + *dst.Action = *src.Action + } + return dst +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _SSHRuleCloneNeedsRegeneration = SSHRule(struct { + RuleExpires *time.Time + Principals []*SSHPrincipal + SSHUsers map[string]string + Action *SSHAction +}{}) + +// Clone makes a deep copy of SSHPrincipal. +// The result aliases no memory with the original. +func (src *SSHPrincipal) Clone() *SSHPrincipal { + if src == nil { + return nil + } + dst := new(SSHPrincipal) + *dst = *src + dst.PubKeys = append(src.PubKeys[:0:0], src.PubKeys...) + return dst +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _SSHPrincipalCloneNeedsRegeneration = SSHPrincipal(struct { + Node StableNodeID + NodeIP string + UserLogin string + Any bool + PubKeys []string +}{}) + // Clone duplicates src into dst and reports whether it succeeded. // To succeed, must be of types <*T, *T> or <*T, **T>, -// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode. +// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal. func Clone(dst, src any) bool { switch src := src.(type) { case *User: @@ -421,6 +479,24 @@ func Clone(dst, src any) bool { *dst = src.Clone() return true } + case *SSHRule: + switch dst := dst.(type) { + case *SSHRule: + *dst = *src.Clone() + return true + case **SSHRule: + *dst = src.Clone() + return true + } + case *SSHPrincipal: + switch dst := dst.(type) { + case *SSHPrincipal: + *dst = *src.Clone() + return true + case **SSHPrincipal: + *dst = src.Clone() + return true + } } return false } diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index d8c3e21a5..ec685b1ad 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -19,7 +19,7 @@ import ( "tailscale.com/types/views" ) -//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode +//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,DERPRegion,DERPMap,DERPNode,SSHRule,SSHPrincipal // View returns a readonly view of User. func (p *User) View() UserView { @@ -763,3 +763,137 @@ var _DERPNodeViewNeedsRegeneration = DERPNode(struct { InsecureForTests bool STUNTestIP string }{}) + +// View returns a readonly view of SSHRule. +func (p *SSHRule) View() SSHRuleView { + return SSHRuleView{ж: p} +} + +// SSHRuleView provides a read-only view over SSHRule. +// +// Its methods should only be called if `Valid()` returns true. +type SSHRuleView struct { + // ж is the underlying mutable value, named with a hard-to-type + // character that looks pointy like a pointer. + // It is named distinctively to make you think of how dangerous it is to escape + // to callers. You must not let callers be able to mutate it. + ж *SSHRule +} + +// Valid reports whether underlying value is non-nil. +func (v SSHRuleView) Valid() bool { return v.ж != nil } + +// AsStruct returns a clone of the underlying value which aliases no memory with +// the original. +func (v SSHRuleView) AsStruct() *SSHRule { + if v.ж == nil { + return nil + } + return v.ж.Clone() +} + +func (v SSHRuleView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +func (v *SSHRuleView) UnmarshalJSON(b []byte) error { + if v.ж != nil { + return errors.New("already initialized") + } + if len(b) == 0 { + return nil + } + var x SSHRule + if err := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v SSHRuleView) RuleExpires() *time.Time { + if v.ж.RuleExpires == nil { + return nil + } + x := *v.ж.RuleExpires + return &x +} + +func (v SSHRuleView) Principals() views.SliceView[*SSHPrincipal, SSHPrincipalView] { + return views.SliceOfViews[*SSHPrincipal, SSHPrincipalView](v.ж.Principals) +} + +func (v SSHRuleView) SSHUsers() views.Map[string, string] { return views.MapOf(v.ж.SSHUsers) } +func (v SSHRuleView) Action() *SSHAction { + if v.ж.Action == nil { + return nil + } + x := *v.ж.Action + return &x +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _SSHRuleViewNeedsRegeneration = SSHRule(struct { + RuleExpires *time.Time + Principals []*SSHPrincipal + SSHUsers map[string]string + Action *SSHAction +}{}) + +// View returns a readonly view of SSHPrincipal. +func (p *SSHPrincipal) View() SSHPrincipalView { + return SSHPrincipalView{ж: p} +} + +// SSHPrincipalView provides a read-only view over SSHPrincipal. +// +// Its methods should only be called if `Valid()` returns true. +type SSHPrincipalView struct { + // ж is the underlying mutable value, named with a hard-to-type + // character that looks pointy like a pointer. + // It is named distinctively to make you think of how dangerous it is to escape + // to callers. You must not let callers be able to mutate it. + ж *SSHPrincipal +} + +// Valid reports whether underlying value is non-nil. +func (v SSHPrincipalView) Valid() bool { return v.ж != nil } + +// AsStruct returns a clone of the underlying value which aliases no memory with +// the original. +func (v SSHPrincipalView) AsStruct() *SSHPrincipal { + if v.ж == nil { + return nil + } + return v.ж.Clone() +} + +func (v SSHPrincipalView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +func (v *SSHPrincipalView) UnmarshalJSON(b []byte) error { + if v.ж != nil { + return errors.New("already initialized") + } + if len(b) == 0 { + return nil + } + var x SSHPrincipal + if err := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v SSHPrincipalView) Node() StableNodeID { return v.ж.Node } +func (v SSHPrincipalView) NodeIP() string { return v.ж.NodeIP } +func (v SSHPrincipalView) UserLogin() string { return v.ж.UserLogin } +func (v SSHPrincipalView) Any() bool { return v.ж.Any } +func (v SSHPrincipalView) PubKeys() views.Slice[string] { return views.SliceOf(v.ж.PubKeys) } + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _SSHPrincipalViewNeedsRegeneration = SSHPrincipal(struct { + Node StableNodeID + NodeIP string + UserLogin string + Any bool + PubKeys []string +}{})