- Add API server with WebSocket support and REST endpoints - Implement authentication system with API keys and permissions - Add task queue system with Redis backend and error handling - Include storage layer with database migrations and schemas - Add comprehensive logging, metrics, and telemetry - Implement security middleware and network utilities - Add experiment management and container orchestration - Include configuration management with smart defaults
229 lines
4.4 KiB
Go
229 lines
4.4 KiB
Go
package auth
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestHashAPIKey(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
key string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "known hash",
|
|
key: "password",
|
|
expected: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
|
|
},
|
|
{
|
|
name: "another known hash",
|
|
key: "test",
|
|
expected: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := HashAPIKey(tt.key)
|
|
if got != tt.expected {
|
|
t.Errorf("HashAPIKey() = %v, want %v", got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHashAPIKeyConsistency(t *testing.T) {
|
|
key := "my-secret-key"
|
|
hash1 := HashAPIKey(key)
|
|
hash2 := HashAPIKey(key)
|
|
|
|
if hash1 != hash2 {
|
|
t.Errorf("HashAPIKey() not consistent: %v != %v", hash1, hash2)
|
|
}
|
|
|
|
if len(hash1) != 64 {
|
|
t.Errorf("HashAPIKey() wrong length: got %d, want 64", len(hash1))
|
|
}
|
|
}
|
|
|
|
func TestGenerateAPIKey(t *testing.T) {
|
|
// Test that it generates keys
|
|
key1 := GenerateAPIKey()
|
|
|
|
if len(key1) != 64 {
|
|
t.Errorf("GenerateAPIKey() length = %d, want 64", len(key1))
|
|
}
|
|
|
|
// Test uniqueness (timing-based, should be different)
|
|
key2 := GenerateAPIKey()
|
|
|
|
if key1 == key2 {
|
|
t.Errorf("GenerateAPIKey() not unique: both generated %s", key1)
|
|
}
|
|
}
|
|
|
|
func TestUserHasPermission(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
user *User
|
|
permission string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "wildcard grants all",
|
|
user: &User{
|
|
Permissions: map[string]bool{"*": true},
|
|
},
|
|
permission: "anything",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "direct permission",
|
|
user: &User{
|
|
Permissions: map[string]bool{"jobs:create": true},
|
|
},
|
|
permission: "jobs:create",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "hierarchical permission match",
|
|
user: &User{
|
|
Permissions: map[string]bool{"jobs": true},
|
|
},
|
|
permission: "jobs:create",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "no permission",
|
|
user: &User{
|
|
Permissions: map[string]bool{"jobs:read": true},
|
|
},
|
|
permission: "jobs:create",
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := tt.user.HasPermission(tt.permission)
|
|
if got != tt.want {
|
|
t.Errorf("HasPermission() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUserHasRole(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
user *User
|
|
role string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "has role",
|
|
user: &User{
|
|
Roles: []string{"admin", "user"},
|
|
},
|
|
role: "admin",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "does not have role",
|
|
user: &User{
|
|
Roles: []string{"user"},
|
|
},
|
|
role: "admin",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "empty roles",
|
|
user: &User{
|
|
Roles: []string{},
|
|
},
|
|
role: "admin",
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := tt.user.HasRole(tt.role)
|
|
if got != tt.want {
|
|
t.Errorf("HasRole() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthConfigValidateAPIKey(t *testing.T) {
|
|
config := &AuthConfig{
|
|
Enabled: true,
|
|
APIKeys: map[Username]APIKeyEntry{
|
|
"testuser": {
|
|
Hash: APIKeyHash(HashAPIKey("test-key")),
|
|
Admin: false,
|
|
Roles: []string{"user"},
|
|
Permissions: map[string]bool{
|
|
"jobs:read": true,
|
|
},
|
|
},
|
|
"admin": {
|
|
Hash: APIKeyHash(HashAPIKey("admin-key")),
|
|
Admin: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
key string
|
|
wantErr bool
|
|
wantAdmin bool
|
|
}{
|
|
{
|
|
name: "valid user key",
|
|
key: "test-key",
|
|
wantErr: false,
|
|
wantAdmin: false,
|
|
},
|
|
{
|
|
name: "valid admin key",
|
|
key: "admin-key",
|
|
wantErr: false,
|
|
wantAdmin: true,
|
|
},
|
|
{
|
|
name: "invalid key",
|
|
key: "wrong-key",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
user, err := config.ValidateAPIKey(tt.key)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ValidateAPIKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !tt.wantErr && user.Admin != tt.wantAdmin {
|
|
t.Errorf("ValidateAPIKey() admin = %v, want %v", user.Admin, tt.wantAdmin)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthConfigDisabled(t *testing.T) {
|
|
config := &AuthConfig{
|
|
Enabled: false,
|
|
}
|
|
|
|
user, err := config.ValidateAPIKey("any-key")
|
|
if err != nil {
|
|
t.Errorf("ValidateAPIKey() with auth disabled should not error: %v", err)
|
|
}
|
|
if !user.Admin {
|
|
t.Error("ValidateAPIKey() with auth disabled should return admin user")
|
|
}
|
|
}
|