mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
config: Add API to expose OPA's active config
This commit adds a new API endpoint to fetch OPA's active configuration. When the discovery feature is enabled, this API can be used to fetch the discovered configuration in the last evaluated discovery bundle. Fixes: #2020 Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
This commit is contained in:
+99
-13
@@ -7,6 +7,7 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -18,19 +19,19 @@ import (
|
||||
|
||||
// Config represents the configuration file that OPA can be started with.
|
||||
type Config struct {
|
||||
Services json.RawMessage `json:"services"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Discovery json.RawMessage `json:"discovery"`
|
||||
Bundle json.RawMessage `json:"bundle"` // Deprecated: Use `bundles` instead
|
||||
Bundles json.RawMessage `json:"bundles"`
|
||||
DecisionLogs json.RawMessage `json:"decision_logs"`
|
||||
Status json.RawMessage `json:"status"`
|
||||
Plugins map[string]json.RawMessage `json:"plugins"`
|
||||
Keys json.RawMessage `json:"keys"`
|
||||
DefaultDecision *string `json:"default_decision"`
|
||||
DefaultAuthorizationDecision *string `json:"default_authorization_decision"`
|
||||
Caching json.RawMessage `json:"caching"`
|
||||
PersistenceDirectory *string `json:"persistence_directory"`
|
||||
Services json.RawMessage `json:"services,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Discovery json.RawMessage `json:"discovery,omitempty"`
|
||||
Bundle json.RawMessage `json:"bundle,omitempty"` // Deprecated: Use `bundles` instead
|
||||
Bundles json.RawMessage `json:"bundles,omitempty"`
|
||||
DecisionLogs json.RawMessage `json:"decision_logs,omitempty"`
|
||||
Status json.RawMessage `json:"status,omitempty"`
|
||||
Plugins map[string]json.RawMessage `json:"plugins,omitempty"`
|
||||
Keys json.RawMessage `json:"keys,omitempty"`
|
||||
DefaultDecision *string `json:"default_decision,omitempty"`
|
||||
DefaultAuthorizationDecision *string `json:"default_authorization_decision,omitempty"`
|
||||
Caching json.RawMessage `json:"caching,omitempty"`
|
||||
PersistenceDirectory *string `json:"persistence_directory,omitempty"`
|
||||
}
|
||||
|
||||
// ParseConfig returns a valid Config object with defaults injected. The id
|
||||
@@ -105,6 +106,91 @@ func (c Config) GetPersistenceDirectory() (string, error) {
|
||||
return *c.PersistenceDirectory, nil
|
||||
}
|
||||
|
||||
// ActiveConfig returns OPA's active configuration
|
||||
// with the credentials and crypto keys removed
|
||||
func (c *Config) ActiveConfig() (interface{}, error) {
|
||||
bs, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
if err := util.Unmarshal(bs, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result["services"] != nil {
|
||||
err = removeServiceCredentials(result["services"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if result["keys"] != nil {
|
||||
err = removeCryptoKeys(result["keys"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func removeServiceCredentials(x interface{}) error {
|
||||
|
||||
switch x := x.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range x {
|
||||
err := removeKey(v, "credentials")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case map[string]interface{}:
|
||||
for _, v := range x {
|
||||
err := removeKey(v, "credentials")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("illegal service config type: %T", x)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeCryptoKeys(x interface{}) error {
|
||||
|
||||
switch x := x.(type) {
|
||||
case map[string]interface{}:
|
||||
for _, v := range x {
|
||||
err := removeKey(v, "key", "private_key")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("illegal keys config type: %T", x)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeKey(x interface{}, keys ...string) error {
|
||||
val, ok := x.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("type assertion error")
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
delete(val, key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
defaultDecisionPath = "/system/main"
|
||||
defaultAuthorizationDecisionPath = "/system/authz/allow"
|
||||
|
||||
@@ -6,9 +6,14 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
"github.com/open-policy-agent/opa/version"
|
||||
)
|
||||
|
||||
func TestConfigPluginsEnabled(t *testing.T) {
|
||||
@@ -98,3 +103,193 @@ func TestPersistDirectory(t *testing.T) {
|
||||
t.Errorf("expected peristDir %v and dir %v to be equal", persistDir, dir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveConfig(t *testing.T) {
|
||||
|
||||
common := `"labels": {
|
||||
"region": "west"
|
||||
},
|
||||
"keys": {
|
||||
"global_key": {
|
||||
"algorithm": HS256,
|
||||
"key": "secret"
|
||||
},
|
||||
"local_key": {
|
||||
"private_key": "some_private_key"
|
||||
}
|
||||
},
|
||||
"decision_logs": {
|
||||
"service": "acmecorp",
|
||||
"reporting": {
|
||||
"min_delay_seconds": 300,
|
||||
"max_delay_seconds": 600
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"some-plugin": {}
|
||||
},
|
||||
"discovery": {"name": "config"}`
|
||||
|
||||
serviceObj := `"services": {
|
||||
"acmecorp": {
|
||||
"url": "https://example.com/control-plane-api/v1",
|
||||
"response_header_timeout_seconds": 5,
|
||||
"headers": {"foo": "bar"},
|
||||
"credentials": {"bearer": {"token": "test"}}
|
||||
},
|
||||
"opa.example.com": {
|
||||
"url": "https://opa.example.com",
|
||||
"headers": {"foo": "bar"},
|
||||
"credentials": {"gcp_metadata": {"audience": "test"}}
|
||||
}
|
||||
},`
|
||||
|
||||
servicesList := `"services": [
|
||||
{
|
||||
"name": "acmecorp",
|
||||
"url": "https://example.com/control-plane-api/v1",
|
||||
"response_header_timeout_seconds": 5,
|
||||
"headers": {"foo": "bar"},
|
||||
"credentials": {"bearer": {"token": "test"}}
|
||||
},
|
||||
{
|
||||
"name": "opa.example.com",
|
||||
"url": "https://opa.example.com",
|
||||
"headers": {"foo": "bar"},
|
||||
"credentials": {"gcp_metadata": {"audience": "test"}}
|
||||
}
|
||||
],`
|
||||
|
||||
expectedCommon := fmt.Sprintf(`"labels": {
|
||||
"id": "foo",
|
||||
"version": %v,
|
||||
"region": "west"
|
||||
},
|
||||
"keys": {
|
||||
"global_key": {
|
||||
"algorithm": HS256
|
||||
},
|
||||
"local_key": {}
|
||||
},
|
||||
"decision_logs": {
|
||||
"service": "acmecorp",
|
||||
"reporting": {
|
||||
"min_delay_seconds": 300,
|
||||
"max_delay_seconds": 600
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"some-plugin": {}
|
||||
},
|
||||
"default_authorization_decision": "/system/authz/allow",
|
||||
"default_decision": "/system/main",
|
||||
"discovery": {"name": "config"}`, version.Version)
|
||||
|
||||
expectedServiceObj := `"services": {
|
||||
"acmecorp": {
|
||||
"url": "https://example.com/control-plane-api/v1",
|
||||
"response_header_timeout_seconds": 5,
|
||||
"headers": {"foo": "bar"}
|
||||
},
|
||||
"opa.example.com": {
|
||||
"url": "https://opa.example.com",
|
||||
"headers": {"foo": "bar"}
|
||||
}
|
||||
},`
|
||||
|
||||
expectedServicesList := `"services": [
|
||||
{
|
||||
"name": "acmecorp",
|
||||
"url": "https://example.com/control-plane-api/v1",
|
||||
"response_header_timeout_seconds": 5,
|
||||
"headers": {"foo": "bar"}
|
||||
},
|
||||
{
|
||||
"name": "opa.example.com",
|
||||
"url": "https://opa.example.com",
|
||||
"headers": {"foo": "bar"}
|
||||
}
|
||||
],`
|
||||
|
||||
badKeysConfig := []byte(`{
|
||||
"keys": [
|
||||
{
|
||||
"algorithm": "HS256"
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
badServicesConfig := []byte(`{
|
||||
"services": {
|
||||
"acmecorp": ["foo"]
|
||||
}
|
||||
}`)
|
||||
|
||||
tests := map[string]struct {
|
||||
raw []byte
|
||||
expected []byte
|
||||
wantErr bool
|
||||
err error
|
||||
}{
|
||||
"valid_config_with_svc_object": {
|
||||
[]byte(fmt.Sprintf(`{ %v %v }`, serviceObj, common)),
|
||||
[]byte(fmt.Sprintf(`{ %v %v }`, expectedServiceObj, expectedCommon)),
|
||||
false,
|
||||
nil,
|
||||
},
|
||||
"valid_config_with_svc_list": {
|
||||
[]byte(fmt.Sprintf(`{ %v %v }`, servicesList, common)),
|
||||
[]byte(fmt.Sprintf(`{ %v %v }`, expectedServicesList, expectedCommon)),
|
||||
false,
|
||||
nil,
|
||||
},
|
||||
"invalid_config_with_bad_keys": {
|
||||
badKeysConfig,
|
||||
nil,
|
||||
true,
|
||||
fmt.Errorf("illegal keys config type: []interface {}"),
|
||||
},
|
||||
"invalid_config_with_bad_creds": {
|
||||
badServicesConfig,
|
||||
nil,
|
||||
true,
|
||||
fmt.Errorf("type assertion error"),
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
conf, err := ParseConfig(tc.raw, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err := conf.ActiveConfig()
|
||||
|
||||
if tc.wantErr {
|
||||
if err == nil {
|
||||
t.Fatal("Expected error but got nil")
|
||||
}
|
||||
|
||||
if tc.err != nil && tc.err.Error() != err.Error() {
|
||||
t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error())
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
var expected map[string]interface{}
|
||||
if err := util.Unmarshal(tc.expected, &expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("want %v got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1828,3 +1828,67 @@ Content-Type: application/json
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
## Config API
|
||||
|
||||
The `/config` API endpoint returns OPA's active configuration. When the discovery feature is enabled, this API can be
|
||||
used to fetch the discovered configuration in the last evaluated discovery bundle. The `credentials` field in the
|
||||
[Services](../configuration#services) configuration and the `private_key` and `key` fields in the [Keys](../configuration#keys)
|
||||
configuration will be omitted from the API response.
|
||||
|
||||
### Get Config
|
||||
|
||||
```
|
||||
GET /v1/config HTTP/1.1
|
||||
```
|
||||
|
||||
#### Query Parameters
|
||||
|
||||
- **pretty** - If parameter is `true`, response will formatted for humans.
|
||||
|
||||
#### Status Codes
|
||||
|
||||
- **200** - no error
|
||||
- **500** - server error
|
||||
|
||||
#### Example Request
|
||||
```http
|
||||
GET /v1/config HTTP/1.1
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
```
|
||||
```json
|
||||
{
|
||||
"services": {
|
||||
"acmecorp": {
|
||||
"url": "https://example.com/control-plane-api/v1"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"id": "test-id",
|
||||
"version": "0.27.0"
|
||||
},
|
||||
"keys": {
|
||||
"global_key": {
|
||||
"scope": "read"
|
||||
}
|
||||
},
|
||||
"decision_logs": {
|
||||
"service": "acmecorp"
|
||||
},
|
||||
"status": {
|
||||
"service": "acmecorp"
|
||||
},
|
||||
"bundles": {
|
||||
"authz": {
|
||||
"service": "acmecorp"
|
||||
}
|
||||
},
|
||||
"default_authorization_decision": "/system/authz/allow",
|
||||
"default_decision": "/system/main"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -170,6 +170,173 @@ func TestProcessBundle(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestProcessBundleWithActiveConfig(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
manager, err := plugins.New([]byte(`{
|
||||
"labels": {"x": "y"},
|
||||
"services": {
|
||||
"localhost": {
|
||||
"url": "http://localhost:9999",
|
||||
"credentials": {"bearer": {"token": "test"}}
|
||||
}
|
||||
},
|
||||
"keys": {
|
||||
"local_key": {
|
||||
"private_key": "local"
|
||||
}
|
||||
},
|
||||
"discovery": {"name": "config"},
|
||||
}`), "test-id", inmem.New())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
initialBundle := makeDataBundle(1, `
|
||||
{
|
||||
"config": {
|
||||
"services": {
|
||||
"acmecorp": {
|
||||
"url": "https://example.com/control-plane-api/v1",
|
||||
"credentials": {"bearer": {"token": "test-acmecorp"}}
|
||||
}
|
||||
},
|
||||
"bundles": {"test-bundle": {"service": "localhost"}},
|
||||
"status": {"partition_name": "foo"},
|
||||
"decision_logs": {"partition_name": "bar"},
|
||||
"default_decision": "bar/baz",
|
||||
"default_authorization_decision": "baz/qux",
|
||||
"keys": {
|
||||
"global_key": {
|
||||
"scope": "read",
|
||||
"key": "secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
disco, err := New(manager)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = disco.processBundle(ctx, initialBundle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err := manager.Config.ActiveConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedConfig := fmt.Sprintf(`{
|
||||
"services": {
|
||||
"acmecorp": {
|
||||
"url": "https://example.com/control-plane-api/v1"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"id": "test-id",
|
||||
"version": %v,
|
||||
"x": "y"
|
||||
},
|
||||
"keys": {
|
||||
"global_key": {
|
||||
"scope": "read"
|
||||
}
|
||||
},
|
||||
"decision_logs": {
|
||||
"partition_name": "bar"
|
||||
},
|
||||
"status": {
|
||||
"partition_name": "foo"
|
||||
},
|
||||
"bundles": {
|
||||
"test-bundle": {
|
||||
"service": "localhost"
|
||||
}
|
||||
},
|
||||
"default_authorization_decision": "baz/qux",
|
||||
"default_decision": "bar/baz"}`, version.Version)
|
||||
|
||||
var expected map[string]interface{}
|
||||
if err := util.Unmarshal([]byte(expectedConfig), &expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("want %v got %v", expected, actual)
|
||||
}
|
||||
|
||||
initialBundle = makeDataBundle(2, `
|
||||
{
|
||||
"config": {
|
||||
"services": {
|
||||
"opa.example.com": {
|
||||
"url": "https://opa.example.com",
|
||||
"credentials": {"bearer": {"token": "test-opa"}}
|
||||
}
|
||||
},
|
||||
"bundles": {"test-bundle-2": {"service": "opa.example.com"}},
|
||||
"decision_logs": {},
|
||||
"keys": {
|
||||
"global_key_2": {
|
||||
"scope": "write",
|
||||
"key": "secret_2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
_, err = disco.processBundle(ctx, initialBundle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
actual, err = manager.Config.ActiveConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedConfig2 := fmt.Sprintf(`{
|
||||
"services": {
|
||||
"opa.example.com": {
|
||||
"url": "https://opa.example.com"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"id": "test-id",
|
||||
"version": %v,
|
||||
"x": "y"
|
||||
},
|
||||
"keys": {
|
||||
"global_key_2": {
|
||||
"scope": "write"
|
||||
}
|
||||
},
|
||||
"decision_logs": {},
|
||||
"bundles": {
|
||||
"test-bundle-2": {
|
||||
"service": "opa.example.com"
|
||||
}
|
||||
},
|
||||
"default_authorization_decision": "/system/authz/allow",
|
||||
"default_decision": "/system/main"}`, version.Version)
|
||||
|
||||
var expected2 map[string]interface{}
|
||||
if err := util.Unmarshal([]byte(expectedConfig2), &expected2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected2) {
|
||||
t.Fatalf("want %v got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
type testFactory struct {
|
||||
p *reconfigureTestPlugin
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ const (
|
||||
PromHandlerV1Query = "v1/query"
|
||||
PromHandlerV1Policies = "v1/policies"
|
||||
PromHandlerV1Compile = "v1/compile"
|
||||
PromHandlerV1Config = "v1/config"
|
||||
PromHandlerIndex = "index"
|
||||
PromHandlerCatch = "catchall"
|
||||
PromHandlerHealth = "health"
|
||||
@@ -612,6 +613,7 @@ func (s *Server) initRouters() {
|
||||
s.registerHandler(mainRouter, 1, "/query", http.MethodGet, s.instrumentHandler(s.v1QueryGet, PromHandlerV1Query))
|
||||
s.registerHandler(mainRouter, 1, "/query", http.MethodPost, s.instrumentHandler(s.v1QueryPost, PromHandlerV1Query))
|
||||
s.registerHandler(mainRouter, 1, "/compile", http.MethodPost, s.instrumentHandler(s.v1CompilePost, PromHandlerV1Compile))
|
||||
s.registerHandler(mainRouter, 1, "/config", http.MethodGet, s.instrumentHandler(s.v1ConfigGet, PromHandlerV1Config))
|
||||
mainRouter.Handle("/", s.instrumentHandler(s.unversionedPost, PromHandlerIndex)).Methods(http.MethodPost)
|
||||
mainRouter.Handle("/", s.instrumentHandler(s.indexGet, PromHandlerIndex)).Methods(http.MethodGet)
|
||||
|
||||
@@ -2004,6 +2006,17 @@ func (s *Server) v1QueryPost(w http.ResponseWriter, r *http.Request) {
|
||||
writer.JSON(w, 200, results, pretty)
|
||||
}
|
||||
|
||||
func (s *Server) v1ConfigGet(w http.ResponseWriter, r *http.Request) {
|
||||
pretty := getBoolParam(r.URL, types.ParamPrettyV1, true)
|
||||
result, err := s.manager.Config.ActiveConfig()
|
||||
if err != nil {
|
||||
writer.ErrorAuto(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writer.JSON(w, 200, result, pretty)
|
||||
}
|
||||
|
||||
func (s *Server) checkPolicyIDScope(ctx context.Context, txn storage.Transaction, id string) error {
|
||||
|
||||
bs, err := s.store.GetPolicy(ctx, txn, id)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/bundle"
|
||||
"github.com/open-policy-agent/opa/config"
|
||||
"github.com/open-policy-agent/opa/metrics"
|
||||
"github.com/open-policy-agent/opa/plugins"
|
||||
pluginBundle "github.com/open-policy-agent/opa/plugins/bundle"
|
||||
@@ -1178,6 +1179,68 @@ p = true { false }`
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigV1(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
|
||||
c := []byte(`{"services": {
|
||||
"acmecorp": {
|
||||
"url": "https://example.com/control-plane-api/v1",
|
||||
"credentials": {"bearer": {"token": "test"}}
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"region": "west"
|
||||
},
|
||||
"keys": {
|
||||
"global_key": {
|
||||
"algorithm": HS256,
|
||||
"key": "secret"
|
||||
}
|
||||
}}`)
|
||||
|
||||
conf, err := config.ParseConfig(c, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f.server.manager.Config = conf
|
||||
|
||||
expected := map[string]interface{}{}
|
||||
expected["labels"] = map[string]interface{}{"id": "foo", "version": version.Version, "region": "west"}
|
||||
expected["keys"] = map[string]interface{}{"global_key": map[string]interface{}{"algorithm": "HS256"}}
|
||||
expected["services"] = map[string]interface{}{"acmecorp": map[string]interface{}{"url": "https://example.com/control-plane-api/v1"}}
|
||||
expected["default_authorization_decision"] = "/system/authz/allow"
|
||||
expected["default_decision"] = "/system/main"
|
||||
|
||||
bs, err := json.Marshal(expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := f.v1(http.MethodGet, "/config", "", 200, string(bs)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
badServicesConfig := []byte(`{
|
||||
"services": {
|
||||
"acmecorp": ["foo"]
|
||||
}
|
||||
}`)
|
||||
|
||||
conf, err = config.ParseConfig(badServicesConfig, "foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f.server.manager.Config = conf
|
||||
|
||||
if err := f.v1(http.MethodGet, "/config", "", 500, `{
|
||||
"code": "internal_error",
|
||||
"message": "type assertion error"}`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataYAML(t *testing.T) {
|
||||
|
||||
testMod1 := `package testmod
|
||||
|
||||
Reference in New Issue
Block a user