Skip to content

Commit 9bd72d0

Browse files
committed
add outpost blueprint tests; update forward-auth docs
- Add tests for GenerateOutpostBlueprint (provider refs, config values) - Add test for blueprint removal when no providers - Update authentik-integration.md to document blueprint-based approach
1 parent 3115277 commit 9bd72d0

File tree

2 files changed

+98
-4
lines changed

2 files changed

+98
-4
lines changed

docs/design/authentik-integration.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,21 @@ entries:
232232

233233
2. **external_host must be root URL**: Set to `http://localhost:8080` (not `http://localhost:8080/embed/qbittorrent/`). The OAuth callback path `/outpost.goauthentik.io/callback` is appended to this, and Traefik only routes this at the root level.
234234

235-
3. **Embedded outpost association**: After creating the proxy provider via blueprint, the host-agent must add it to the "authentik Embedded Outpost" via API. Blueprints create providers but don't automatically associate them with outposts.
235+
3. **Embedded outpost association**: The host-agent generates a `bloud-outpost.yaml` blueprint that adds all forward-auth proxy providers to the embedded outpost and configures `authentik_host`/`authentik_host_browser` for correct OAuth redirects. This blueprint uses `!Find` to reference providers by name, so they can be created by separate app blueprints.
236236

237-
```go
238-
// Host-agent adds provider to embedded outpost after blueprint applies
239-
client.AddProviderToEmbeddedOutpost("qBittorrent Proxy Provider")
237+
```yaml
238+
# bloud-outpost.yaml (auto-generated)
239+
entries:
240+
- model: authentik_outposts.outpost
241+
state: present
242+
identifiers:
243+
name: authentik Embedded Outpost
244+
attrs:
245+
providers:
246+
- !Find [authentik_providers_proxy.proxyprovider, [name, "App Proxy Provider"]]
247+
config:
248+
authentik_host: "http://localhost:8080"
249+
authentik_host_browser: "http://localhost:8080"
240250
```
241251
242252
---

services/host-agent/internal/sso/blueprint_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,87 @@ func TestDeleteBlueprint_NonExistent(t *testing.T) {
175175
t.Errorf("DeleteBlueprint should not error for non-existent file: %v", err)
176176
}
177177
}
178+
179+
func TestGenerateOutpostBlueprint(t *testing.T) {
180+
dir := t.TempDir()
181+
182+
gen := NewBlueprintGenerator(
183+
"test-secret",
184+
"http://localhost:8080",
185+
"http://localhost:8080",
186+
dir,
187+
)
188+
189+
providers := []ForwardAuthProvider{
190+
{DisplayName: "App One"},
191+
{DisplayName: "App Two"},
192+
}
193+
194+
err := gen.GenerateOutpostBlueprint(providers)
195+
if err != nil {
196+
t.Fatalf("GenerateOutpostBlueprint failed: %v", err)
197+
}
198+
199+
// Read the generated file
200+
content, err := os.ReadFile(filepath.Join(dir, "bloud-outpost.yaml"))
201+
if err != nil {
202+
t.Fatalf("ReadFile failed: %v", err)
203+
}
204+
205+
contentStr := string(content)
206+
t.Logf("Generated blueprint:\n%s", contentStr)
207+
208+
// Verify it references the embedded outpost
209+
if !strings.Contains(contentStr, "authentik Embedded Outpost") {
210+
t.Error("Expected 'authentik Embedded Outpost' identifier not found")
211+
}
212+
213+
// Verify provider references using !Find syntax
214+
if !strings.Contains(contentStr, `"App One Proxy Provider"`) {
215+
t.Error("Expected App One provider reference not found")
216+
}
217+
if !strings.Contains(contentStr, `"App Two Proxy Provider"`) {
218+
t.Error("Expected App Two provider reference not found")
219+
}
220+
221+
// Verify config includes authentik_host and authentik_host_browser
222+
if !strings.Contains(contentStr, `authentik_host: "http://localhost:8080"`) {
223+
t.Error("Expected authentik_host config not found")
224+
}
225+
if !strings.Contains(contentStr, `authentik_host_browser: "http://localhost:8080"`) {
226+
t.Error("Expected authentik_host_browser config not found")
227+
}
228+
}
229+
230+
func TestGenerateOutpostBlueprint_NoProviders(t *testing.T) {
231+
dir := t.TempDir()
232+
233+
gen := NewBlueprintGenerator(
234+
"test-secret",
235+
"http://localhost:8080",
236+
"http://localhost:8080",
237+
dir,
238+
)
239+
240+
// Create a blueprint file first
241+
err := gen.GenerateOutpostBlueprint([]ForwardAuthProvider{{DisplayName: "Test"}})
242+
if err != nil {
243+
t.Fatalf("GenerateOutpostBlueprint failed: %v", err)
244+
}
245+
246+
blueprintPath := filepath.Join(dir, "bloud-outpost.yaml")
247+
if _, err := os.Stat(blueprintPath); os.IsNotExist(err) {
248+
t.Fatal("Blueprint file was not created")
249+
}
250+
251+
// Call with empty providers - should remove the file
252+
err = gen.GenerateOutpostBlueprint([]ForwardAuthProvider{})
253+
if err != nil {
254+
t.Fatalf("GenerateOutpostBlueprint with empty providers failed: %v", err)
255+
}
256+
257+
// Verify file is removed
258+
if _, err := os.Stat(blueprintPath); !os.IsNotExist(err) {
259+
t.Error("Blueprint file should be removed when no providers")
260+
}
261+
}

0 commit comments

Comments
 (0)