mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-17 22:12:03 +09:00
types: drop the proto builders
Remove the Proto() methods and RegisterMethodToV1Enum; the v1 API builds responses from the state views directly now.
This commit is contained in:
@@ -96,7 +96,7 @@ func TestPreAuthKeyACLTags(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, listedPaks, 1)
|
require.Len(t, listedPaks, 1)
|
||||||
|
|
||||||
gotTags := listedPaks[0].Proto().GetAclTags()
|
gotTags := listedPaks[0].Tags
|
||||||
slices.Sort(gotTags)
|
slices.Sort(gotTags)
|
||||||
assert.Equal(t, expectedTags, gotTags)
|
assert.Equal(t, expectedTags, gotTags)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ package types
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
|
||||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewAPIKeyPrefixLength is the length of the prefix for new API keys.
|
// NewAPIKeyPrefixLength is the length of the prefix for new API keys.
|
||||||
@@ -24,28 +22,6 @@ type APIKey struct {
|
|||||||
LastSeen *time.Time
|
LastSeen *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (key *APIKey) Proto() *v1.ApiKey {
|
|
||||||
protoKey := v1.ApiKey{
|
|
||||||
Id: key.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
protoKey.Prefix = key.maskedPrefix()
|
|
||||||
|
|
||||||
if key.Expiration != nil {
|
|
||||||
protoKey.Expiration = timestamppb.New(*key.Expiration)
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.CreatedAt != nil {
|
|
||||||
protoKey.CreatedAt = timestamppb.New(*key.CreatedAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.LastSeen != nil {
|
|
||||||
protoKey.LastSeen = timestamppb.New(*key.LastSeen)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &protoKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// maskedPrefix returns the API key prefix in masked format for safe logging.
|
// maskedPrefix returns the API key prefix in masked format for safe logging.
|
||||||
// SECURITY: Never log the full key or hash, only the masked prefix.
|
// SECURITY: Never log the full key or hash, only the masked prefix.
|
||||||
func (k *APIKey) maskedPrefix() string {
|
func (k *APIKey) maskedPrefix() string {
|
||||||
|
|||||||
@@ -10,13 +10,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
|
||||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@@ -483,55 +481,6 @@ func (nodes Nodes) ContainsNodeKey(nodeKey key.NodePublic) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) Proto() *v1.Node {
|
|
||||||
nodeProto := &v1.Node{
|
|
||||||
Id: uint64(node.ID),
|
|
||||||
MachineKey: node.MachineKey.String(),
|
|
||||||
|
|
||||||
NodeKey: node.NodeKey.String(),
|
|
||||||
DiscoKey: node.DiscoKey.String(),
|
|
||||||
|
|
||||||
// TODO(kradalby): replace list with v4, v6 field?
|
|
||||||
IpAddresses: node.IPsAsString(),
|
|
||||||
Name: node.Hostname,
|
|
||||||
GivenName: node.GivenName,
|
|
||||||
User: nil, // Will be set below based on node type
|
|
||||||
Tags: node.Tags,
|
|
||||||
Online: node.IsOnline != nil && *node.IsOnline,
|
|
||||||
|
|
||||||
// Only ApprovedRoutes and AvailableRoutes is set here. SubnetRoutes has
|
|
||||||
// to be populated manually with PrimaryRoute, to ensure it includes the
|
|
||||||
// routes that are actively served from the node.
|
|
||||||
ApprovedRoutes: util.PrefixesToString(node.ApprovedRoutes),
|
|
||||||
AvailableRoutes: util.PrefixesToString(node.AnnouncedRoutes()),
|
|
||||||
|
|
||||||
RegisterMethod: node.RegisterMethodToV1Enum(),
|
|
||||||
|
|
||||||
CreatedAt: timestamppb.New(node.CreatedAt),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set User field based on node ownership
|
|
||||||
// Note: User will be set to [TaggedDevices] in the gRPC layer (grpcv1.go)
|
|
||||||
// for proper [tailcfg.MapResponse] formatting
|
|
||||||
if node.User != nil {
|
|
||||||
nodeProto.User = node.User.Proto()
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.AuthKey != nil {
|
|
||||||
nodeProto.PreAuthKey = node.AuthKey.Proto()
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.LastSeen != nil {
|
|
||||||
nodeProto.LastSeen = timestamppb.New(*node.LastSeen)
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Expiry != nil {
|
|
||||||
nodeProto.Expiry = timestamppb.New(*node.Expiry)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeProto
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) GetFQDN(baseDomain string) (string, error) {
|
func (node *Node) GetFQDN(baseDomain string) (string, error) {
|
||||||
if node.GivenName == "" {
|
if node.GivenName == "" {
|
||||||
return "", fmt.Errorf("creating valid FQDN: %w", ErrNodeHasNoGivenName)
|
return "", fmt.Errorf("creating valid FQDN: %w", ErrNodeHasNoGivenName)
|
||||||
@@ -684,19 +633,6 @@ func EndpointsChanged(oldEndpoints, newEndpoints []netip.AddrPort) bool {
|
|||||||
return !equalUnordered(oldEndpoints, newEndpoints, netip.AddrPort.Compare)
|
return !equalUnordered(oldEndpoints, newEndpoints, netip.AddrPort.Compare)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) RegisterMethodToV1Enum() v1.RegisterMethod {
|
|
||||||
switch node.RegisterMethod {
|
|
||||||
case "authkey":
|
|
||||||
return v1.RegisterMethod_REGISTER_METHOD_AUTH_KEY
|
|
||||||
case "oidc":
|
|
||||||
return v1.RegisterMethod_REGISTER_METHOD_OIDC
|
|
||||||
case "cli":
|
|
||||||
return v1.RegisterMethod_REGISTER_METHOD_CLI
|
|
||||||
default:
|
|
||||||
return v1.RegisterMethod_REGISTER_METHOD_UNSPECIFIED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyPeerChange takes a [tailcfg.PeerChange] struct and updates the node.
|
// ApplyPeerChange takes a [tailcfg.PeerChange] struct and updates the node.
|
||||||
func (node *Node) ApplyPeerChange(change *tailcfg.PeerChange) {
|
func (node *Node) ApplyPeerChange(change *tailcfg.PeerChange) {
|
||||||
if change.Key != nil {
|
if change.Key != nil {
|
||||||
@@ -998,15 +934,6 @@ func (nv NodeView) RequestTags() []string {
|
|||||||
return nv.Hostinfo().RequestTags().AsSlice()
|
return nv.Hostinfo().RequestTags().AsSlice()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proto converts the [NodeView] to a protobuf representation.
|
|
||||||
func (nv NodeView) Proto() *v1.Node {
|
|
||||||
if !nv.Valid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nv.ж.Proto()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasIP reports if a node has a given IP address.
|
// HasIP reports if a node has a given IP address.
|
||||||
func (nv NodeView) HasIP(i netip.Addr) bool {
|
func (nv NodeView) HasIP(i netip.Addr) bool {
|
||||||
if !nv.Valid() {
|
if !nv.Valid() {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
|
||||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
@@ -657,56 +656,6 @@ func TestApplyPeerChange(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeRegisterMethodToV1Enum(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
node Node
|
|
||||||
want v1.RegisterMethod
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "authkey",
|
|
||||||
node: Node{
|
|
||||||
ID: 1,
|
|
||||||
RegisterMethod: util.RegisterMethodAuthKey,
|
|
||||||
},
|
|
||||||
want: v1.RegisterMethod_REGISTER_METHOD_AUTH_KEY,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "oidc",
|
|
||||||
node: Node{
|
|
||||||
ID: 1,
|
|
||||||
RegisterMethod: util.RegisterMethodOIDC,
|
|
||||||
},
|
|
||||||
want: v1.RegisterMethod_REGISTER_METHOD_OIDC,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cli",
|
|
||||||
node: Node{
|
|
||||||
ID: 1,
|
|
||||||
RegisterMethod: util.RegisterMethodCLI,
|
|
||||||
},
|
|
||||||
want: v1.RegisterMethod_REGISTER_METHOD_CLI,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown",
|
|
||||||
node: Node{
|
|
||||||
ID: 0,
|
|
||||||
},
|
|
||||||
want: v1.RegisterMethod_REGISTER_METHOD_UNSPECIFIED,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got := tt.node.RegisterMethodToV1Enum()
|
|
||||||
|
|
||||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
|
||||||
t.Errorf("RegisterMethodToV1Enum() unexpected result (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHasNetworkChanges tests the [NodeView] method for detecting
|
// TestHasNetworkChanges tests the [NodeView] method for detecting
|
||||||
// when a node's network properties have changed.
|
// when a node's network properties have changed.
|
||||||
func TestHasNetworkChanges(t *testing.T) {
|
func TestHasNetworkChanges(t *testing.T) {
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package types
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
|
||||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PAKError string
|
type PAKError string
|
||||||
@@ -56,66 +54,6 @@ type PreAuthKeyNew struct {
|
|||||||
User *User // Can be nil for system-created tagged keys
|
User *User // Can be nil for system-created tagged keys
|
||||||
}
|
}
|
||||||
|
|
||||||
func (key *PreAuthKeyNew) Proto() *v1.PreAuthKey {
|
|
||||||
protoKey := v1.PreAuthKey{
|
|
||||||
Id: key.ID,
|
|
||||||
Key: key.Key,
|
|
||||||
User: nil, // Will be set below if not nil
|
|
||||||
Reusable: key.Reusable,
|
|
||||||
Ephemeral: key.Ephemeral,
|
|
||||||
AclTags: key.Tags,
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.User != nil {
|
|
||||||
protoKey.User = key.User.Proto()
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.Expiration != nil {
|
|
||||||
protoKey.Expiration = timestamppb.New(*key.Expiration)
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.CreatedAt != nil {
|
|
||||||
protoKey.CreatedAt = timestamppb.New(*key.CreatedAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &protoKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (key *PreAuthKey) Proto() *v1.PreAuthKey {
|
|
||||||
protoKey := v1.PreAuthKey{
|
|
||||||
User: nil, // Will be set below if not nil
|
|
||||||
Id: key.ID,
|
|
||||||
Ephemeral: key.Ephemeral,
|
|
||||||
Reusable: key.Reusable,
|
|
||||||
Used: key.Used,
|
|
||||||
AclTags: key.Tags,
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.User != nil {
|
|
||||||
protoKey.User = key.User.Proto()
|
|
||||||
}
|
|
||||||
|
|
||||||
// For new keys (with prefix/hash), show the prefix so users can identify the key
|
|
||||||
// For legacy keys (with plaintext key), show the full key for backwards compatibility
|
|
||||||
if masked := key.maskedPrefix(); masked != "" {
|
|
||||||
protoKey.Key = masked
|
|
||||||
} else if key.Key != "" {
|
|
||||||
// Legacy key - show full key for backwards compatibility
|
|
||||||
// TODO: Consider hiding this in a future major version
|
|
||||||
protoKey.Key = key.Key
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.Expiration != nil {
|
|
||||||
protoKey.Expiration = timestamppb.New(*key.Expiration)
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.CreatedAt != nil {
|
|
||||||
protoKey.CreatedAt = timestamppb.New(*key.CreatedAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &protoKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks if a pre auth key can be used.
|
// Validate checks if a pre auth key can be used.
|
||||||
func (pak *PreAuthKey) Validate() error {
|
func (pak *PreAuthKey) Validate() error {
|
||||||
if pak == nil {
|
if pak == nil {
|
||||||
|
|||||||
@@ -11,12 +11,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
)
|
)
|
||||||
@@ -192,28 +190,6 @@ func (u UserView) TailscaleUserProfile() tailcfg.UserProfile {
|
|||||||
return u.ж.TailscaleUserProfile()
|
return u.ж.TailscaleUserProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) Proto() *v1.User {
|
|
||||||
// Use Name if set, otherwise fall back to Username() which provides
|
|
||||||
// a display-friendly identifier (Email > ProviderIdentifier > ID).
|
|
||||||
// This ensures OIDC users (who typically have empty Name) display
|
|
||||||
// their email, while CLI users retain their original Name.
|
|
||||||
name := u.Name
|
|
||||||
if name == "" {
|
|
||||||
name = u.Username()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &v1.User{
|
|
||||||
Id: uint64(u.ID),
|
|
||||||
Name: name,
|
|
||||||
CreatedAt: timestamppb.New(u.CreatedAt),
|
|
||||||
DisplayName: u.DisplayName,
|
|
||||||
Email: u.Email,
|
|
||||||
ProviderId: u.ProviderIdentifier.String,
|
|
||||||
Provider: u.Provider,
|
|
||||||
ProfilePicUrl: u.ProfilePicURL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalZerologObject implements [zerolog.LogObjectMarshaler] for safe logging.
|
// MarshalZerologObject implements [zerolog.LogObjectMarshaler] for safe logging.
|
||||||
func (u *User) MarshalZerologObject(e *zerolog.Event) {
|
func (u *User) MarshalZerologObject(e *zerolog.Event) {
|
||||||
if u == nil {
|
if u == nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user