1package apps_test
2
3import (
4 "context"
5 "testing"
6
7 "github.com/bnema/zerowrap"
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10
11 outmocks "github.com/bnema/gordon/internal/boundaries/out/mocks"
12 "github.com/bnema/gordon/internal/domain"
13 "github.com/bnema/gordon/internal/usecase/apps"
14)
15
16func capabilityPolicy(appNames, services []string) domain.AppCapabilityPolicy {
17 return domain.AppCapabilityPolicy{
18 Name: "net-admin",
19 Add: []string{"NET_ADMIN"},
20 AllowedApps: appNames,
21 AllowedServices: services,
22 }
23}
24
25func TestApply_AcceptsCapabilitiesUnderAdministrativePolicy(t *testing.T) {
26 store := outmocks.NewMockAppState(t)
27 spec := testSpec("blog")
28 spec.Services[0].Capabilities = []string{"net-admin"}
29 applySuccess(store, spec)
30
31 svc := apps.NewService(store, zerowrap.Default()).
32 WithCapabilityPolicies(map[string]domain.AppCapabilityPolicy{
33 "net-admin": capabilityPolicy([]string{"blog"}, []string{"web"}),
34 })
35 _, _, err := svc.Apply(context.Background(), spec, []byte("manifest"), false)
36 require.NoError(t, err)
37}
38
39func TestApply_RejectsCapabilityWithoutPolicyBeforeStoreAccess(t *testing.T) {
40 store := outmocks.NewMockAppState(t)
41 spec := testSpec("blog")
42 spec.Services[0].Capabilities = []string{"net-admin"}
43 svc := apps.NewService(store, zerowrap.Default())
44
45 _, _, err := svc.Apply(context.Background(), spec, []byte("manifest"), false)
46
47 require.ErrorIs(t, err, domain.ErrCapabilityPolicy)
48 assert.Contains(t, err.Error(), "blog")
49 assert.Contains(t, err.Error(), "web")
50 assert.Contains(t, err.Error(), "net-admin")
51}
52
53func TestApply_RejectsCapabilityOutsideAllowlists(t *testing.T) {
54 spec := testSpec("blog")
55 spec.Services[0].Capabilities = []string{"net-admin"}
56 for name, policy := range map[string]domain.AppCapabilityPolicy{
57 "app not allowed": capabilityPolicy([]string{"other"}, []string{"web"}),
58 "service not allowed": capabilityPolicy([]string{"blog"}, []string{"other"}),
59 } {
60 t.Run(name, func(t *testing.T) {
61 store := outmocks.NewMockAppState(t)
62 svc := apps.NewService(store, zerowrap.Default()).
63 WithCapabilityPolicies(map[string]domain.AppCapabilityPolicy{"net-admin": policy})
64 _, _, err := svc.Apply(context.Background(), spec, []byte("manifest"), false)
65 require.ErrorIs(t, err, domain.ErrCapabilityPolicy)
66 assert.NotContains(t, err.Error(), "NET_ADMIN")
67 })
68 }
69}
70
71func TestApply_CapabilityPoliciesReplaceOnReload(t *testing.T) {
72 spec := testSpec("blog")
73 spec.Services[0].Capabilities = []string{"net-admin"}
74
75 store := outmocks.NewMockAppState(t)
76 applySuccess(store, spec)
77 svc := apps.NewService(store, zerowrap.Default()).
78 WithCapabilityPolicies(map[string]domain.AppCapabilityPolicy{
79 "net-admin": capabilityPolicy([]string{"blog"}, []string{"web"}),
80 })
81 _, _, err := svc.Apply(context.Background(), spec, []byte("manifest"), false)
82 require.NoError(t, err)
83
84 svc.SetCapabilityPolicies(nil)
85 _, _, err = svc.Apply(context.Background(), spec, []byte("manifest"), false)
86 require.ErrorIs(t, err, domain.ErrCapabilityPolicy)
87}
88
89func TestApply_CapabilityDryRunRefusedWithoutMutation(t *testing.T) {
90 store := outmocks.NewMockAppState(t)
91 spec := testSpec("blog")
92 spec.Services[0].Capabilities = []string{"net-admin"}
93 svc := apps.NewService(store, zerowrap.Default())
94
95 _, _, err := svc.Apply(context.Background(), spec, []byte("manifest"), true)
96 require.ErrorIs(t, err, domain.ErrCapabilityPolicy)
97}