Files
kandev/apps/backend/internal/backendapp/github_deployment_app_test.go
T

110 lines
3.6 KiB
Go

package backendapp
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"path/filepath"
"testing"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
"github.com/kandev/kandev/internal/common/config"
"github.com/kandev/kandev/internal/common/logger"
"github.com/kandev/kandev/internal/db"
"github.com/kandev/kandev/internal/github"
"github.com/kandev/kandev/internal/integrations/secretadapter"
"github.com/kandev/kandev/internal/secrets"
)
func TestGitHubAppBootLoadsCatalogRegistrationFromEncryptedStore(t *testing.T) {
ctx := context.Background()
dir := t.TempDir()
connection, err := db.OpenSQLite(filepath.Join(dir, "kandev.db"))
if err != nil {
t.Fatal(err)
}
database := sqlx.NewDb(connection, "sqlite3")
t.Cleanup(func() { _ = database.Close() })
masterKey, err := secrets.NewMasterKeyProvider(dir)
if err != nil {
t.Fatal(err)
}
secretStore, cleanup, err := secrets.Provide(database, database, masterKey)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = cleanup() })
log, err := logger.NewFromZap(zap.NewNop())
if err != nil {
t.Fatal(err)
}
seedService, cleanupGitHub, err := github.Provide(database, database, nil, nil, log)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = cleanupGitHub() })
adapter := secretadapter.New(secretStore)
repository := github.NewAppRegistrationRepository(seedService.TestStore(), adapter)
registration := &github.AppRegistration{
ID: "11111111-1111-4111-8111-111111111111", Source: github.AppRegistrationSourceManaged,
DisplayName: "Kandev Acme", GitHubHost: "github.com", AppID: 123,
ClientID: "Iv1.client", Slug: "kandev-acme",
OwnerLogin: "acme", OwnerType: github.DeploymentAppOwnerOrganization,
Visibility: github.AppRegistrationVisibilityPrivate,
PublicBaseURL: "https://kandev.example", CredentialGeneration: 1,
Status: github.AppRegistrationStatusActive, WebhookStatus: github.DeploymentAppWebhookUnverified,
}
privateKey := testGitHubDeploymentPrivateKey(t)
if err := repository.SaveRegistration(ctx, registration, github.DeploymentAppCredentials{
PrivateKey: privateKey, ClientSecret: "client-secret",
WebhookSecret: "webhook-secret",
}); err != nil {
t.Fatal(err)
}
const orphanID = github.AppRegistrationCredentialsSecretPrefix + "orphan:g99:secret"
if err := adapter.Set(ctx, orphanID, "orphan", `{"version":1}`); err != nil {
t.Fatal(err)
}
service := initGitHubService(
&config.Config{}, db.NewPool(database, database), nil, secretStore, log,
)
if service == nil {
t.Fatal("GitHub service was not initialized")
}
snapshot := service.AppRegistrationRuntimeSnapshot(registration.ID)
if !snapshot.Ready || snapshot.Source != github.DeploymentAppSourceManaged ||
snapshot.AppID != registration.AppID || snapshot.Generation != 1 {
t.Fatalf("boot runtime = %+v", snapshot)
}
if exists, err := adapter.Exists(ctx, orphanID); err != nil || exists {
t.Fatalf("orphan credential exists = %v, error = %v", exists, err)
}
var encrypted []byte
if err := database.Get(&encrypted, `SELECT encrypted_value FROM secrets WHERE id = ?`,
registration.CredentialSecretID); err != nil {
t.Fatal(err)
}
for _, plaintext := range [][]byte{[]byte("client-secret"), []byte("webhook-secret"), []byte(privateKey)} {
if bytes.Contains(encrypted, plaintext) {
t.Fatal("deployment credentials were stored as plaintext")
}
}
}
func testGitHubDeploymentPrivateKey(t *testing.T) string {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
return string(pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key),
}))
}