mirror of
https://github.com/juanfont/headscale.git
synced 2026-01-23 02:24:10 +00:00
all: apply lint auto-fixes and update test expectations
Apply additional golangci-lint auto-fixes (wsl_v5, formatting) and update SSH policy test error message expectations to match the new sentinel error formats introduced in the err113 fixes.
This commit is contained in:
parent
8bfd508cf0
commit
58b532ae3c
31 changed files with 110 additions and 68 deletions
|
|
@ -35,7 +35,8 @@ func init() {
|
|||
|
||||
checkPolicy.Flags().StringP("file", "f", "", "Path to a policy file in HuJSON format")
|
||||
|
||||
if err := checkPolicy.MarkFlagRequired("file"); err != nil {
|
||||
err := checkPolicy.MarkFlagRequired("file")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
|
||||
// Sentinel errors for CLI commands.
|
||||
var (
|
||||
ErrNameOrIDRequired = errors.New("--name or --identifier flag is required")
|
||||
ErrNameOrIDRequired = errors.New("--name or --identifier flag is required")
|
||||
ErrMultipleUsersFoundUseID = errors.New("unable to determine user, query returned multiple users, use ID")
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -321,7 +321,8 @@ func checkRequiredFiles() DoctorResult {
|
|||
|
||||
for _, file := range requiredFiles {
|
||||
cmd := exec.CommandContext(context.Background(), "test", "-e", file)
|
||||
if err := cmd.Run(); err != nil {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
missingFiles = append(missingFiles, file)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ func runIntegrationTest(env *command.Env) error {
|
|||
log.Printf("Running pre-flight system checks...")
|
||||
}
|
||||
|
||||
if err := runDoctorCheck(env.Context()); err != nil {
|
||||
err := runDoctorCheck(env.Context())
|
||||
if err != nil {
|
||||
return fmt.Errorf("pre-flight checks failed: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,7 +261,8 @@ AND auth_key_id NOT IN (
|
|||
if err == nil && routesExists {
|
||||
log.Info().Msg("Dropping leftover routes table")
|
||||
|
||||
if err := tx.Exec("DROP TABLE routes").Error; err != nil {
|
||||
err := tx.Exec("DROP TABLE routes").Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("dropping routes table: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -294,7 +295,8 @@ AND auth_key_id NOT IN (
|
|||
_ = tx.Exec("DROP TABLE IF EXISTS " + table + "_old").Error
|
||||
|
||||
// Rename current table to _old
|
||||
if err := tx.Exec("ALTER TABLE " + table + " RENAME TO " + table + "_old").Error; err != nil {
|
||||
err := tx.Exec("ALTER TABLE " + table + " RENAME TO " + table + "_old").Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("renaming table %s to %s_old: %w", table, table, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -368,7 +370,8 @@ AND auth_key_id NOT IN (
|
|||
}
|
||||
|
||||
for _, createSQL := range tableCreationSQL {
|
||||
if err := tx.Exec(createSQL).Error; err != nil {
|
||||
err := tx.Exec(createSQL).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating new table: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -397,7 +400,8 @@ AND auth_key_id NOT IN (
|
|||
}
|
||||
|
||||
for _, copySQL := range dataCopySQL {
|
||||
if err := tx.Exec(copySQL).Error; err != nil {
|
||||
err := tx.Exec(copySQL).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("copying data: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -420,14 +424,16 @@ AND auth_key_id NOT IN (
|
|||
}
|
||||
|
||||
for _, indexSQL := range indexes {
|
||||
if err := tx.Exec(indexSQL).Error; err != nil {
|
||||
err := tx.Exec(indexSQL).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating index: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Drop old tables only after everything succeeds
|
||||
for _, table := range tablesToRename {
|
||||
if err := tx.Exec("DROP TABLE IF EXISTS " + table + "_old").Error; err != nil {
|
||||
err := tx.Exec("DROP TABLE IF EXISTS " + table + "_old").Error
|
||||
if err != nil {
|
||||
log.Warn().Str("table", table+"_old").Err(err).Msg("Failed to drop old table, but migration succeeded")
|
||||
}
|
||||
}
|
||||
|
|
@ -946,18 +952,21 @@ func runMigrations(cfg types.DatabaseConfig, dbConn *gorm.DB, migrations *gormig
|
|||
|
||||
if needsFKDisabled {
|
||||
// Disable foreign keys for this migration
|
||||
if err := dbConn.Exec("PRAGMA foreign_keys = OFF").Error; err != nil {
|
||||
err := dbConn.Exec("PRAGMA foreign_keys = OFF").Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("disabling foreign keys for migration %s: %w", migrationID, err)
|
||||
}
|
||||
} else {
|
||||
// Ensure foreign keys are enabled for this migration
|
||||
if err := dbConn.Exec("PRAGMA foreign_keys = ON").Error; err != nil {
|
||||
err := dbConn.Exec("PRAGMA foreign_keys = ON").Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("enabling foreign keys for migration %s: %w", migrationID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Run up to this specific migration (will only run the next pending migration)
|
||||
if err := migrations.MigrateTo(migrationID); err != nil {
|
||||
err := migrations.MigrateTo(migrationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("running migration %s: %w", migrationID, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1009,7 +1018,8 @@ func runMigrations(cfg types.DatabaseConfig, dbConn *gorm.DB, migrations *gormig
|
|||
}
|
||||
} else {
|
||||
// PostgreSQL can run all migrations in one block - no foreign key issues
|
||||
if err := migrations.Migrate(); err != nil {
|
||||
err := migrations.Migrate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -365,7 +365,8 @@ func (c *Config) Validate() error {
|
|||
// ToURL builds a properly encoded SQLite connection string using _pragma parameters
|
||||
// compatible with modernc.org/sqlite driver.
|
||||
func (c *Config) ToURL() (string, error) {
|
||||
if err := c.Validate(); err != nil {
|
||||
err := c.Validate()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid config: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import (
|
|||
|
||||
// Sentinel errors for text serialisation.
|
||||
var (
|
||||
ErrTextUnmarshalFailed = errors.New("failed to unmarshal text value")
|
||||
ErrUnsupportedType = errors.New("unsupported type")
|
||||
ErrTextMarshalerOnly = errors.New("only encoding.TextMarshaler is supported")
|
||||
ErrTextUnmarshalFailed = errors.New("failed to unmarshal text value")
|
||||
ErrUnsupportedType = errors.New("unsupported type")
|
||||
ErrTextMarshalerOnly = errors.New("only encoding.TextMarshaler is supported")
|
||||
)
|
||||
|
||||
// Got from https://github.com/xdg-go/strum/blob/main/types.go
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ func (hsdb *HSDatabase) CreateUser(user types.User) (*types.User, error) {
|
|||
// CreateUser creates a new User. Returns error if could not be created
|
||||
// or another user already exists.
|
||||
func CreateUser(tx *gorm.DB, user types.User) (*types.User, error) {
|
||||
if err := util.ValidateHostname(user.Name); err != nil {
|
||||
err := util.ValidateHostname(user.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := tx.Create(&user).Error; err != nil {
|
||||
|
|
@ -164,7 +165,8 @@ func ListUsers(tx *gorm.DB, where ...*types.User) ([]types.User, error) {
|
|||
}
|
||||
|
||||
users := []types.User{}
|
||||
if err := tx.Where(user).Find(&users).Error; err != nil {
|
||||
err := tx.Where(user).Find(&users).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ func (d *DERPServer) GenerateRegion() (tailcfg.DERPRegion, error) {
|
|||
if err != nil {
|
||||
return tailcfg.DERPRegion{}, err
|
||||
}
|
||||
|
||||
var (
|
||||
host string
|
||||
port int
|
||||
|
|
@ -416,7 +417,8 @@ type DERPVerifyTransport struct {
|
|||
|
||||
func (t *DERPVerifyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := t.handleVerifyRequest(req, buf); err != nil {
|
||||
err := t.handleVerifyRequest(req, buf)
|
||||
if err != nil {
|
||||
log.Error().Caller().Err(err).Msg("Failed to handle client verify request: ")
|
||||
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -154,7 +154,8 @@ func (h *Headscale) KeyHandler(
|
|||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(writer).Encode(resp); err != nil {
|
||||
err := json.NewEncoder(writer).Encode(resp)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to encode key response")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -667,7 +667,8 @@ func (mc *multiChannelNodeConn) send(data *tailcfg.MapResponse) error {
|
|||
Str("conn.id", conn.id).Int("connection_index", i).
|
||||
Msg("send: attempting to send to connection")
|
||||
|
||||
if err := conn.send(data); err != nil {
|
||||
err := conn.send(data)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
|
||||
failedConnections = append(failedConnections, i)
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@ const (
|
|||
|
||||
// Channel configuration.
|
||||
NORMAL_BUFFER_SIZE = 50
|
||||
SMALL_BUFFER_SIZE = 3
|
||||
TINY_BUFFER_SIZE = 1 // For maximum contention
|
||||
LARGE_BUFFER_SIZE = 200
|
||||
SMALL_BUFFER_SIZE = 3
|
||||
TINY_BUFFER_SIZE = 1 // For maximum contention
|
||||
LARGE_BUFFER_SIZE = 200
|
||||
)
|
||||
|
||||
// TestData contains all test entities created for a test scenario.
|
||||
|
|
@ -1145,6 +1145,7 @@ func XTestBatcherChannelClosingRace(t *testing.T) {
|
|||
|
||||
wg.Go(func() {
|
||||
runtime.Gosched() // Yield to introduce timing variability
|
||||
|
||||
_ = batcher.AddNode(testNode.n.ID, ch2, tailcfg.CapabilityVersion(100))
|
||||
})
|
||||
|
||||
|
|
@ -1747,6 +1748,7 @@ func XTestBatcherScalability(t *testing.T) {
|
|||
for i := range testNodes {
|
||||
node := testNodes[i]
|
||||
_ = batcher.AddNode(node.n.ID, node.ch, tailcfg.CapabilityVersion(100))
|
||||
|
||||
connectedNodesMutex.Lock()
|
||||
|
||||
connectedNodes[node.n.ID] = true
|
||||
|
|
|
|||
|
|
@ -283,7 +283,8 @@ func (ns *noiseServer) NoiseRegistrationHandler(
|
|||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
||||
if err := json.NewEncoder(writer).Encode(registerResponse); err != nil {
|
||||
err := json.NewEncoder(writer).Encode(registerResponse)
|
||||
if err != nil {
|
||||
log.Error().Caller().Err(err).Msg("NoiseRegistrationHandler: failed to encode RegisterResponse")
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1214,7 +1214,7 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
]
|
||||
}`,
|
||||
expectErr: true,
|
||||
errorMessage: `invalid SSH action "invalid", must be one of: accept, check`,
|
||||
errorMessage: `invalid SSH action: "invalid", must be one of: accept, check`,
|
||||
},
|
||||
{
|
||||
name: "invalid-check-period",
|
||||
|
|
@ -1249,7 +1249,7 @@ func TestSSHPolicyRules(t *testing.T) {
|
|||
]
|
||||
}`,
|
||||
expectErr: true,
|
||||
errorMessage: "autogroup \"autogroup:invalid\" is not supported",
|
||||
errorMessage: `autogroup not supported for SSH: "autogroup:invalid" for SSH user`,
|
||||
},
|
||||
{
|
||||
name: "autogroup-nonroot-should-use-wildcard-with-root-excluded",
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func TestInvalidateAutogroupSelfCache(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.Equal(t, len(initialNodes), len(pm.filterRulesMap))
|
||||
require.Len(t, pm.filterRulesMap, len(initialNodes))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -3131,8 +3131,8 @@ func TestACL_UnmarshalJSON_WithCommentFields(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expected.Action, acl.Action)
|
||||
assert.Equal(t, tt.expected.Protocol, acl.Protocol)
|
||||
assert.Equal(t, len(tt.expected.Sources), len(acl.Sources))
|
||||
assert.Equal(t, len(tt.expected.Destinations), len(acl.Destinations))
|
||||
assert.Len(t, acl.Sources, len(tt.expected.Sources))
|
||||
assert.Len(t, acl.Destinations, len(tt.expected.Destinations))
|
||||
|
||||
// Compare sources
|
||||
for i, expectedSrc := range tt.expected.Sources {
|
||||
|
|
@ -3179,8 +3179,8 @@ func TestACL_UnmarshalJSON_Roundtrip(t *testing.T) {
|
|||
// Should be equal
|
||||
assert.Equal(t, original.Action, unmarshaled.Action)
|
||||
assert.Equal(t, original.Protocol, unmarshaled.Protocol)
|
||||
assert.Equal(t, len(original.Sources), len(unmarshaled.Sources))
|
||||
assert.Equal(t, len(original.Destinations), len(unmarshaled.Destinations))
|
||||
assert.Len(t, unmarshaled.Sources, len(original.Sources))
|
||||
assert.Len(t, unmarshaled.Destinations, len(original.Destinations))
|
||||
}
|
||||
|
||||
func TestACL_UnmarshalJSON_PolicyIntegration(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -249,7 +249,8 @@ func (m *mapSession) serveLongPoll() {
|
|||
return
|
||||
}
|
||||
|
||||
if err := m.writeMap(update); err != nil {
|
||||
err := m.writeMap(update)
|
||||
if err != nil {
|
||||
m.errf(err, "cannot write update to client")
|
||||
return
|
||||
}
|
||||
|
|
@ -258,7 +259,8 @@ func (m *mapSession) serveLongPoll() {
|
|||
m.resetKeepAlive()
|
||||
|
||||
case <-m.keepAliveTicker.C:
|
||||
if err := m.writeMap(&keepAlive); err != nil {
|
||||
err := m.writeMap(&keepAlive)
|
||||
if err != nil {
|
||||
m.errf(err, "cannot write keep alive")
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,4 +133,3 @@ func TestNetInfoPreservationInRegistrationFlow(t *testing.T) {
|
|||
assert.Equal(t, 7, result.PreferredDERP, "Should preserve DERP region from existing node")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,7 +187,8 @@ func NewState(cfg *types.Config) (*State, error) {
|
|||
func (s *State) Close() error {
|
||||
s.nodeStore.Stop()
|
||||
|
||||
if err := s.db.Close(); err != nil {
|
||||
err := s.db.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("closing database: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -741,7 +742,8 @@ func (s *State) SetApprovedRoutes(nodeID types.NodeID, routes []netip.Prefix) (t
|
|||
|
||||
// RenameNode changes the display name of a node.
|
||||
func (s *State) RenameNode(nodeID types.NodeID, newName string) (types.NodeView, change.Change, error) {
|
||||
if err := util.ValidateHostname(newName); err != nil {
|
||||
err := util.ValidateHostname(newName)
|
||||
if err != nil {
|
||||
return types.NodeView{}, change.Change{}, fmt.Errorf("renaming node: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -1214,7 +1216,8 @@ func (s *State) createAndSaveNewNode(params newNodeParams) (types.NodeView, erro
|
|||
|
||||
// New node - database first to get ID, then NodeStore
|
||||
savedNode, err := hsdb.Write(s.db.DB, func(tx *gorm.DB) (*types.Node, error) {
|
||||
if err := tx.Save(&nodeToRegister).Error; err != nil {
|
||||
err := tx.Save(&nodeToRegister).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save node: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,16 +29,16 @@ const (
|
|||
PKCEMethodPlain string = "plain"
|
||||
PKCEMethodS256 string = "S256"
|
||||
|
||||
defaultNodeStoreBatchSize = 100
|
||||
defaultWALAutocheckpoint = 1000 // SQLite default
|
||||
defaultNodeStoreBatchSize = 100
|
||||
defaultWALAutocheckpoint = 1000 // SQLite default
|
||||
)
|
||||
|
||||
var (
|
||||
errOidcMutuallyExclusive = errors.New("oidc_client_secret and oidc_client_secret_path are mutually exclusive")
|
||||
errServerURLSuffix = errors.New("server_url cannot be part of base_domain in a way that could make the DERP and headscale server unreachable")
|
||||
errServerURLSame = errors.New("server_url cannot use the same domain as base_domain in a way that could make the DERP and headscale server unreachable")
|
||||
errInvalidPKCEMethod = errors.New("pkce.method must be either 'plain' or 'S256'")
|
||||
errNoPrefixConfigured = errors.New("no IPv4 or IPv6 prefix configured, minimum one prefix is required")
|
||||
errOidcMutuallyExclusive = errors.New("oidc_client_secret and oidc_client_secret_path are mutually exclusive")
|
||||
errServerURLSuffix = errors.New("server_url cannot be part of base_domain in a way that could make the DERP and headscale server unreachable")
|
||||
errServerURLSame = errors.New("server_url cannot use the same domain as base_domain in a way that could make the DERP and headscale server unreachable")
|
||||
errInvalidPKCEMethod = errors.New("pkce.method must be either 'plain' or 'S256'")
|
||||
errNoPrefixConfigured = errors.New("no IPv4 or IPv6 prefix configured, minimum one prefix is required")
|
||||
errInvalidAllocationStrategy = errors.New("invalid prefixes.allocation strategy")
|
||||
)
|
||||
|
||||
|
|
@ -406,7 +406,8 @@ func LoadConfig(path string, isFile bool) error {
|
|||
viper.SetDefault("prefixes.allocation", string(IPAllocationStrategySequential))
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||
var configFileNotFoundError viper.ConfigFileNotFoundError
|
||||
if errors.As(err, &configFileNotFoundError) {
|
||||
log.Warn().Msg("No config file found, using defaults")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -446,7 +447,8 @@ func validateServerConfig() error {
|
|||
depr.fatal("oidc.map_legacy_users")
|
||||
|
||||
if viper.GetBool("oidc.enabled") {
|
||||
if err := validatePKCEMethod(viper.GetString("oidc.pkce.method")); err != nil {
|
||||
err := validatePKCEMethod(viper.GetString("oidc.pkce.method"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -984,7 +986,8 @@ func LoadServerConfig() (*Config, error) {
|
|||
// - Control plane runs on login.tailscale.com/controlplane.tailscale.com
|
||||
// - MagicDNS (BaseDomain) for users is on a *.ts.net domain per tailnet (e.g. tail-scale.ts.net)
|
||||
if dnsConfig.BaseDomain != "" {
|
||||
if err := isSafeServerURL(serverURL, dnsConfig.BaseDomain); err != nil {
|
||||
err := isSafeServerURL(serverURL, dnsConfig.BaseDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -573,7 +573,8 @@ func (node *Node) ApplyHostnameFromHostInfo(hostInfo *tailcfg.Hostinfo) {
|
|||
}
|
||||
|
||||
newHostname := strings.ToLower(hostInfo.Hostname)
|
||||
if err := util.ValidateHostname(newHostname); err != nil {
|
||||
err := util.ValidateHostname(newHostname)
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Str("node.id", node.ID.String()).
|
||||
Str("current_hostname", node.Hostname).
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ func TestUnmarshallOIDCClaims(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var got OIDCClaims
|
||||
if err := json.Unmarshal([]byte(tt.jsonstr), &got); err != nil {
|
||||
err := json.Unmarshal([]byte(tt.jsonstr), &got)
|
||||
if err != nil {
|
||||
t.Errorf("UnmarshallOIDCClaims() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
|
@ -482,7 +483,8 @@ func TestOIDCClaimsJSONToUser(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var got OIDCClaims
|
||||
if err := json.Unmarshal([]byte(tt.jsonstr), &got); err != nil {
|
||||
err := json.Unmarshal([]byte(tt.jsonstr), &got)
|
||||
if err != nil {
|
||||
t.Errorf("TestOIDCClaimsJSONToUser() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,17 +31,17 @@ var ErrInvalidHostName = errors.New("invalid hostname")
|
|||
|
||||
// Sentinel errors for username validation.
|
||||
var (
|
||||
ErrUsernameTooShort = errors.New("username must be at least 2 characters long")
|
||||
ErrUsernameTooShort = errors.New("username must be at least 2 characters long")
|
||||
ErrUsernameMustStartLetter = errors.New("username must start with a letter")
|
||||
ErrUsernameTooManyAt = errors.New("username cannot contain more than one '@'")
|
||||
ErrUsernameInvalidChar = errors.New("username contains invalid character")
|
||||
ErrUsernameTooManyAt = errors.New("username cannot contain more than one '@'")
|
||||
ErrUsernameInvalidChar = errors.New("username contains invalid character")
|
||||
)
|
||||
|
||||
// Sentinel errors for hostname validation.
|
||||
var (
|
||||
ErrHostnameTooShort = errors.New("hostname too short, must be at least 2 characters")
|
||||
ErrHostnameHyphenEnds = errors.New("hostname cannot start or end with a hyphen")
|
||||
ErrHostnameDotEnds = errors.New("hostname cannot start or end with a dot")
|
||||
ErrHostnameTooShort = errors.New("hostname too short, must be at least 2 characters")
|
||||
ErrHostnameHyphenEnds = errors.New("hostname cannot start or end with a hyphen")
|
||||
ErrHostnameDotEnds = errors.New("hostname cannot start or end with a dot")
|
||||
)
|
||||
|
||||
// ValidateUsername checks if a username is valid.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ func YesNo(msg string) bool {
|
|||
fmt.Fprint(os.Stderr, msg+" [y/n] ")
|
||||
|
||||
var resp string
|
||||
|
||||
_, _ = fmt.Scanln(&resp)
|
||||
|
||||
resp = strings.ToLower(resp)
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ func TestYesNo(t *testing.T) {
|
|||
|
||||
// Check that the prompt was written to stderr
|
||||
var stderrBuf bytes.Buffer
|
||||
|
||||
_, _ = io.Copy(&stderrBuf, stderrR)
|
||||
stderrR.Close()
|
||||
|
||||
|
|
@ -149,6 +150,7 @@ func TestYesNoPromptMessage(t *testing.T) {
|
|||
|
||||
// Check that the custom message was included in the prompt
|
||||
var stderrBuf bytes.Buffer
|
||||
|
||||
_, _ = io.Copy(&stderrBuf, stderrR)
|
||||
stderrR.Close()
|
||||
|
||||
|
|
|
|||
|
|
@ -323,7 +323,8 @@ func EnsureHostname(hostinfo *tailcfg.Hostinfo, machineKey, nodeKey string) stri
|
|||
}
|
||||
|
||||
lowercased := strings.ToLower(hostinfo.Hostname)
|
||||
if err := ValidateHostname(lowercased); err == nil {
|
||||
err := ValidateHostname(lowercased)
|
||||
if err == nil {
|
||||
return lowercased
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
|||
t.Run("HTTP_NoAuthHeader", func(t *testing.T) {
|
||||
// Test 1: Request without any Authorization header
|
||||
// Expected: Should return 401 with ONLY "Unauthorized" text, no user data
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
|
|
@ -131,7 +131,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
|||
t.Run("HTTP_InvalidAuthHeader", func(t *testing.T) {
|
||||
// Test 2: Request with invalid Authorization header (missing "Bearer " prefix)
|
||||
// Expected: Should return 401 with ONLY "Unauthorized" text, no user data
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "InvalidToken")
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
|||
// Test 3: Request with Bearer prefix but invalid token
|
||||
// Expected: Should return 401 with ONLY "Unauthorized" text, no user data
|
||||
// Note: Both malformed and properly formatted invalid tokens should return 401
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer invalid-token-12345")
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ func TestAPIAuthenticationBypass(t *testing.T) {
|
|||
t.Run("HTTP_ValidAPIKey", func(t *testing.T) {
|
||||
// Test 4: Request with valid API key
|
||||
// Expected: Should return 200 with user data (this is the authorized case)
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", apiURL, nil)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, apiURL, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+validAPIKey)
|
||||
|
||||
|
|
@ -294,6 +294,7 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
|||
)
|
||||
|
||||
var responseBodySb295 strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
if after, ok := strings.CutPrefix(line, "HTTP_CODE:"); ok {
|
||||
httpCode = after
|
||||
|
|
@ -301,6 +302,7 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
|||
responseBodySb295.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
responseBody += responseBodySb295.String()
|
||||
|
||||
// Should return 401
|
||||
|
|
@ -345,6 +347,7 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
|||
)
|
||||
|
||||
var responseBodySb344 strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
if after, ok := strings.CutPrefix(line, "HTTP_CODE:"); ok {
|
||||
httpCode = after
|
||||
|
|
@ -352,6 +355,7 @@ func TestAPIAuthenticationBypassCurl(t *testing.T) {
|
|||
responseBodySb344.WriteString(line)
|
||||
}
|
||||
}
|
||||
|
||||
responseBody += responseBodySb344.String()
|
||||
|
||||
assert.Equal(t, "401", httpCode)
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ func TestAuthKeyLogoutAndReloginNewUser(t *testing.T) {
|
|||
status, err := client.Status()
|
||||
assert.NoError(ct, err, "Failed to get status for client %s", client.Hostname())
|
||||
assert.Equal(ct, "user1@test.no", status.User[status.Self.UserID].LoginName, "Client %s should be logged in as user1 after user switch, got %s", client.Hostname(), status.User[status.Self.UserID].LoginName)
|
||||
}, 30*time.Second, 2*time.Second, fmt.Sprintf("validating %s is logged in as user1 after auth key user switch", client.Hostname()))
|
||||
}, 30*time.Second, 2*time.Second, "validating %s is logged in as user1 after auth key user switch", client.Hostname())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"testing"
|
||||
|
|
@ -364,7 +363,7 @@ func TestAuthWebFlowLogoutAndReloginNewUser(t *testing.T) {
|
|||
status, err := client.Status()
|
||||
assert.NoError(ct, err, "Failed to get status for client %s", client.Hostname())
|
||||
assert.Equal(ct, "user1@test.no", status.User[status.Self.UserID].LoginName, "Client %s should be logged in as user1 after web flow user switch, got %s", client.Hostname(), status.User[status.Self.UserID].LoginName)
|
||||
}, 30*time.Second, 2*time.Second, fmt.Sprintf("validating %s is logged in as user1 after web flow user switch", client.Hostname()))
|
||||
}, 30*time.Second, 2*time.Second, "validating %s is logged in as user1 after web flow user switch", client.Hostname())
|
||||
}
|
||||
|
||||
// Test connectivity after user switch
|
||||
|
|
|
|||
|
|
@ -109,7 +109,8 @@ func DERPVerify(
|
|||
defer c.Close()
|
||||
|
||||
var result error
|
||||
if err := c.Connect(t.Context()); err != nil {
|
||||
err := c.Connect(t.Context())
|
||||
if err != nil {
|
||||
result = fmt.Errorf("client Connect: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) {
|
|||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
command := []string{"echo", fmt.Sprintf("'%s'", string(b4)), ">", erPath}
|
||||
_, err = hs.Execute([]string{"bash", "-c", strings.Join(command, " ")})
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue