snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
302
internal/driver/config/provider_test.go
Normal file
302
internal/driver/config/provider_test.go
Normal file
@@ -0,0 +1,302 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gobuffalo/httptest"
|
||||
"github.com/ory/x/configx"
|
||||
"github.com/ory/x/logrusx"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ory/keto/embedx"
|
||||
"github.com/ory/keto/internal/namespace"
|
||||
)
|
||||
|
||||
// createFile writes the content to a temporary file, returning the path.
|
||||
// Good for testing config files.
|
||||
func createFile(t *testing.T, content string) (path string) {
|
||||
t.Helper()
|
||||
|
||||
f, err := os.CreateTemp(t.TempDir(), "config-*.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() { _ = os.Remove(f.Name()) }) //nolint:gosec // Test file path from os.CreateTemp, not user input.
|
||||
|
||||
n, err := f.WriteString(content)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != len(content) {
|
||||
t.Fatal("failed to write the complete content")
|
||||
}
|
||||
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
// createFileF writes the content format string with the applied args to a
|
||||
// temporary file, returning the path. Good for testing config files.
|
||||
func createFileF(t *testing.T, contentF string, args ...any) (path string) {
|
||||
return createFile(t, fmt.Sprintf(contentF, args...))
|
||||
}
|
||||
|
||||
func setup(t *testing.T, configFile string) (*test.Hook, *Config) {
|
||||
t.Helper()
|
||||
hook := test.Hook{}
|
||||
l := logrusx.New("test", "today", logrusx.WithHook(&hook))
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
config, err := NewDefault(
|
||||
ctx,
|
||||
pflag.NewFlagSet("test", pflag.ContinueOnError),
|
||||
l,
|
||||
configx.WithConfigFiles(configFile),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &hook, config
|
||||
}
|
||||
|
||||
func assertNamespaces(t *testing.T, p *Config, nn ...*namespace.Namespace) {
|
||||
t.Helper()
|
||||
|
||||
nm, err := p.NamespaceManager()
|
||||
require.NoError(t, err)
|
||||
actualNamespaces, err := nm.Namespaces(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(nn), len(actualNamespaces))
|
||||
assert.ElementsMatch(t, nn, actualNamespaces)
|
||||
}
|
||||
|
||||
// The new way to configure namespaces is through the Ory Permissions Language.
|
||||
// We check here that we still support enumerating the namespaces directly in
|
||||
// the config or through a file reference, in which case there should be no
|
||||
// rewrites configured.
|
||||
func TestLegacyNamespaceConfig(t *testing.T) {
|
||||
t.Run("case=creates memory namespace manager when namespaces are set", func(t *testing.T) {
|
||||
config := createFile(t, `
|
||||
dsn: memory
|
||||
namespaces:
|
||||
- name: n0
|
||||
- name: n1
|
||||
- name: n2`)
|
||||
|
||||
run := func(namespaces []*namespace.Namespace) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
_, p := setup(t, config)
|
||||
|
||||
assertNamespaces(t, p, namespaces...)
|
||||
|
||||
nm, err := p.NamespaceManager()
|
||||
require.NoError(t, err)
|
||||
_, ok := nm.(*memoryNamespaceManager)
|
||||
assert.True(t, ok)
|
||||
assert.False(t, p.StrictMode())
|
||||
}
|
||||
}
|
||||
|
||||
nn := []*namespace.Namespace{
|
||||
{Name: "n0"},
|
||||
{Name: "n1"},
|
||||
{Name: "n2"},
|
||||
}
|
||||
nnJson, err := json.Marshal(nn)
|
||||
require.NoError(t, err)
|
||||
nnValue := make([]interface{}, 0)
|
||||
require.NoError(t, json.Unmarshal(nnJson, &nnValue))
|
||||
|
||||
t.Run(
|
||||
"type=[]*namespace.Namespace",
|
||||
run(nn),
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("case=reloads namespace manager when namespaces are updated using Set()", func(t *testing.T) {
|
||||
_, p := setup(t, createFile(t, "dsn: memory"))
|
||||
|
||||
n0 := &namespace.Namespace{
|
||||
Name: "n0",
|
||||
}
|
||||
n1 := &namespace.Namespace{
|
||||
Name: "n1",
|
||||
}
|
||||
|
||||
require.NoError(t, p.Set(KeyNamespaces, []*namespace.Namespace{n0}))
|
||||
assertNamespaces(t, p, n0)
|
||||
|
||||
require.NoError(t, p.Set(KeyNamespaces, []*namespace.Namespace{n1}))
|
||||
assertNamespaces(t, p, n1)
|
||||
})
|
||||
|
||||
t.Run("case=creates watcher manager when namespaces is string URL", func(t *testing.T) {
|
||||
_, p := setup(t, createFileF(t, `
|
||||
dsn: memory
|
||||
namespaces: file://%s`,
|
||||
t.TempDir()))
|
||||
|
||||
nm, err := p.NamespaceManager()
|
||||
require.NoError(t, err)
|
||||
_, ok := nm.(*NamespaceWatcher)
|
||||
assert.True(t, ok)
|
||||
assert.False(t, p.StrictMode())
|
||||
})
|
||||
|
||||
t.Run("case=uses passed configx provider", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cp, err := configx.New(ctx, embedx.ConfigSchema, configx.WithValue(KeyDSN, "foobar"))
|
||||
require.NoError(t, err)
|
||||
|
||||
p := New(ctx, logrusx.New("test", "today"), cp)
|
||||
assert.Equal(t, "foobar", p.DSN())
|
||||
assert.Same(t, cp, p.p)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInvalidNamespaceConfig(t *testing.T) {
|
||||
config := createFile(t, `
|
||||
dsn: memory
|
||||
namespaces:
|
||||
- name: n0
|
||||
id: 2147483648
|
||||
`)
|
||||
hook := test.Hook{}
|
||||
l := logrusx.New("test", "today", logrusx.WithHook(&hook))
|
||||
_, err := NewDefault(
|
||||
context.Background(),
|
||||
pflag.NewFlagSet("test", pflag.ContinueOnError),
|
||||
l,
|
||||
configx.WithConfigFiles(config),
|
||||
)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "must be <= 2.147483647e+09 but found 2147483648")
|
||||
}
|
||||
|
||||
// Test that the namespaces can be configured through the Ory Permission
|
||||
// Language.
|
||||
func TestRewritesNamespaceConfig(t *testing.T) {
|
||||
oplContent := `
|
||||
class User implements Namespace {
|
||||
related: {
|
||||
manager: User[]
|
||||
}
|
||||
}
|
||||
|
||||
class Group implements Namespace {
|
||||
related: {
|
||||
members: (User | Group)[]
|
||||
}
|
||||
}`
|
||||
|
||||
oplConfigFile := createFile(t, oplContent)
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
_, err := w.Write([]byte(oplContent))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}))
|
||||
t.Cleanup(func() { srv.Close() })
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
location string
|
||||
disallowPrivateIPRanges bool
|
||||
expectErr bool
|
||||
}{{
|
||||
name: "local file",
|
||||
location: "file://" + oplConfigFile,
|
||||
}, {
|
||||
name: "HTTP url forbidden",
|
||||
location: srv.URL,
|
||||
disallowPrivateIPRanges: true,
|
||||
expectErr: true,
|
||||
}, {
|
||||
name: "HTTP url allowed",
|
||||
location: srv.URL,
|
||||
}}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run("case="+tc.name, func(t *testing.T) {
|
||||
config := createFileF(t, `
|
||||
dsn: memory
|
||||
clients:
|
||||
http:
|
||||
disallow_private_ip_ranges: %v
|
||||
namespaces:
|
||||
location: %s`, tc.disallowPrivateIPRanges, tc.location)
|
||||
|
||||
_, p := setup(t, config)
|
||||
nm, err := p.NamespaceManager()
|
||||
if tc.expectErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
namespaces, err := nm.Namespaces(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, namespaces, 2)
|
||||
|
||||
names, relationNames := []string{namespaces[0].Name, namespaces[1].Name}, []string{namespaces[0].Relations[0].Name, namespaces[1].Relations[0].Name}
|
||||
|
||||
assert.False(t, p.StrictMode())
|
||||
assert.ElementsMatch(t, names, []string{"User", "Group"})
|
||||
assert.ElementsMatch(t, relationNames, []string{"manager", "members"})
|
||||
})
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
deprecated bool
|
||||
new bool
|
||||
want bool
|
||||
}{
|
||||
{name: "neither key set", deprecated: false, new: false, want: false},
|
||||
{name: "deprecated key only", deprecated: true, new: false, want: true},
|
||||
{name: "new key only", deprecated: false, new: true, want: true},
|
||||
{name: "both keys true", deprecated: true, new: true, want: true},
|
||||
} {
|
||||
t.Run("case=strict_mode/"+tt.name, func(t *testing.T) {
|
||||
cfg := createFileF(t, `
|
||||
dsn: memory
|
||||
namespaces:
|
||||
location: file://%s
|
||||
experimental_strict_mode: %v
|
||||
feature_flags:
|
||||
strict_mode: %v`, oplConfigFile, tt.deprecated, tt.new)
|
||||
|
||||
_, p := setup(t, cfg)
|
||||
assert.Equal(t, tt.want, p.StrictMode())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider_DefaultReadAPIListenOn(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config, err := NewDefault(
|
||||
ctx,
|
||||
pflag.NewFlagSet("test", pflag.ContinueOnError),
|
||||
logrusx.New("", ""),
|
||||
configx.WithValue("dsn", "foo"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
addr, listenFile := config.ReadAPIListenOn()
|
||||
assert.Equal(t, ":4466", addr)
|
||||
assert.Zero(t, listenFile)
|
||||
}
|
||||
Reference in New Issue
Block a user