545 lines
20 KiB
Go
545 lines
20 KiB
Go
package controller
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/heicode/manager/common"
|
|
"github.com/heicode/manager/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/glebarez/sqlite"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func setupResourceControllerTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
gin.SetMode(gin.TestMode)
|
|
common.UsingSQLite = true
|
|
common.UsingMySQL = false
|
|
common.UsingPostgreSQL = false
|
|
common.RedisEnabled = false
|
|
|
|
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", strings.ReplaceAll(t.Name(), "/", "_"))
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
model.DB = db
|
|
model.LOG_DB = db
|
|
require.NoError(t, db.AutoMigrate(&model.ResourceBinding{}, &model.ResourceGrant{}))
|
|
t.Cleanup(func() {
|
|
sqlDB, err := db.DB()
|
|
if err == nil {
|
|
_ = sqlDB.Close()
|
|
}
|
|
})
|
|
return db
|
|
}
|
|
|
|
func performResourceRequest(handler gin.HandlerFunc, userID int, method string, path string, body string) *httptest.ResponseRecorder {
|
|
return performResourceRequestWithRoute(handler, userID, method, path, path, body)
|
|
}
|
|
|
|
func performResourceRequestWithRoute(handler gin.HandlerFunc, userID int, method string, routePath string, requestPath string, body string) *httptest.ResponseRecorder {
|
|
r := gin.New()
|
|
r.Handle(method, routePath, func(c *gin.Context) {
|
|
c.Set("id", userID)
|
|
handler(c)
|
|
})
|
|
req := httptest.NewRequest(method, requestPath, bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
return w
|
|
}
|
|
|
|
func TestCreateResourceStoresMetadataAndSecretRefOnly(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
body := `{
|
|
"binding_scope":"https://example.com/org/repo#main",
|
|
"name":"Project repository",
|
|
"resource_type":"git",
|
|
"provider":"github",
|
|
"external_id":"https://example.com/org/repo",
|
|
"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/repo",
|
|
"metadata":{"repo_url":"https://example.com/org/repo","ref":"main","allowed_paths":["."]},
|
|
"permission_scope":{"actions":["read","write"]},
|
|
"constraints":{"environment":"dev"}
|
|
}`
|
|
|
|
w := performResourceRequest(CreateResource, 7, http.MethodPost, "/", body)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":true`)
|
|
require.Contains(t, w.Body.String(), `"binding_scope":"https://example.com/org/repo#main"`)
|
|
require.Contains(t, w.Body.String(), `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/repo"`)
|
|
|
|
var resource model.ResourceBinding
|
|
require.NoError(t, db.First(&resource).Error)
|
|
require.Equal(t, "git", resource.ResourceType)
|
|
require.Equal(t, "https://example.com/org/repo#main", resource.BindingScope)
|
|
require.Equal(t, "azkv://heicode-kv.vault.azure.net/secrets/repo", resource.SecretRef)
|
|
require.NotContains(t, resource.Metadata, "token")
|
|
require.NotContains(t, resource.PermissionScope, "token")
|
|
require.NotContains(t, resource.Constraints, "token")
|
|
}
|
|
|
|
func TestCreateResourceRejectsPlaintextSecretKeys(t *testing.T) {
|
|
setupResourceControllerTestDB(t)
|
|
body := `{
|
|
"name":"Cloud account",
|
|
"resource_type":"cloud_account",
|
|
"metadata":{"account_id":"sub-1","access_key":"do-not-store"},
|
|
"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/cloud-sub-1"
|
|
}`
|
|
|
|
w := performResourceRequest(CreateResource, 7, http.MethodPost, "/", body)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":false`)
|
|
require.Contains(t, w.Body.String(), "plaintext secrets are not allowed")
|
|
}
|
|
|
|
func TestDeleteResourceRevokesBindingAndActiveGrants(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
resource := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "project-alpha",
|
|
Name: "Project repo",
|
|
ResourceType: "git",
|
|
Provider: "gitee",
|
|
SecretRef: "azkv://heicode-kv.vault.azure.net/secrets/project-alpha",
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&resource).Error)
|
|
grant := model.ResourceGrant{
|
|
UserId: 7,
|
|
BindingScope: "project-alpha",
|
|
ResourceId: resource.Id,
|
|
Role: "backend",
|
|
AgnetId: "agent-backend-1",
|
|
PermissionScope: `{"actions":["repo:read"]}`,
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&grant).Error)
|
|
|
|
w := performResourceRequestWithRoute(
|
|
DeleteResource,
|
|
7,
|
|
http.MethodDelete,
|
|
"/:id",
|
|
fmt.Sprintf("/%d", resource.Id),
|
|
"",
|
|
)
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"status":"revoked"`)
|
|
|
|
var storedResource model.ResourceBinding
|
|
require.NoError(t, db.First(&storedResource, resource.Id).Error)
|
|
require.Equal(t, "revoked", storedResource.Status)
|
|
|
|
var storedGrant model.ResourceGrant
|
|
require.NoError(t, db.First(&storedGrant, grant.Id).Error)
|
|
require.Equal(t, "revoked", storedGrant.Status)
|
|
require.NotZero(t, storedGrant.RevokedAt)
|
|
|
|
listActive := performResourceRequestWithRoute(
|
|
ListResources,
|
|
7,
|
|
http.MethodGet,
|
|
"/",
|
|
"/?status=active",
|
|
"",
|
|
)
|
|
require.Equal(t, http.StatusOK, listActive.Code)
|
|
require.Contains(t, listActive.Body.String(), `"items":[]`)
|
|
}
|
|
|
|
func TestCreateResourceGrantAssignsBoundResourceToRoleAgnet(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
resource := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "https://example.com/sk.git#main",
|
|
Name: "SK repo",
|
|
ResourceType: "sk",
|
|
Provider: "git",
|
|
SecretRef: "azkv://heicode-kv.vault.azure.net/secrets/sk-repo",
|
|
Metadata: `{"repo_url":"https://example.com/sk.git"}`,
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&resource).Error)
|
|
|
|
body := fmt.Sprintf(`{
|
|
"binding_scope":"https://example.com/sk.git#main",
|
|
"resource_id":%d,
|
|
"role":"developer",
|
|
"agnet_id":"agnet-dev-1",
|
|
"permission_scope":{"actions":["read"]},
|
|
"constraints":{"paths":["skills/**"]}
|
|
}`, resource.Id)
|
|
|
|
w := performResourceRequest(CreateResourceGrant, 7, http.MethodPost, "/", body)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":true`)
|
|
require.Contains(t, w.Body.String(), `"binding_scope":"https://example.com/sk.git#main"`)
|
|
require.Contains(t, w.Body.String(), `"role":"developer"`)
|
|
require.Contains(t, w.Body.String(), `"agnet_id":"agnet-dev-1"`)
|
|
|
|
var grant model.ResourceGrant
|
|
require.NoError(t, db.First(&grant).Error)
|
|
require.Equal(t, resource.Id, grant.ResourceId)
|
|
require.Equal(t, "https://example.com/sk.git#main", grant.BindingScope)
|
|
require.Equal(t, "developer", grant.Role)
|
|
require.Equal(t, "agnet-dev-1", grant.AgnetId)
|
|
}
|
|
|
|
func TestCreateResourceGrantRejectsMismatchedBindingScope(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
resource := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "azure-vm-prod",
|
|
Name: "VM",
|
|
ResourceType: "cloud_resource",
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&resource).Error)
|
|
|
|
body := fmt.Sprintf(`{
|
|
"binding_scope":"azure-vm-dev",
|
|
"resource_id":%d,
|
|
"role":"operator",
|
|
"agnet_id":"agnet-ops-1"
|
|
}`, resource.Id)
|
|
|
|
w := performResourceRequest(CreateResourceGrant, 7, http.MethodPost, "/", body)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":false`)
|
|
require.Contains(t, w.Body.String(), "resource binding_scope does not match grant binding_scope")
|
|
}
|
|
|
|
func TestGenerateResourceGrantManifestIncludesActiveGrantsOnly(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
resource := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "repo-main",
|
|
Name: "Project repository",
|
|
ResourceType: "git",
|
|
Provider: "github",
|
|
ExternalId: "https://example.com/org/repo.git",
|
|
SecretRef: "azkv://heicode-kv.vault.azure.net/secrets/repo-main",
|
|
Metadata: `{"repo_url":"https://example.com/org/repo.git"}`,
|
|
PermissionScope: `{"actions":["read","write"]}`,
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&resource).Error)
|
|
require.NoError(t, db.Create(&model.ResourceGrant{
|
|
UserId: 7,
|
|
BindingScope: "repo-main",
|
|
ResourceId: resource.Id,
|
|
Role: "backend",
|
|
AgnetId: "agnet-backend-1",
|
|
PermissionScope: `{"actions":["read"]}`,
|
|
Constraints: `{"paths":["heicode/controller/**"]}`,
|
|
Status: "active",
|
|
}).Error)
|
|
require.NoError(t, db.Create(&model.ResourceGrant{
|
|
UserId: 7,
|
|
BindingScope: "repo-main",
|
|
ResourceId: resource.Id,
|
|
Role: "backend",
|
|
AgnetId: "agnet-backend-1",
|
|
PermissionScope: `{"actions":["write"]}`,
|
|
Status: "revoked",
|
|
}).Error)
|
|
|
|
w := performResourceRequestWithRoute(
|
|
GenerateResourceGrantManifest,
|
|
7,
|
|
http.MethodGet,
|
|
"/manifest",
|
|
"/manifest?binding_scope=repo-main&role=backend&agnet_id=agnet-backend-1",
|
|
"",
|
|
)
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":true`)
|
|
require.Contains(t, w.Body.String(), `"binding_scope":"repo-main"`)
|
|
require.Contains(t, w.Body.String(), `"agent_role":"backend"`)
|
|
require.Contains(t, w.Body.String(), `"target_agent_ref":"agnet-backend-1"`)
|
|
require.Contains(t, w.Body.String(), `"resource_type":"git"`)
|
|
require.Contains(t, w.Body.String(), `"allowed_actions":["read"]`)
|
|
require.Contains(t, w.Body.String(), `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/repo-main"`)
|
|
require.NotContains(t, w.Body.String(), `"allowed_actions":["write"]`)
|
|
}
|
|
|
|
func TestDeleteResourceGrantRevokesInsteadOfDeleting(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
resource := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "project-alpha",
|
|
Name: "Project repo",
|
|
ResourceType: "git",
|
|
Provider: "gitee",
|
|
SecretRef: "azkv://heicode-kv.vault.azure.net/secrets/project-alpha",
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&resource).Error)
|
|
grant := model.ResourceGrant{
|
|
UserId: 7,
|
|
BindingScope: "project-alpha",
|
|
ResourceId: resource.Id,
|
|
Role: "backend",
|
|
AgnetId: "agent-backend-1",
|
|
PermissionScope: `{"actions":["repo:read","repo:write"]}`,
|
|
Constraints: `{"paths":["src/**"]}`,
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&grant).Error)
|
|
|
|
w := performResourceRequestWithRoute(
|
|
DeleteResourceGrant,
|
|
7,
|
|
http.MethodDelete,
|
|
"/:id",
|
|
fmt.Sprintf("/%d", grant.Id),
|
|
"",
|
|
)
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"status":"revoked"`)
|
|
|
|
var stored model.ResourceGrant
|
|
require.NoError(t, db.First(&stored, grant.Id).Error)
|
|
require.Equal(t, "revoked", stored.Status)
|
|
require.NotZero(t, stored.RevokedAt)
|
|
|
|
manifestRecorder := performResourceRequestWithRoute(
|
|
GenerateResourceGrantManifest,
|
|
7,
|
|
http.MethodGet,
|
|
"/manifest",
|
|
"/manifest?binding_scope=project-alpha",
|
|
"",
|
|
)
|
|
require.Equal(t, http.StatusOK, manifestRecorder.Code)
|
|
require.Contains(t, manifestRecorder.Body.String(), `"resource_grants":[]`)
|
|
}
|
|
|
|
func TestUpsertResourceSecretWritesAzureKeyVaultAndStoresOnlySecretRef(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
resource := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "github-org-repo-main",
|
|
Name: "GitHub",
|
|
ResourceType: "git",
|
|
Provider: "github",
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&resource).Error)
|
|
|
|
tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodGet, r.Method)
|
|
require.Equal(t, "true", r.Header.Get("Metadata"))
|
|
require.Equal(t, "2018-02-01", r.URL.Query().Get("api-version"))
|
|
require.Equal(t, "https://vault.azure.net", r.URL.Query().Get("resource"))
|
|
_, _ = w.Write([]byte(`{"access_token":"manager-token","expires_in":"3600"}`))
|
|
}))
|
|
defer tokenServer.Close()
|
|
|
|
var writtenPath string
|
|
var writtenBody map[string]any
|
|
vaultServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodPut, r.Method)
|
|
require.Equal(t, "Bearer manager-token", r.Header.Get("Authorization"))
|
|
require.Equal(t, "/secrets/users-7-bindings-github-org-repo-main-resources-1", r.URL.Path)
|
|
require.Equal(t, "7.4", r.URL.Query().Get("api-version"))
|
|
require.NoError(t, common.DecodeJson(r.Body, &writtenBody))
|
|
writtenPath = r.URL.Path
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(`{"id":"https://heicode-kv.vault.azure.net/secrets/users-7-bindings-github-org-repo-main-resources-1/version"}`))
|
|
}))
|
|
defer vaultServer.Close()
|
|
t.Setenv("AZURE_KEY_VAULT_URL", vaultServer.URL)
|
|
t.Setenv("AZURE_MANAGED_IDENTITY_TOKEN_URL", tokenServer.URL)
|
|
|
|
body := `{"data":{"access_token":"do-not-echo","username":"octo"}}`
|
|
w := performResourceRequestWithRoute(UpsertResourceSecret, 7, http.MethodPost, "/:id/secret", "/1/secret", body)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":true`)
|
|
require.Contains(t, w.Body.String(), `"secret_ref":"azkv://127.0.0.1`)
|
|
require.Contains(t, w.Body.String(), `/secrets/users-7-bindings-github-org-repo-main-resources-1"`)
|
|
require.NotContains(t, w.Body.String(), "do-not-echo")
|
|
require.Equal(t, "/secrets/users-7-bindings-github-org-repo-main-resources-1", writtenPath)
|
|
require.Equal(t, "application/json", writtenBody["contentType"])
|
|
require.JSONEq(t, `{"access_token":"do-not-echo","username":"octo"}`, writtenBody["value"].(string))
|
|
|
|
var stored model.ResourceBinding
|
|
require.NoError(t, db.First(&stored, resource.Id).Error)
|
|
require.Contains(t, stored.SecretRef, "azkv://127.0.0.1")
|
|
require.Contains(t, stored.SecretRef, "/secrets/users-7-bindings-github-org-repo-main-resources-1")
|
|
require.NotContains(t, stored.Metadata, "do-not-echo")
|
|
}
|
|
|
|
func TestUpsertResourceSecretRequiresAzureKeyVaultURL(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
resource := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "github",
|
|
Name: "GitHub",
|
|
ResourceType: "git",
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&resource).Error)
|
|
t.Setenv("AZURE_KEY_VAULT_URL", "")
|
|
|
|
w := performResourceRequestWithRoute(UpsertResourceSecret, 7, http.MethodPost, "/:id/secret", "/1/secret", `{"data":{"token":"x"}}`)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":false`)
|
|
require.Contains(t, w.Body.String(), "AZURE_KEY_VAULT_URL is not configured")
|
|
}
|
|
|
|
func TestDiscoverAzureResourcesReadsKeyVaultSecretAndStoresCloudResources(t *testing.T) {
|
|
db := setupResourceControllerTestDB(t)
|
|
|
|
tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodGet, r.Method)
|
|
require.Equal(t, "https://vault.azure.net", r.URL.Query().Get("resource"))
|
|
_, _ = w.Write([]byte(`{"access_token":"kv-token"}`))
|
|
}))
|
|
defer tokenServer.Close()
|
|
|
|
vaultServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodGet, r.Method)
|
|
require.Equal(t, "Bearer kv-token", r.Header.Get("Authorization"))
|
|
require.Equal(t, "/secrets/azure-sub-1", r.URL.Path)
|
|
require.Equal(t, "7.4", r.URL.Query().Get("api-version"))
|
|
_, _ = w.Write([]byte(`{"value":"{\"client_secret\":\"sp-secret\"}"}`))
|
|
}))
|
|
defer vaultServer.Close()
|
|
vaultURL, err := url.Parse(vaultServer.URL)
|
|
require.NoError(t, err)
|
|
|
|
authorityServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodPost, r.Method)
|
|
require.Equal(t, "/tenant-a/oauth2/v2.0/token", r.URL.Path)
|
|
require.NoError(t, r.ParseForm())
|
|
require.Equal(t, "client_credentials", r.Form.Get("grant_type"))
|
|
require.Equal(t, "client-a", r.Form.Get("client_id"))
|
|
require.Equal(t, "sp-secret", r.Form.Get("client_secret"))
|
|
require.Equal(t, "https://management.azure.com/.default", r.Form.Get("scope"))
|
|
_, _ = w.Write([]byte(`{"access_token":"arm-token"}`))
|
|
}))
|
|
defer authorityServer.Close()
|
|
|
|
armServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodGet, r.Method)
|
|
require.Equal(t, "Bearer arm-token", r.Header.Get("Authorization"))
|
|
require.Equal(t, "/subscriptions/sub-a/resources", r.URL.Path)
|
|
require.Equal(t, "2021-04-01", r.URL.Query().Get("api-version"))
|
|
_, _ = w.Write([]byte(`{
|
|
"value": [
|
|
{
|
|
"id": "/subscriptions/sub-a/resourceGroups/rg-app/providers/Microsoft.Compute/virtualMachines/vm-api-01",
|
|
"name": "vm-api-01",
|
|
"type": "Microsoft.Compute/virtualMachines",
|
|
"location": "eastus",
|
|
"tags": {"env": "test"}
|
|
}
|
|
]
|
|
}`))
|
|
}))
|
|
defer armServer.Close()
|
|
|
|
t.Setenv("AZURE_KEY_VAULT_URL", vaultServer.URL)
|
|
t.Setenv("AZURE_MANAGED_IDENTITY_TOKEN_URL", tokenServer.URL)
|
|
t.Setenv("AZURE_AUTHORITY_HOST", authorityServer.URL)
|
|
t.Setenv("AZURE_ARM_BASE_URL", armServer.URL)
|
|
|
|
account := model.ResourceBinding{
|
|
UserId: 7,
|
|
BindingScope: "azure:sub-a",
|
|
Name: "Test Azure",
|
|
ResourceType: "cloud_account",
|
|
Provider: "azure",
|
|
ExternalId: "sub-a",
|
|
SecretRef: fmt.Sprintf("azkv://%s/secrets/azure-sub-1", vaultURL.Host),
|
|
Metadata: `{"subscription_id":"sub-a","tenant_id":"tenant-a","client_id":"client-a"}`,
|
|
Status: "active",
|
|
}
|
|
require.NoError(t, db.Create(&account).Error)
|
|
|
|
w := performResourceRequestWithRoute(
|
|
DiscoverAzureResources,
|
|
7,
|
|
http.MethodPost,
|
|
"/:id/azure/discover",
|
|
"/1/azure/discover",
|
|
"",
|
|
)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":true`)
|
|
require.Contains(t, w.Body.String(), `"resource_group":"rg-app"`)
|
|
require.Contains(t, w.Body.String(), `"type":"Microsoft.Compute/virtualMachines"`)
|
|
require.NotContains(t, w.Body.String(), "sp-secret")
|
|
|
|
var resources []model.ResourceBinding
|
|
require.NoError(t, db.Where("user_id = ? AND resource_type = ?", 7, "cloud_resource").Find(&resources).Error)
|
|
require.Len(t, resources, 1)
|
|
require.Equal(t, "vm-api-01", resources[0].Name)
|
|
require.Equal(t, "azure", resources[0].Provider)
|
|
require.Equal(t, "active", resources[0].Status)
|
|
require.Equal(t, account.SecretRef, resources[0].SecretRef)
|
|
require.Contains(t, resources[0].ExternalId, "Microsoft.Compute/virtualMachines/vm-api-01")
|
|
require.Contains(t, resources[0].Metadata, `"resource_group":"rg-app"`)
|
|
require.NotContains(t, resources[0].Metadata, "sp-secret")
|
|
}
|
|
|
|
func TestResourceSecretNameFitsAzureKeyVaultRules(t *testing.T) {
|
|
name := resourceSecretName(model.ResourceBinding{
|
|
Id: 99,
|
|
UserId: 7,
|
|
BindingScope: strings.Repeat("repo/with spaces/", 20),
|
|
Name: "fallback",
|
|
})
|
|
|
|
require.LessOrEqual(t, len(name), azureKeyVaultSecretNameMaxLen)
|
|
require.Regexp(t, `^[A-Za-z0-9-]+$`, name)
|
|
require.Contains(t, name, "users-7-bindings-")
|
|
require.Contains(t, name, "-resources-99")
|
|
}
|
|
|
|
func TestGetSecretStoreStatusReportsAzureKeyVaultReachable(t *testing.T) {
|
|
tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, "true", r.Header.Get("Metadata"))
|
|
_, _ = w.Write([]byte(`{"access_token":"manager-token"}`))
|
|
}))
|
|
defer tokenServer.Close()
|
|
|
|
vaultServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodGet, r.Method)
|
|
require.Equal(t, "/secrets", r.URL.Path)
|
|
require.Equal(t, "Bearer manager-token", r.Header.Get("Authorization"))
|
|
_, _ = w.Write([]byte(`{"value":[]}`))
|
|
}))
|
|
defer vaultServer.Close()
|
|
|
|
t.Setenv("AZURE_KEY_VAULT_URL", vaultServer.URL)
|
|
t.Setenv("AZURE_MANAGED_IDENTITY_TOKEN_URL", tokenServer.URL)
|
|
|
|
r := gin.New()
|
|
r.GET("/status", GetSecretStoreStatus)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/status", nil))
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.Contains(t, w.Body.String(), `"success":true`)
|
|
require.Contains(t, w.Body.String(), `"configured":true`)
|
|
require.Contains(t, w.Body.String(), `"reachable":true`)
|
|
require.Contains(t, w.Body.String(), `"provider":"azure_key_vault"`)
|
|
require.Contains(t, w.Body.String(), "Azure Key Vault is reachable")
|
|
}
|