feat: expand vehicle data platform capabilities
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
@@ -86,6 +87,10 @@ func TestAPIAuthEnforcesTokensAndRoleBoundaries(t *testing.T) {
|
||||
if operatorRule.Code != http.StatusForbidden {
|
||||
t.Fatalf("operator rule mutation should be forbidden, status=%d", operatorRule.Code)
|
||||
}
|
||||
operatorRollback := authRequest(t, cfg, http.MethodPost, "/api/v2/alerts/rules/rule-1/rollback", operatorToken)
|
||||
if operatorRollback.Code != http.StatusForbidden {
|
||||
t.Fatalf("operator rule rollback should be forbidden, status=%d", operatorRollback.Code)
|
||||
}
|
||||
operatorProfile := authRequest(t, cfg, http.MethodPut, "/api/v2/vehicles/VIN001/profile", operatorToken)
|
||||
if operatorProfile.Code != http.StatusForbidden {
|
||||
t.Fatalf("operator profile mutation should be forbidden, status=%d", operatorProfile.Code)
|
||||
@@ -94,6 +99,10 @@ func TestAPIAuthEnforcesTokensAndRoleBoundaries(t *testing.T) {
|
||||
if operatorProfileSync.Code != http.StatusForbidden {
|
||||
t.Fatalf("operator profile sync should be forbidden, status=%d", operatorProfileSync.Code)
|
||||
}
|
||||
operatorIdentityClaim := authRequest(t, cfg, http.MethodPost, "/api/v2/access/unresolved-identities/identity-1/claim", operatorToken)
|
||||
if operatorIdentityClaim.Code != http.StatusForbidden {
|
||||
t.Fatalf("operator identity claim should be forbidden, status=%d", operatorIdentityClaim.Code)
|
||||
}
|
||||
viewerSourceDiagnostic := authRequest(t, cfg, http.MethodGet, "/api/v2/operations/vehicles/VIN001/sources", viewerToken)
|
||||
if viewerSourceDiagnostic.Code != http.StatusForbidden {
|
||||
t.Fatalf("viewer source diagnostic should be forbidden, status=%d", viewerSourceDiagnostic.Code)
|
||||
@@ -114,6 +123,18 @@ func TestAPIAuthEnforcesTokensAndRoleBoundaries(t *testing.T) {
|
||||
if operatorReconciliation.Code != http.StatusNoContent {
|
||||
t.Fatalf("operator reconciliation action status=%d body=%s", operatorReconciliation.Code, operatorReconciliation.Body.String())
|
||||
}
|
||||
operatorReconciliationArchive := authRequest(t, cfg, http.MethodPost, "/api/v2/reconciliation/issues/reconciliation-1/archive", operatorToken)
|
||||
if operatorReconciliationArchive.Code != http.StatusForbidden {
|
||||
t.Fatalf("operator reconciliation archive should be forbidden, status=%d", operatorReconciliationArchive.Code)
|
||||
}
|
||||
adminReconciliationArchive := authRequest(t, cfg, http.MethodPost, "/api/v2/reconciliation/issues/reconciliation-1/archive", adminToken)
|
||||
if adminReconciliationArchive.Code != http.StatusNoContent {
|
||||
t.Fatalf("admin reconciliation archive status=%d body=%s", adminReconciliationArchive.Code, adminReconciliationArchive.Body.String())
|
||||
}
|
||||
operatorOpenPlatform := authRequest(t, cfg, http.MethodGet, "/api/v2/open-platform/apps", operatorToken)
|
||||
if operatorOpenPlatform.Code != http.StatusForbidden {
|
||||
t.Fatalf("operator open-platform management should be forbidden, status=%d", operatorOpenPlatform.Code)
|
||||
}
|
||||
adminProfile := authRequest(t, cfg, http.MethodPut, "/api/v2/vehicles/VIN001/profile", adminToken)
|
||||
if adminProfile.Code != http.StatusNoContent || adminProfile.Header().Get("X-Principal") != "admin-a:admin" {
|
||||
t.Fatalf("admin profile mutation status=%d principal=%s", adminProfile.Code, adminProfile.Header().Get("X-Principal"))
|
||||
@@ -126,10 +147,45 @@ func TestAPIAuthEnforcesTokensAndRoleBoundaries(t *testing.T) {
|
||||
if adminThreshold.Code != http.StatusNoContent {
|
||||
t.Fatalf("admin threshold status=%d body=%s", adminThreshold.Code, adminThreshold.Body.String())
|
||||
}
|
||||
adminIdentityClaim := authRequest(t, cfg, http.MethodPost, "/api/v2/access/unresolved-identities/identity-1/claim", adminToken)
|
||||
if adminIdentityClaim.Code != http.StatusNoContent {
|
||||
t.Fatalf("admin identity claim status=%d body=%s", adminIdentityClaim.Code, adminIdentityClaim.Body.String())
|
||||
}
|
||||
adminSourcePolicy := authRequest(t, cfg, http.MethodPut, "/api/v2/operations/vehicles/VIN001/sources/ref", adminToken)
|
||||
if adminSourcePolicy.Code != http.StatusNoContent {
|
||||
t.Fatalf("admin source policy status=%d body=%s", adminSourcePolicy.Code, adminSourcePolicy.Body.String())
|
||||
}
|
||||
adminOpenPlatform := authRequest(t, cfg, http.MethodGet, "/api/v2/open-platform/apps", adminToken)
|
||||
if adminOpenPlatform.Code != http.StatusNoContent {
|
||||
t.Fatalf("admin open-platform management status=%d body=%s", adminOpenPlatform.Code, adminOpenPlatform.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisabledMockModeProvidesOperableIdentityDirectory(t *testing.T) {
|
||||
cfg := config.Config{AuthMode: "disabled", DataMode: "mock"}
|
||||
handler := withAPIAuth(http.NotFoundHandler(), cfg)
|
||||
|
||||
listResponse := httptest.NewRecorder()
|
||||
handler.ServeHTTP(listResponse, httptest.NewRequest(http.MethodGet, "/api/v2/admin/users", nil))
|
||||
if listResponse.Code != http.StatusOK {
|
||||
t.Fatalf("mock directory status=%d body=%s", listResponse.Code, listResponse.Body.String())
|
||||
}
|
||||
var listEnvelope struct {
|
||||
Data []authUser `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(listResponse.Body.Bytes(), &listEnvelope); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(listEnvelope.Data) < 4 || listEnvelope.Data[1].AuthProvider != "OneOS" || listEnvelope.Data[1].ExternalSubject == "" || listEnvelope.Data[2].ExternalSubject != "" {
|
||||
t.Fatalf("mock identity examples are incomplete: %+v", listEnvelope.Data)
|
||||
}
|
||||
|
||||
updateBody := `{"displayName":"华东数据客户","password":"ChangeMe2026!","status":"enabled","customerRef":"CUS-EAST","tenantRef":"tenant-east","menuKeys":["monitor"],"vehicleVins":["LMRKH9AC2R1004087"]}`
|
||||
updateResponse := httptest.NewRecorder()
|
||||
handler.ServeHTTP(updateResponse, httptest.NewRequest(http.MethodPut, "/api/v2/admin/users/102", strings.NewReader(updateBody)))
|
||||
if updateResponse.Code != http.StatusBadRequest || !strings.Contains(updateResponse.Body.String(), "EXTERNAL_IDENTITY_PASSWORD_READ_ONLY") {
|
||||
t.Fatalf("mock external credential ownership was not enforced: status=%d body=%s", updateResponse.Code, updateResponse.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIAuthSessionAndDisabledMode(t *testing.T) {
|
||||
@@ -156,6 +212,18 @@ func TestAuthSelfServiceEndpointsAllowCustomerRole(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMileagePostQueriesAllowCustomerRole(t *testing.T) {
|
||||
for _, path := range []string{"/api/mileage/daily", "/api/v2/statistics/mileage"} {
|
||||
req := httptest.NewRequest(http.MethodPost, path, nil)
|
||||
if role := requiredRole(req); role != "viewer" {
|
||||
t.Fatalf("%s should allow authenticated customers, required role=%s", path, role)
|
||||
}
|
||||
if menu := requiredMenu(req); menu != "statistics" {
|
||||
t.Fatalf("%s should use statistics menu scope, required menu=%s", path, menu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIAuthMisconfigurationFailsClosed(t *testing.T) {
|
||||
cases := []config.Config{
|
||||
{AuthMode: "enforce"},
|
||||
|
||||
Reference in New Issue
Block a user