omx(team): auto-checkpoint worker-4 [4]
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/heicode/manager/common"
|
||||
"github.com/heicode/manager/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupResourceControllerTestDB(t *testing.T) {
|
||||
t.Helper()
|
||||
db := openTokenControllerTestDB(t)
|
||||
require.NoError(t, db.AutoMigrate(&model.ResourceBinding{}, &model.ResourceGrant{}))
|
||||
}
|
||||
|
||||
func decodeResourceBindingData(t *testing.T, raw []byte) resourceBindingResponse {
|
||||
t.Helper()
|
||||
var response tokenAPIResponse
|
||||
require.NoError(t, common.Unmarshal(raw, &response))
|
||||
require.True(t, response.Success, response.Message)
|
||||
var data resourceBindingResponse
|
||||
require.NoError(t, common.Unmarshal(response.Data, &data))
|
||||
return data
|
||||
}
|
||||
|
||||
func decodeResourceGrantData(t *testing.T, raw []byte) resourceGrantResponse {
|
||||
t.Helper()
|
||||
var response tokenAPIResponse
|
||||
require.NoError(t, common.Unmarshal(raw, &response))
|
||||
require.True(t, response.Success, response.Message)
|
||||
var data resourceGrantResponse
|
||||
require.NoError(t, common.Unmarshal(response.Data, &data))
|
||||
return data
|
||||
}
|
||||
|
||||
func TestCreateResourceAcceptsSecretRefOnly(t *testing.T) {
|
||||
setupResourceControllerTestDB(t)
|
||||
|
||||
body := map[string]any{
|
||||
"tenant_id": "tenant-a",
|
||||
"project_id": "project-a",
|
||||
"resource_type": "git",
|
||||
"name": "main repo",
|
||||
"provider": "github",
|
||||
"resource_ref": "https://github.com/example/repo.git",
|
||||
"metadata": map[string]any{
|
||||
"ref": "main",
|
||||
},
|
||||
"permission_scope": map[string]any{
|
||||
"actions": []string{"read", "write"},
|
||||
},
|
||||
"constraints": map[string]any{
|
||||
"paths": []string{"src"},
|
||||
},
|
||||
"secret_ref": "vault://tenant-a/git/main-repo",
|
||||
}
|
||||
ctx, recorder := newAuthenticatedContext(t, http.MethodPost, "/api/resources/", body, 7)
|
||||
|
||||
CreateResource(ctx)
|
||||
|
||||
data := decodeResourceBindingData(t, recorder.Body.Bytes())
|
||||
assert.Equal(t, 7, data.UserId)
|
||||
assert.Equal(t, "tenant-a", data.TenantId)
|
||||
assert.Equal(t, "project-a", data.ProjectId)
|
||||
assert.Equal(t, "git", data.ResourceType)
|
||||
assert.Equal(t, "vault://tenant-a/git/main-repo", data.SecretRef)
|
||||
assert.Equal(t, "main", data.Metadata["ref"])
|
||||
assert.NotContains(t, recorder.Body.String(), "plain-token")
|
||||
}
|
||||
|
||||
func TestCreateResourceRejectsPlaintextSecret(t *testing.T) {
|
||||
setupResourceControllerTestDB(t)
|
||||
|
||||
body := map[string]any{
|
||||
"tenant_id": "tenant-a",
|
||||
"project_id": "project-a",
|
||||
"resource_type": "cloud_account",
|
||||
"name": "aws account",
|
||||
"resource_ref": "arn:aws:organizations::123456789012:account/o-example/123456789012",
|
||||
"secret": "plain-token",
|
||||
}
|
||||
ctx, recorder := newAuthenticatedContext(t, http.MethodPost, "/api/resources/", body, 7)
|
||||
|
||||
CreateResource(ctx)
|
||||
|
||||
var response tokenAPIResponse
|
||||
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &response))
|
||||
assert.False(t, response.Success)
|
||||
assert.Contains(t, response.Message, "secret plaintext is not accepted")
|
||||
assert.NotContains(t, recorder.Body.String(), "plain-token")
|
||||
}
|
||||
|
||||
func TestCreateResourceGrantBindsProjectRoleAndSubAgnet(t *testing.T) {
|
||||
setupResourceControllerTestDB(t)
|
||||
|
||||
resource := model.ResourceBinding{
|
||||
UserId: 7,
|
||||
TenantId: "tenant-a",
|
||||
ProjectId: "project-a",
|
||||
ResourceType: "cloud_resource",
|
||||
Name: "staging vm",
|
||||
Provider: "azure",
|
||||
ResourceRef: "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm-a",
|
||||
Metadata: "{}",
|
||||
PermissionScope: "{}",
|
||||
Constraints: "{}",
|
||||
Status: "active",
|
||||
SecretRef: "vault://tenant-a/azure/vm-a",
|
||||
}
|
||||
require.NoError(t, model.DB.Create(&resource).Error)
|
||||
|
||||
body := map[string]any{
|
||||
"tenant_id": "tenant-a",
|
||||
"project_id": "project-a",
|
||||
"resource_id": resource.Id,
|
||||
"role": "frontend-developer",
|
||||
"sub_agnet_id": "agnet-worker-1",
|
||||
"permission_scope": map[string]any{
|
||||
"actions": []string{"read", "deploy"},
|
||||
},
|
||||
"constraints": map[string]any{
|
||||
"environment": "staging",
|
||||
},
|
||||
"audit_note": "grant for deployment smoke test",
|
||||
}
|
||||
ctx, recorder := newAuthenticatedContext(t, http.MethodPost, "/api/resource-grants/", body, 7)
|
||||
|
||||
CreateResourceGrant(ctx)
|
||||
|
||||
data := decodeResourceGrantData(t, recorder.Body.Bytes())
|
||||
assert.Equal(t, "tenant-a", data.TenantId)
|
||||
assert.Equal(t, "project-a", data.ProjectId)
|
||||
assert.Equal(t, resource.Id, data.ResourceId)
|
||||
assert.Equal(t, "frontend-developer", data.Role)
|
||||
assert.Equal(t, "agnet-worker-1", data.SubAgnetId)
|
||||
assert.Equal(t, "staging", data.Constraints["environment"])
|
||||
}
|
||||
@@ -281,6 +281,8 @@ func migrateDB() error {
|
||||
&CustomOAuthProvider{},
|
||||
&UserOAuthBinding{},
|
||||
&GitSource{},
|
||||
&ResourceBinding{},
|
||||
&ResourceGrant{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -330,6 +332,8 @@ func migrateDBFast() error {
|
||||
{&CustomOAuthProvider{}, "CustomOAuthProvider"},
|
||||
{&UserOAuthBinding{}, "UserOAuthBinding"},
|
||||
{&GitSource{}, "GitSource"},
|
||||
{&ResourceBinding{}, "ResourceBinding"},
|
||||
{&ResourceGrant{}, "ResourceGrant"},
|
||||
}
|
||||
// 动态计算migration数量,确保errChan缓冲区足够大
|
||||
errChan := make(chan error, len(migrations))
|
||||
|
||||
@@ -184,6 +184,22 @@ func SetApiRouter(router *gin.Engine) {
|
||||
gitSourceRoute.DELETE("/:id", controller.DeleteGitSource)
|
||||
}
|
||||
|
||||
resourceRoute := apiRouter.Group("/resources")
|
||||
resourceRoute.Use(middleware.UserAuth())
|
||||
{
|
||||
resourceRoute.GET("/", controller.ListResources)
|
||||
resourceRoute.POST("/", controller.CreateResource)
|
||||
resourceRoute.DELETE("/:id", controller.DeleteResource)
|
||||
}
|
||||
|
||||
resourceGrantRoute := apiRouter.Group("/resource-grants")
|
||||
resourceGrantRoute.Use(middleware.UserAuth())
|
||||
{
|
||||
resourceGrantRoute.GET("/", controller.ListResourceGrants)
|
||||
resourceGrantRoute.POST("/", controller.CreateResourceGrant)
|
||||
resourceGrantRoute.DELETE("/:id", controller.DeleteResourceGrant)
|
||||
}
|
||||
|
||||
optionRoute := apiRouter.Group("/option")
|
||||
optionRoute.Use(middleware.RootAuth())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user