docs: preserve reviewed NetKingdom session artifacts
This commit is contained in:
parent
d6168526c5
commit
d9a74861ba
44 changed files with 18977 additions and 0 deletions
|
|
@ -0,0 +1,708 @@
|
|||
[
|
||||
{
|
||||
"operation": [
|
||||
"migrate"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused-debug-logging",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/CLIENT_SECRET"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"synthetic-oauth",
|
||||
"--provider",
|
||||
"github",
|
||||
"--key",
|
||||
"synthetic-public-id",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/CLIENT_SECRET",
|
||||
"--group-claim-name",
|
||||
"groups",
|
||||
"--admin-group",
|
||||
"unchanged-admin",
|
||||
"--scopes",
|
||||
"read:user,user:email"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--icon-url",
|
||||
"https://synthetic.invalid/icon"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/replacement"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"synthetic-guarded"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/good"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/newline"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/newline"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/short"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/short"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/long"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/long"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/badbyte"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/badbyte"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/world"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/world"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/writable"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/writable"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/executable"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/executable"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/empty"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/empty"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/symlink"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/symlink"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/fifo"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/fifo"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/directory"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/directory"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/missing"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/missing"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/badparent/value"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/badparent/value"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/parentlink/value"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/parentlink/value"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"relative"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"relative"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
""
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
""
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/../forgejo-oidc/good"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/../forgejo-oidc/good"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/writable-input/good"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret-file",
|
||||
"/run/writable-input/good"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused",
|
||||
"--secret",
|
||||
"",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/good"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--secret",
|
||||
"",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/good"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--required-claim-values",
|
||||
""
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/replacement"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"update-oauth",
|
||||
"--id",
|
||||
"1",
|
||||
"--secret",
|
||||
"synthetic-legacy-literal"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 32.57,
|
||||
"argv": [
|
||||
"bwrap",
|
||||
"--unshare-pid",
|
||||
"--die-with-parent",
|
||||
"--new-session",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--symlink",
|
||||
"usr/bin",
|
||||
"/bin",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/cache/download",
|
||||
"/cached",
|
||||
"--ro-bind",
|
||||
"/etc/ssl/certs",
|
||||
"/etc/ssl/certs",
|
||||
"--ro-bind",
|
||||
"/etc/resolv.conf",
|
||||
"/etc/resolv.conf",
|
||||
"--chdir",
|
||||
"/work/source",
|
||||
"--clearenv",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/work/go/bin:/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/work/home",
|
||||
"--setenv",
|
||||
"GOPATH",
|
||||
"/work/gopath",
|
||||
"--setenv",
|
||||
"GOTOOLCHAIN",
|
||||
"local",
|
||||
"--setenv",
|
||||
"GOPROXY",
|
||||
"file:///cached,https://proxy.golang.org",
|
||||
"--setenv",
|
||||
"GOSUMDB",
|
||||
"sum.golang.org",
|
||||
"--setenv",
|
||||
"GOVCS",
|
||||
"*:off",
|
||||
"--setenv",
|
||||
"GOMODCACHE",
|
||||
"/work/modules",
|
||||
"--setenv",
|
||||
"GOCACHE",
|
||||
"/work/gocache",
|
||||
"--setenv",
|
||||
"GOMAXPROCS",
|
||||
"4",
|
||||
"--setenv",
|
||||
"CGO_ENABLED",
|
||||
"1",
|
||||
"--setenv",
|
||||
"GIT_CONFIG_NOSYSTEM",
|
||||
"1",
|
||||
"--setenv",
|
||||
"GIT_CONFIG_GLOBAL",
|
||||
"/dev/null",
|
||||
"--setenv",
|
||||
"GIT_TERMINAL_PROMPT",
|
||||
"0",
|
||||
"--",
|
||||
"go",
|
||||
"mod",
|
||||
"download"
|
||||
],
|
||||
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
|
||||
"module_hashes": {
|
||||
"go.mod": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a",
|
||||
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf"
|
||||
},
|
||||
"network": "public module download only",
|
||||
"pid_namespace": "private",
|
||||
"real_credentials": false
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
{
|
||||
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
|
||||
"inputs_verified": true,
|
||||
"paths": [
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "go.mod",
|
||||
"sha256": "652162a4a95c6d1c349d2a8c0b514def5abd837f001137b77b7b3f4a0f162d00"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "cmd/admin_auth_oauth.go",
|
||||
"sha256": "1dd03f0bd23889a340a10fcc18d2e3a49270b79056aafd1b3baf01b18559b37f"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "services/forms/auth_form.go",
|
||||
"sha256": "026720f23cbdb5e54f8c594109c2dc306b87e323cc649df5fa3194f609cd7737"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "services/auth/source/oauth2/required_claim_values_test.go",
|
||||
"sha256": "54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "services/auth/source/oauth2/required_claim_values.go",
|
||||
"sha256": "29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "services/auth/source/oauth2/providers_openid.go",
|
||||
"sha256": "5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "services/auth/source/oauth2/source.go",
|
||||
"sha256": "44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "routers/web/admin/auths.go",
|
||||
"sha256": "3498229e06fb9ff52396bcb5e68c0cc2d2ad24e6494533d8717d7c833f3e2923"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-required-claim-values-r1",
|
||||
"path": "routers/web/auth/oauth.go",
|
||||
"sha256": "081bcb94023d6e27289196bbe518bebba0a8a6082ea1a32d2794c5562075907b"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-private-input-r1",
|
||||
"path": "cmd/admin_auth_oauth.go",
|
||||
"sha256": "c01084dd631937e3cd1d08593e7090d44661c6cba2838e217bdadbbc5f3c29d9"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-private-input-r1",
|
||||
"path": "cmd/admin_auth_oauth_secret_linux.go",
|
||||
"sha256": "6741961f1df67c1afbf34c796e549425f0e04a0d0678c6eedb05f66d4754c64b"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-private-input-r1",
|
||||
"path": "cmd/admin_auth_oauth_secret_other.go",
|
||||
"sha256": "54056c989a4757e73db0252130e7c16bf16f5ec8eaaca2468f019ba2ef5c532d"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-private-input-r1",
|
||||
"path": "cmd/admin_auth_oauth_secret_linux_test.go",
|
||||
"sha256": "b28c2369dfab9bf2ca48e707e01b4394a36c8be5490cfe5bb658017bb626c4b2"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-private-input-r1",
|
||||
"path": "models/db/engine.go",
|
||||
"sha256": "43683804d0c5bc6ead1dba0344c69dd196825cf3d5eb502af8d932a7b8207b96"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-private-input-r1",
|
||||
"path": "models/db/sensitive_sql.go",
|
||||
"sha256": "8d4e6b3f08e9fdd919e97e47bd857cbd8935fdcbf367d20b5b8c545f279e796e"
|
||||
},
|
||||
{
|
||||
"packet": "forgejo-private-input-r1",
|
||||
"path": "models/db/sensitive_sql_test.go",
|
||||
"sha256": "f0f74c09a9e96a6529db7b9ce459a2ea37e204de38c5ad01855b80c9a992a769"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/db/engine.go",
|
||||
"sha256": "43683804d0c5bc6ead1dba0344c69dd196825cf3d5eb502af8d932a7b8207b96"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/db/context.go",
|
||||
"sha256": "d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/db/sensitive_sql.go",
|
||||
"sha256": "8d4e6b3f08e9fdd919e97e47bd857cbd8935fdcbf367d20b5b8c545f279e796e"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/db/sensitive_sql_test.go",
|
||||
"sha256": "de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/auth/source_operation_internal_test.go",
|
||||
"sha256": "af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/auth/source_operation_test.go",
|
||||
"sha256": "77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/auth/source_operation.go",
|
||||
"sha256": "ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31"
|
||||
},
|
||||
{
|
||||
"packet": "apps-operation-backend-r1",
|
||||
"path": "models/auth/source_operation_process_test.go",
|
||||
"sha256": "40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd"
|
||||
},
|
||||
{
|
||||
"packet": "apps-access-engine-integration-r2",
|
||||
"path": "go.mod",
|
||||
"sha256": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a"
|
||||
},
|
||||
{
|
||||
"packet": "apps-access-engine-integration-r2",
|
||||
"path": "services/auth/source/oauth2/access_engine_test.go",
|
||||
"sha256": "1bbc4a823cdf93005d72a6db2eb77e0096c80903fbdf247a385431d70b9620da"
|
||||
},
|
||||
{
|
||||
"packet": "apps-access-engine-integration-r2",
|
||||
"path": "services/auth/source/oauth2/source_callout.go",
|
||||
"sha256": "6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c"
|
||||
},
|
||||
{
|
||||
"packet": "apps-access-engine-integration-r2",
|
||||
"path": "services/auth/source/oauth2/access_engine_callback_test.go",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"packet": "apps-access-engine-integration-r2",
|
||||
"path": "services/auth/source/oauth2/access_engine.go",
|
||||
"sha256": "c1f2b4c76d09687b9b66dd13b3ba671ba92ae72f26751f8c273607ab9be65b07"
|
||||
},
|
||||
{
|
||||
"packet": "apps-access-engine-integration-r2",
|
||||
"path": "routers/web/auth/oauth.go",
|
||||
"sha256": "58bff778e0dc17a521706289bf45c3e0d76a759a0874f76c379b3d1ddb419b17"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"returncode": 1,
|
||||
"seconds": 500.05,
|
||||
"argv": [
|
||||
"bwrap",
|
||||
"--unshare-pid",
|
||||
"--die-with-parent",
|
||||
"--new-session",
|
||||
"--unshare-net",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--symlink",
|
||||
"usr/bin",
|
||||
"/bin",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/cache/download",
|
||||
"/cached",
|
||||
"--ro-bind",
|
||||
"/etc/ssl/certs",
|
||||
"/etc/ssl/certs",
|
||||
"--ro-bind",
|
||||
"/etc/resolv.conf",
|
||||
"/etc/resolv.conf",
|
||||
"--chdir",
|
||||
"/work/source",
|
||||
"--clearenv",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/work/go/bin:/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/work/home",
|
||||
"--setenv",
|
||||
"GOPATH",
|
||||
"/work/gopath",
|
||||
"--setenv",
|
||||
"GOTOOLCHAIN",
|
||||
"local",
|
||||
"--setenv",
|
||||
"GOPROXY",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOSUMDB",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOVCS",
|
||||
"*:off",
|
||||
"--setenv",
|
||||
"GOMODCACHE",
|
||||
"/work/modules",
|
||||
"--setenv",
|
||||
"GOCACHE",
|
||||
"/work/gocache",
|
||||
"--setenv",
|
||||
"GOMAXPROCS",
|
||||
"4",
|
||||
"--setenv",
|
||||
"CGO_ENABLED",
|
||||
"1",
|
||||
"--setenv",
|
||||
"GIT_CONFIG_NOSYSTEM",
|
||||
"1",
|
||||
"--setenv",
|
||||
"GIT_CONFIG_GLOBAL",
|
||||
"/dev/null",
|
||||
"--setenv",
|
||||
"GIT_TERMINAL_PROMPT",
|
||||
"0",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"4",
|
||||
"-json",
|
||||
"-tags",
|
||||
"sqlite sqlite_unlock_notify",
|
||||
"./services/auth/source/oauth2",
|
||||
"./cmd",
|
||||
"./models/auth",
|
||||
"./models/db",
|
||||
"-run",
|
||||
"TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent|TestPrivateSecret|TestSourceOperation|TestSensitiveSQLHooks",
|
||||
"-count=1"
|
||||
],
|
||||
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
|
||||
"module_hashes": {
|
||||
"go.mod": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a",
|
||||
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf"
|
||||
},
|
||||
"network": "disabled",
|
||||
"pid_namespace": "private",
|
||||
"real_credentials": false
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 21.03,
|
||||
"argv": [
|
||||
"bwrap",
|
||||
"--unshare-pid",
|
||||
"--die-with-parent",
|
||||
"--new-session",
|
||||
"--unshare-net",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--symlink",
|
||||
"usr/bin",
|
||||
"/bin",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/cache/download",
|
||||
"/cached",
|
||||
"--ro-bind",
|
||||
"/etc/ssl/certs",
|
||||
"/etc/ssl/certs",
|
||||
"--ro-bind",
|
||||
"/etc/resolv.conf",
|
||||
"/etc/resolv.conf",
|
||||
"--chdir",
|
||||
"/work/source",
|
||||
"--clearenv",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/descriptor-fixtures",
|
||||
"/run/writable-input",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/work/go/bin:/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/work/home",
|
||||
"--setenv",
|
||||
"GOPATH",
|
||||
"/work/gopath",
|
||||
"--setenv",
|
||||
"GOTOOLCHAIN",
|
||||
"local",
|
||||
"--setenv",
|
||||
"GOPROXY",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOSUMDB",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOVCS",
|
||||
"*:off",
|
||||
"--setenv",
|
||||
"GOMODCACHE",
|
||||
"/work/modules",
|
||||
"--setenv",
|
||||
"GOCACHE",
|
||||
"/work/gocache",
|
||||
"--setenv",
|
||||
"GOMAXPROCS",
|
||||
"4",
|
||||
"--setenv",
|
||||
"CGO_ENABLED",
|
||||
"1",
|
||||
"--setenv",
|
||||
"GIT_CONFIG_NOSYSTEM",
|
||||
"1",
|
||||
"--setenv",
|
||||
"GIT_CONFIG_GLOBAL",
|
||||
"/work/home/test.gitconfig",
|
||||
"--setenv",
|
||||
"GIT_TERMINAL_PROMPT",
|
||||
"0",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"1",
|
||||
"-json",
|
||||
"-tags",
|
||||
"sqlite sqlite_unlock_notify",
|
||||
"./services/auth/source/oauth2",
|
||||
"./cmd",
|
||||
"./models/auth",
|
||||
"./models/db",
|
||||
"-run",
|
||||
"TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent|TestPrivateSecret|TestSourceOperation|TestSensitiveSQLHooks",
|
||||
"-count=1"
|
||||
],
|
||||
"workspace": "/tmp/custodian-forgejo-successor-attempt2",
|
||||
"module_hashes": {
|
||||
"go.mod": "130138c4c3ecc29fa0d7f1e2eef0589ddf3b06071f3cc0448aa7b2326847653a",
|
||||
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf"
|
||||
},
|
||||
"network": "disabled",
|
||||
"pid_namespace": "private",
|
||||
"real_credentials": false
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
---
|
||||
id: forgejo.application-access
|
||||
name: Forgejo authenticated application access
|
||||
namespace: forgejo:application
|
||||
version: v1
|
||||
status: draft
|
||||
package: flexauth.forgejo.application
|
||||
allow_ttl: 10s
|
||||
actions: [application.access]
|
||||
owner: railiance-apps
|
||||
caring:
|
||||
profile: caring-0.4.0-rc2
|
||||
enforce: false
|
||||
---
|
||||
|
||||
This source policy enforces the original three-group any-of in the sole PDP.
|
||||
The registry identifies the application tenant and admitted issuer. Fresh identity
|
||||
and UserInfo facts arrive only in request context from the admitted Forgejo caller,
|
||||
which verifies issuer JOSE and current UserInfo before this request. They are not
|
||||
static subject registry attributes. This policy requires aal2 assurance.
|
||||
The PEP independently verifies the ID token and supplies fresh UserInfo groups;
|
||||
the authenticated workload caller may represent only the Forgejo system. A
|
||||
decision authorizes one login callback, never a later HTTP/Git/SSH/API operation.
|
||||
The accompanying registry is synthetic test data, not an estate admission.
|
||||
|
||||
```rego
|
||||
import rego.v1
|
||||
|
||||
default decision := {"effect": "deny", "reason": "application_access_refused"}
|
||||
|
||||
original_groups := {"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}
|
||||
|
||||
decision := {"effect": "allow", "reason": "current_authenticated_application_access"} if {
|
||||
input.action == "application.access"
|
||||
object.get(input, "purpose", "") == ""
|
||||
input.resource.id == "application:forgejo"
|
||||
input.resource.type == "application"
|
||||
input.resource.system == "forgejo"
|
||||
input.tenant == input.resource.tenant
|
||||
input.tenant == input.resource.attributes.tenant
|
||||
input.subject.tenant == input.tenant
|
||||
input.context.identity_tenant == input.tenant
|
||||
input.subject.id == input.context.identity_subject
|
||||
input.subject.type == input.context.identity_principal_type
|
||||
input.subject.type == "human"
|
||||
input.context.identity_issuer == input.resource.attributes.identity_issuer
|
||||
input.context.identity_assurance == "aal2"
|
||||
count(input.context.required_groups_any_of) == 3
|
||||
{g | g := input.context.required_groups_any_of[_]} == original_groups
|
||||
some group in input.context.current_userinfo_groups
|
||||
group in original_groups
|
||||
is_string(input.context.decisionpep_request_nonce)
|
||||
count(input.context.decisionpep_request_nonce) == 64
|
||||
}
|
||||
```
|
||||
|
||||
```rego test
|
||||
package flexauth.forgejo.application_test
|
||||
import rego.v1
|
||||
import data.flexauth.forgejo.application
|
||||
|
||||
valid_request := {
|
||||
"tenant": "tenant:synthetic",
|
||||
"subject": {
|
||||
"id": "user:synthetic",
|
||||
"type": "human",
|
||||
"tenant": "tenant:synthetic"
|
||||
},
|
||||
"action": "application.access",
|
||||
"resource": {
|
||||
"id": "application:forgejo",
|
||||
"type": "application",
|
||||
"system": "forgejo",
|
||||
"tenant": "tenant:synthetic",
|
||||
"attributes": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"identity_issuer": "https://issuer.invalid"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"identity_tenant": "tenant:synthetic",
|
||||
"identity_subject": "user:synthetic",
|
||||
"identity_principal_type": "human",
|
||||
"identity_issuer": "https://issuer.invalid",
|
||||
"identity_assurance": "aal2",
|
||||
"current_userinfo_groups": [
|
||||
"forge-admins"
|
||||
],
|
||||
"required_groups_any_of": [
|
||||
"forge-admins",
|
||||
"forge-coulomb-owners",
|
||||
"forge-coulomb-readers"
|
||||
],
|
||||
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
|
||||
test_original_groups_allow if {
|
||||
every group in ["forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"] {
|
||||
application.decision.effect == "allow" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": [group]})})
|
||||
}
|
||||
}
|
||||
|
||||
test_outsider_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": ["outsider"]})})
|
||||
}
|
||||
|
||||
test_missing_identity_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(object.remove(valid_request, ["context"]), {"context": object.remove(valid_request.context, ["identity_issuer"])})
|
||||
}
|
||||
|
||||
test_low_assurance_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"identity_assurance": "aal1"})})
|
||||
}
|
||||
|
||||
test_changed_group_contract_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"required_groups_any_of": ["outsider"]})})
|
||||
}
|
||||
```
|
||||
|
||||
```yaml fixture
|
||||
[
|
||||
{
|
||||
"id": "allowed-original-group",
|
||||
"request": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"subject": {
|
||||
"id": "user:synthetic",
|
||||
"type": "human",
|
||||
"tenant": "tenant:synthetic"
|
||||
},
|
||||
"action": "application.access",
|
||||
"resource": {
|
||||
"id": "application:forgejo",
|
||||
"type": "application",
|
||||
"system": "forgejo",
|
||||
"tenant": "tenant:synthetic",
|
||||
"attributes": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"identity_issuer": "https://issuer.invalid"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"identity_tenant": "tenant:synthetic",
|
||||
"identity_subject": "user:synthetic",
|
||||
"identity_principal_type": "human",
|
||||
"identity_issuer": "https://issuer.invalid",
|
||||
"identity_assurance": "aal2",
|
||||
"current_userinfo_groups": [
|
||||
"forge-admins"
|
||||
],
|
||||
"required_groups_any_of": [
|
||||
"forge-admins",
|
||||
"forge-coulomb-owners",
|
||||
"forge-coulomb-readers"
|
||||
],
|
||||
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"effect": "allow",
|
||||
"reason": "current_authenticated_application_access"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "missing-identity-context",
|
||||
"request": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"subject": {
|
||||
"id": "user:synthetic",
|
||||
"type": "human",
|
||||
"tenant": "tenant:synthetic"
|
||||
},
|
||||
"action": "application.access",
|
||||
"resource": {
|
||||
"id": "application:forgejo",
|
||||
"type": "application",
|
||||
"system": "forgejo",
|
||||
"tenant": "tenant:synthetic",
|
||||
"attributes": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"identity_issuer": "https://issuer.invalid"
|
||||
}
|
||||
},
|
||||
"context": {}
|
||||
},
|
||||
"expect": {
|
||||
"effect": "deny",
|
||||
"reason": "application_access_refused"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
[
|
||||
{
|
||||
"operation": [
|
||||
"migrate"
|
||||
],
|
||||
"returncode": 0,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"refused-debug-logging",
|
||||
"--provider",
|
||||
"github",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/CLIENT_SECRET"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
},
|
||||
{
|
||||
"operation": [
|
||||
"admin",
|
||||
"auth",
|
||||
"add-oauth",
|
||||
"--name",
|
||||
"synthetic-oauth",
|
||||
"--provider",
|
||||
"github",
|
||||
"--key",
|
||||
"synthetic-public-id",
|
||||
"--secret-file",
|
||||
"/run/forgejo-oidc/CLIENT_SECRET",
|
||||
"--group-claim-name",
|
||||
"groups",
|
||||
"--admin-group",
|
||||
"unchanged-admin",
|
||||
"--scopes",
|
||||
"read:user,user:email"
|
||||
],
|
||||
"returncode": 1,
|
||||
"stdout_stderr_secret_absent": true
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import pathlib,subprocess,json,sqlite3,os,shlex,time
|
||||
w=pathlib.Path('/tmp/custodian-forgejo-successor-attempt2/cli-work');s='/tmp/forgejo-private-input-r1';r='/tmp/forgejo-independent-r1'
|
||||
fixtures=w/'fixtures'; fixtures.mkdir(exist_ok=True)
|
||||
A='AbCdEf012345678-'*4;B='ZyXwVu_987654321'*4 # synthetic exact64
|
||||
assert len(A)==64 and len(B)==64
|
||||
for name,data,mode in [('good',A,0o400),('replacement',B,0o440),('newline',A+'\n',0o400),('short',A[:-1],0o400),('long',A+'A',0o400),('badbyte',A[:-1]+'=',0o400),('world',A,0o444),('writable',A,0o600),('executable',A,0o500),('empty','',0o400),('CLIENT_SECRET','',0o400)]:
|
||||
p=fixtures/name
|
||||
if p.exists():p.chmod(0o600)
|
||||
p.write_text(data);p.chmod(mode)
|
||||
for name in ['symlink']:
|
||||
p=fixtures/name
|
||||
if not p.exists():p.symlink_to('good')
|
||||
if not (fixtures/'fifo').exists():os.mkfifo(fixtures/'fifo',0o400)
|
||||
(fixtures/'directory').mkdir(exist_ok=True)
|
||||
(fixtures/'badparent').mkdir(exist_ok=True);(fixtures/'badparent').chmod(0o777)
|
||||
if (fixtures/'badparent/value').exists():(fixtures/'badparent/value').chmod(0o600)
|
||||
(fixtures/'badparent/value').write_text(A);(fixtures/'badparent/value').chmod(0o400)
|
||||
if not (fixtures/'parentlink').exists():(fixtures/'parentlink').symlink_to('directory',target_is_directory=True)
|
||||
(w/'runtime').mkdir(exist_ok=True)
|
||||
dbfile=w/'runtime/test.db'
|
||||
if dbfile.exists():dbfile.unlink() # isolated synthetic test database only
|
||||
ini=w/'runtime/app.ini';ini.write_text('''APP_NAME = synthetic-private-input-test
|
||||
RUN_USER = worsch
|
||||
WORK_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = SYNTHETIC-TEST-KEY
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
SLOW_QUERY_THRESHOLD = 1ns
|
||||
PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/test.db
|
||||
[server]
|
||||
APP_DATA_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/data
|
||||
OFFLINE_MODE = true
|
||||
[repository]
|
||||
ROOT = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/repos
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = Error
|
||||
''')
|
||||
env={'PATH':'/usr/bin:/bin','HOME':str(w/'home'),'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':str(w/'home/test.gitconfig'),'GIT_TERMINAL_PROMPT':'0','USER':'worsch','GITEA_WORK_DIR':str(w/'runtime')}
|
||||
base=['bwrap','--unshare-pid','--unshare-net','--ro-bind','/usr','/usr','--ro-bind','/lib','/lib','--ro-bind','/lib64','/lib64','--symlink','usr/bin','/bin','--proc','/proc','--dev','/dev','--tmpfs','/tmp','--bind',str(w),str(w),'--dir','/run','--ro-bind',str(fixtures),'/run/forgejo-oidc','--ro-bind',str(fixtures/'good'),'/run/forgejo-oidc/CLIENT_SECRET','--bind',str(fixtures),'/run/writable-input','--chdir',str(w/'runtime'),'--','env','-i']+[k+'='+v for k,v in env.items()]+[str(w/'forgejo'),'--config',str(ini)]
|
||||
results=[]
|
||||
def run(args,ok=True,reason=None):
|
||||
p=subprocess.run(base+args,capture_output=True,text=True,timeout=60); output=p.stdout+p.stderr
|
||||
assert A not in output and B not in output, 'synthetic input leaked in output'
|
||||
assert all(A not in arg and B not in arg for arg in base+args), 'synthetic input in argv'
|
||||
assert all(A not in v and B not in v for v in env.values()), 'synthetic input in env'
|
||||
results.append({'operation':args,'returncode':p.returncode,'stdout_stderr_secret_absent':True})
|
||||
(w/'cli-attempts.json').write_text(json.dumps(results,indent=2))
|
||||
if (p.returncode==0)!=ok:raise AssertionError('CLI unexpected status: '+repr(args)+'\n'+output[-5000:])
|
||||
if reason:assert reason in output,output[-2000:]
|
||||
return p
|
||||
run(['migrate'])
|
||||
clean_ini=ini.read_text()
|
||||
ini.write_text(clean_ini.replace('DB_TYPE = sqlite3', 'DB_TYPE = sqlite3\nLOG_SQL = true'))
|
||||
run(['admin','auth','add-oauth','--name','refused-debug-logging','--provider','github','--secret-file','/run/forgejo-oidc/CLIENT_SECRET'],False,'OAuth configuration requires SQL logging disabled')
|
||||
ini.write_text(clean_ini)
|
||||
def rows():
|
||||
with sqlite3.connect(w/'runtime/test.db') as db:return db.execute('select id,name,cfg from login_source order by id').fetchall()
|
||||
def cfg():return json.loads(rows()[0][2])
|
||||
run(['admin','auth','add-oauth','--name','synthetic-oauth','--provider','github','--key','synthetic-public-id','--secret-file','/run/forgejo-oidc/CLIENT_SECRET','--group-claim-name','groups','--admin-group','unchanged-admin','--scopes','read:user,user:email'])
|
||||
assert len(rows())==1 and cfg()['ClientSecret']==A
|
||||
id=str(rows()[0][0]); before=cfg()
|
||||
run(['admin','auth','update-oauth','--id',id,'--icon-url','https://synthetic.invalid/icon'])
|
||||
assert cfg()['ClientSecret']==A
|
||||
preserved=cfg()
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'])
|
||||
after=cfg();assert after['ClientSecret']==B
|
||||
assert {k:v for k,v in after.items() if k!='ClientSecret'}=={k:v for k,v in preserved.items() if k!='ClientSecret'},'secret replacement changed unrelated config'
|
||||
# Guarded inactive source exercises actual update omission and strict list semantics
|
||||
# without discovery/network or claiming a browser login.
|
||||
with sqlite3.connect(w/'runtime/test.db') as db:
|
||||
guarded=after|{'Provider':'openidConnect','OpenIDConnectAutoDiscoveryURL':'https://synthetic.invalid/.well-known/openid-configuration','RequiredClaimName':'groups','RequiredClaimValues':['forge-admins','forge-coulomb-owners','forge-coulomb-readers']}
|
||||
db.execute('update login_source set is_active=0,cfg=? where id=?',(json.dumps(guarded),id))
|
||||
run(['admin','auth','update-oauth','--id',id,'--name','synthetic-guarded'])
|
||||
assert cfg()['RequiredClaimValues']==guarded['RequiredClaimValues'] and cfg()['ClientSecret']==B
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/good'])
|
||||
assert cfg()['ClientSecret']==A and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
|
||||
for path in ['/run/forgejo-oidc/'+n for n in ['newline','short','long','badbyte','world','writable','executable','empty','symlink','fifo','directory','missing','badparent/value','parentlink/value']]+['relative','','/run/forgejo-oidc/../forgejo-oidc/good','/run/writable-input/good']:
|
||||
before=rows()
|
||||
for cmd in [['admin','auth','add-oauth','--name','refused','--provider','github'],['admin','auth','update-oauth','--id',id,'--name','refused']]:
|
||||
run(cmd+['--secret-file',path],False,'secret-file input refused')
|
||||
assert rows()==before,'failure changed DB'
|
||||
for action in [['add-oauth','--name','refused'],['update-oauth','--id',id]]:
|
||||
before=rows();run(['admin','auth']+action+['--secret','','--secret-file','/run/forgejo-oidc/good'],False,'mutually exclusive');assert rows()==before
|
||||
# Failure to clear/ambiguously replace gate keeps stored application bytes unchanged.
|
||||
before=rows();run(['admin','auth','update-oauth','--id',id,'--required-claim-values',''],False);assert rows()==before
|
||||
# Actual XORM error hook under a failing SQLite trigger must not disclose cfg.
|
||||
before=rows()
|
||||
with sqlite3.connect(w/'runtime/test.db') as database:
|
||||
database.execute("create trigger synthetic_private_error before update on login_source begin select raise(abort, 'synthetic-db-failure'); end")
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'],False,'OAuth source update failed')
|
||||
assert rows()==before
|
||||
with sqlite3.connect(w/'runtime/test.db') as database:database.execute('drop trigger synthetic_private_error')
|
||||
# Legacy explicit secret remains supported; synthetic legacy string is not private input.
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret','synthetic-legacy-literal'])
|
||||
assert cfg()['ClientSecret']=='synthetic-legacy-literal' and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
|
||||
(w/'cli-results.json').write_text(json.dumps({'passed':True,'subprocesses':len(results),'real_backend_cli_and_sqlite':True,'network':'disabled by bwrap','secret_inputs':'synthetic only','values_never_in_argv_env_output':True,'actual_browser_login':False,'actual_readonly_file_bind':True,'replacement_preserves_other_config':True,'unsafe_SQL_logging_refused':True,'actual_error_and_slow_hooks_no_leak':True},indent=2));print('CLI integrations passed:',len(results))
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
---
|
||||
id: forgejo.application-access
|
||||
name: Forgejo authenticated application access
|
||||
namespace: forgejo:application
|
||||
version: v1
|
||||
status: draft
|
||||
package: flexauth.forgejo.application
|
||||
allow_ttl: 10s
|
||||
actions: [application.access]
|
||||
owner: railiance-apps
|
||||
caring:
|
||||
profile: caring-0.4.0-rc2
|
||||
enforce: false
|
||||
---
|
||||
|
||||
This source policy enforces the original three-group any-of in the sole PDP.
|
||||
The registry identifies the application tenant and admitted issuer. Fresh identity
|
||||
and UserInfo facts arrive only in request context from the admitted Forgejo caller,
|
||||
which verifies issuer JOSE and current UserInfo before this request. They are not
|
||||
static subject registry attributes. This policy requires aal2 assurance.
|
||||
The PEP independently verifies the ID token and supplies fresh UserInfo groups;
|
||||
the authenticated workload caller may represent only the Forgejo system. A
|
||||
decision authorizes one login callback, never a later HTTP/Git/SSH/API operation.
|
||||
The accompanying registry is synthetic test data, not an estate admission.
|
||||
|
||||
```rego
|
||||
import rego.v1
|
||||
|
||||
default decision := {"effect": "deny", "reason": "application_access_refused"}
|
||||
|
||||
original_groups := {"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}
|
||||
|
||||
decision := {"effect": "allow", "reason": "current_authenticated_application_access"} if {
|
||||
input.action == "application.access"
|
||||
object.get(input, "purpose", "") == ""
|
||||
input.resource.id == "application:forgejo"
|
||||
input.resource.type == "application"
|
||||
input.resource.system == "forgejo"
|
||||
input.tenant == input.resource.tenant
|
||||
input.tenant == input.resource.attributes.tenant
|
||||
input.subject.tenant == input.tenant
|
||||
input.context.identity_tenant == input.tenant
|
||||
input.subject.id == input.context.identity_subject
|
||||
input.subject.type == input.context.identity_principal_type
|
||||
input.subject.type == "human"
|
||||
input.context.identity_issuer == input.resource.attributes.identity_issuer
|
||||
input.context.identity_assurance == "aal2"
|
||||
count(input.context.required_groups_any_of) == 3
|
||||
{g | g := input.context.required_groups_any_of[_]} == original_groups
|
||||
some group in input.context.current_userinfo_groups
|
||||
group in original_groups
|
||||
is_string(input.context.decisionpep_request_nonce)
|
||||
count(input.context.decisionpep_request_nonce) == 64
|
||||
}
|
||||
```
|
||||
|
||||
```rego test
|
||||
package flexauth.forgejo.application_test
|
||||
import rego.v1
|
||||
import data.flexauth.forgejo.application
|
||||
|
||||
valid_request := {
|
||||
"tenant": "tenant:synthetic",
|
||||
"subject": {
|
||||
"id": "user:synthetic",
|
||||
"type": "human",
|
||||
"tenant": "tenant:synthetic"
|
||||
},
|
||||
"action": "application.access",
|
||||
"resource": {
|
||||
"id": "application:forgejo",
|
||||
"type": "application",
|
||||
"system": "forgejo",
|
||||
"tenant": "tenant:synthetic",
|
||||
"attributes": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"identity_issuer": "https://issuer.invalid"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"identity_tenant": "tenant:synthetic",
|
||||
"identity_subject": "user:synthetic",
|
||||
"identity_principal_type": "human",
|
||||
"identity_issuer": "https://issuer.invalid",
|
||||
"identity_assurance": "aal2",
|
||||
"current_userinfo_groups": [
|
||||
"forge-admins"
|
||||
],
|
||||
"required_groups_any_of": [
|
||||
"forge-admins",
|
||||
"forge-coulomb-owners",
|
||||
"forge-coulomb-readers"
|
||||
],
|
||||
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
|
||||
test_original_groups_allow if {
|
||||
every group in ["forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"] {
|
||||
application.decision.effect == "allow" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": [group]})})
|
||||
}
|
||||
}
|
||||
|
||||
test_outsider_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"current_userinfo_groups": ["outsider"]})})
|
||||
}
|
||||
|
||||
test_missing_identity_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.remove(valid_request.context, ["identity_issuer"])})
|
||||
}
|
||||
|
||||
test_low_assurance_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"identity_assurance": "aal1"})})
|
||||
}
|
||||
|
||||
test_changed_group_contract_denied if {
|
||||
application.decision.effect == "deny" with input as object.union(valid_request, {"context": object.union(valid_request.context, {"required_groups_any_of": ["outsider"]})})
|
||||
}
|
||||
```
|
||||
|
||||
```yaml fixture
|
||||
[
|
||||
{
|
||||
"id": "allowed-original-group",
|
||||
"request": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"subject": {
|
||||
"id": "user:synthetic",
|
||||
"type": "human",
|
||||
"tenant": "tenant:synthetic"
|
||||
},
|
||||
"action": "application.access",
|
||||
"resource": {
|
||||
"id": "application:forgejo",
|
||||
"type": "application",
|
||||
"system": "forgejo",
|
||||
"tenant": "tenant:synthetic",
|
||||
"attributes": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"identity_issuer": "https://issuer.invalid"
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"identity_tenant": "tenant:synthetic",
|
||||
"identity_subject": "user:synthetic",
|
||||
"identity_principal_type": "human",
|
||||
"identity_issuer": "https://issuer.invalid",
|
||||
"identity_assurance": "aal2",
|
||||
"current_userinfo_groups": [
|
||||
"forge-admins"
|
||||
],
|
||||
"required_groups_any_of": [
|
||||
"forge-admins",
|
||||
"forge-coulomb-owners",
|
||||
"forge-coulomb-readers"
|
||||
],
|
||||
"decisionpep_request_nonce": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"effect": "allow",
|
||||
"reason": "current_authenticated_application_access"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "missing-identity-context",
|
||||
"request": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"subject": {
|
||||
"id": "user:synthetic",
|
||||
"type": "human",
|
||||
"tenant": "tenant:synthetic"
|
||||
},
|
||||
"action": "application.access",
|
||||
"resource": {
|
||||
"id": "application:forgejo",
|
||||
"type": "application",
|
||||
"system": "forgejo",
|
||||
"tenant": "tenant:synthetic",
|
||||
"attributes": {
|
||||
"tenant": "tenant:synthetic",
|
||||
"identity_issuer": "https://issuer.invalid"
|
||||
}
|
||||
},
|
||||
"context": {}
|
||||
},
|
||||
"expect": {
|
||||
"effect": "deny",
|
||||
"reason": "application_access_refused"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -0,0 +1,368 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/services/auth/source/oauth2"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
oauthCLIFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
Usage: "Application Name",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "provider",
|
||||
Value: "",
|
||||
Usage: "OAuth2 Provider",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "key",
|
||||
Value: "",
|
||||
Usage: "Client ID (Key)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "secret",
|
||||
Value: "",
|
||||
Usage: "Client Secret",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "secret-file",
|
||||
Usage: "Read a 64-byte base64url client secret from an admitted read-only regular file",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "auto-discover-url",
|
||||
Value: "",
|
||||
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "use-custom-urls",
|
||||
Value: "false",
|
||||
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-tenant-id",
|
||||
Value: "",
|
||||
Usage: "Use custom Tenant ID for OAuth endpoints",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-auth-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-token-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-profile-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-email-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Email URL (option for GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "icon-url",
|
||||
Value: "",
|
||||
Usage: "Custom icon URL for OAuth2 login source",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "scopes",
|
||||
Value: nil,
|
||||
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "required-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name that has to be set to allow users to login with this source",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "required-claim-value",
|
||||
Value: "",
|
||||
Usage: "Claim value that has to be set to allow users to login with this source",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "required-claim-values",
|
||||
Usage: "Require any one exact array value from current OpenID Connect UserInfo (cannot combine with required-claim-value)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "group-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name providing group names for this source",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "admin-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for administrator users",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "restricted-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for restricted users",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "group-team-map",
|
||||
Value: "",
|
||||
Usage: "JSON mapping between groups and org teams",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "group-team-map-removal",
|
||||
Usage: "Activate automatic team membership removal depending on groups",
|
||||
},
|
||||
}
|
||||
|
||||
microcmdAuthAddOauth = &cli.Command{
|
||||
Name: "add-oauth",
|
||||
Usage: "Add new Oauth authentication source",
|
||||
Action: runAddOauth,
|
||||
Flags: oauthCLIFlags,
|
||||
}
|
||||
|
||||
microcmdAuthUpdateOauth = &cli.Command{
|
||||
Name: "update-oauth",
|
||||
Usage: "Update existing Oauth authentication source",
|
||||
Action: runUpdateOauth,
|
||||
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
|
||||
}
|
||||
)
|
||||
|
||||
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||
var customURLMapping *oauth2.CustomURLMapping
|
||||
if c.IsSet("use-custom-urls") {
|
||||
customURLMapping = &oauth2.CustomURLMapping{
|
||||
TokenURL: c.String("custom-token-url"),
|
||||
AuthURL: c.String("custom-auth-url"),
|
||||
ProfileURL: c.String("custom-profile-url"),
|
||||
EmailURL: c.String("custom-email-url"),
|
||||
Tenant: c.String("custom-tenant-id"),
|
||||
}
|
||||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
return &oauth2.Source{
|
||||
Provider: c.String("provider"),
|
||||
ClientID: c.String("key"),
|
||||
ClientSecret: c.String("secret"),
|
||||
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
||||
CustomURLMapping: customURLMapping,
|
||||
IconURL: c.String("icon-url"),
|
||||
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
|
||||
Scopes: c.StringSlice("scopes"),
|
||||
RequiredClaimName: c.String("required-claim-name"),
|
||||
RequiredClaimValue: c.String("required-claim-value"),
|
||||
RequiredClaimValues: c.StringSlice("required-claim-values"),
|
||||
GroupClaimName: c.String("group-claim-name"),
|
||||
AdminGroup: c.String("admin-group"),
|
||||
RestrictedGroup: c.String("restricted-group"),
|
||||
GroupTeamMap: c.String("group-team-map"),
|
||||
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
|
||||
}
|
||||
}
|
||||
|
||||
func runAddOauth(c *cli.Context) error {
|
||||
secret, selected, err := oauthSecretInput(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
ctx = db.WithSensitiveSQL(ctx)
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if db.SQLLoggingEnabled(ctx) {
|
||||
return errors.New("OAuth configuration requires SQL logging disabled")
|
||||
}
|
||||
|
||||
config := parseOAuth2Config(c)
|
||||
if selected {
|
||||
config.ClientSecret = secret
|
||||
}
|
||||
if err := oauth2.ValidateRequiredClaimValues(config.RequiredClaimName, config.RequiredClaimValue, config.RequiredClaimValues); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.RequiredClaimValues != nil && config.Provider != "openidConnect" {
|
||||
return errors.New("required-claim-values requires openidConnect")
|
||||
}
|
||||
if config.Provider == "openidConnect" {
|
||||
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
|
||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
|
||||
}
|
||||
}
|
||||
|
||||
err = auth_model.CreateSource(ctx, &auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActive: true,
|
||||
Cfg: config,
|
||||
})
|
||||
if err != nil && c.IsSet("secret-file") {
|
||||
return errors.New("OAuth source creation failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func runUpdateOauth(c *cli.Context) error {
|
||||
secret, selected, err := oauthSecretInput(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !c.IsSet("id") {
|
||||
return errors.New("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
ctx = db.WithSensitiveSQL(ctx)
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if db.SQLLoggingEnabled(ctx) {
|
||||
return errors.New("OAuth configuration requires SQL logging disabled")
|
||||
}
|
||||
|
||||
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oAuth2Config, ok := source.Cfg.(*oauth2.Source)
|
||||
if !ok {
|
||||
return errors.New("authentication source is not OAuth2")
|
||||
}
|
||||
|
||||
if c.IsSet("name") {
|
||||
source.Name = c.String("name")
|
||||
}
|
||||
|
||||
if c.IsSet("provider") {
|
||||
oAuth2Config.Provider = c.String("provider")
|
||||
}
|
||||
|
||||
if c.IsSet("key") {
|
||||
oAuth2Config.ClientID = c.String("key")
|
||||
}
|
||||
|
||||
if selected {
|
||||
oAuth2Config.ClientSecret = secret
|
||||
}
|
||||
|
||||
if c.IsSet("auto-discover-url") {
|
||||
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
|
||||
}
|
||||
|
||||
if c.IsSet("icon-url") {
|
||||
oAuth2Config.IconURL = c.String("icon-url")
|
||||
}
|
||||
|
||||
if c.IsSet("scopes") {
|
||||
oAuth2Config.Scopes = c.StringSlice("scopes")
|
||||
}
|
||||
|
||||
if c.IsSet("required-claim-name") {
|
||||
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
|
||||
}
|
||||
if c.IsSet("required-claim-value") {
|
||||
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
|
||||
}
|
||||
|
||||
if c.IsSet("required-claim-values") {
|
||||
oAuth2Config.RequiredClaimValues = c.StringSlice("required-claim-values")
|
||||
}
|
||||
|
||||
if c.IsSet("group-claim-name") {
|
||||
oAuth2Config.GroupClaimName = c.String("group-claim-name")
|
||||
}
|
||||
if c.IsSet("admin-group") {
|
||||
oAuth2Config.AdminGroup = c.String("admin-group")
|
||||
}
|
||||
if c.IsSet("restricted-group") {
|
||||
oAuth2Config.RestrictedGroup = c.String("restricted-group")
|
||||
}
|
||||
if c.IsSet("group-team-map") {
|
||||
oAuth2Config.GroupTeamMap = c.String("group-team-map")
|
||||
}
|
||||
if c.IsSet("group-team-map-removal") {
|
||||
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
|
||||
}
|
||||
|
||||
// update custom URL mapping
|
||||
customURLMapping := &oauth2.CustomURLMapping{}
|
||||
|
||||
if oAuth2Config.CustomURLMapping != nil {
|
||||
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
|
||||
customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
|
||||
customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
|
||||
customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
|
||||
customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
|
||||
}
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
|
||||
customURLMapping.TokenURL = c.String("custom-token-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
|
||||
customURLMapping.AuthURL = c.String("custom-auth-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
|
||||
customURLMapping.ProfileURL = c.String("custom-profile-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
|
||||
customURLMapping.EmailURL = c.String("custom-email-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
|
||||
customURLMapping.Tenant = c.String("custom-tenant-id")
|
||||
}
|
||||
|
||||
oAuth2Config.CustomURLMapping = customURLMapping
|
||||
if err := oauth2.ValidateRequiredClaimValues(oAuth2Config.RequiredClaimName, oAuth2Config.RequiredClaimValue, oAuth2Config.RequiredClaimValues); err != nil {
|
||||
return err
|
||||
}
|
||||
if oAuth2Config.RequiredClaimValues != nil && oAuth2Config.Provider != "openidConnect" {
|
||||
return errors.New("required-claim-values requires openidConnect")
|
||||
}
|
||||
source.Cfg = oAuth2Config
|
||||
|
||||
err = auth_model.UpdateSource(ctx, source)
|
||||
if err != nil && c.IsSet("secret-file") {
|
||||
return errors.New("OAuth source update failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve before opening the DB or mutating a loaded source. Never store the
|
||||
// resolved bytes in the CLI context: they must not become flag/argv diagnostics.
|
||||
func oauthSecretInput(c *cli.Context) (string, bool, error) {
|
||||
if c.IsSet("secret-file") {
|
||||
if c.IsSet("secret") {
|
||||
return "", false, errors.New("secret and secret-file are mutually exclusive")
|
||||
}
|
||||
value, err := readOAuthSecretFile(c.String("secret-file"))
|
||||
return value, true, err
|
||||
}
|
||||
return c.String("secret"), c.IsSet("secret"), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
module forgejo.org
|
||||
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
code.forgejo.org/f3/gof3/v3 v3.10.6
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
code.forgejo.org/forgejo/levelqueue v1.0.0
|
||||
code.forgejo.org/forgejo/reply v1.0.2
|
||||
code.forgejo.org/go-chi/binding v1.0.0
|
||||
code.forgejo.org/go-chi/cache v1.0.0
|
||||
code.forgejo.org/go-chi/captcha v1.0.1
|
||||
code.forgejo.org/go-chi/session v1.0.1
|
||||
code.gitea.io/actions-proto-go v0.4.0
|
||||
code.gitea.io/sdk/gitea v0.20.0
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
connectrpc.com/connect v1.17.0
|
||||
github.com/42wim/httpsig v1.2.2
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/ProtonMail/go-crypto v1.1.6
|
||||
github.com/PuerkitoBio/goquery v1.10.2
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
||||
github.com/alecthomas/chroma/v2 v2.15.0
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.5.2
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||
github.com/caddyserver/certmagic v0.22.2
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
github.com/djherbis/nio/v3 v3.0.1
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
|
||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
||||
github.com/go-chi/chi/v5 v5.2.0
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.2
|
||||
github.com/go-git/go-git/v5 v5.13.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-openapi/spec v0.20.14
|
||||
github.com/go-sql-driver/mysql v1.9.1
|
||||
github.com/go-webauthn/webauthn v0.12.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/go-github/v64 v64.0.0
|
||||
github.com/google/pprof v0.0.0-20241017200806-017d972448fc
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||
github.com/jhillyerd/enmime/v2 v2.1.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.17.11
|
||||
github.com/klauspost/cpuid/v2 v2.2.10
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.80.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.28
|
||||
github.com/meilisearch/meilisearch-go v0.31.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/minio/minio-go/v7 v7.0.88
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/nektos/act v0.2.52
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/redis/go-redis/v9 v9.7.3
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.12
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
github.com/valyala/fastjson v1.6.4
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go v0.126.0
|
||||
go.uber.org/mock v0.5.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/image v0.25.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.12.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/text v0.23.0
|
||||
google.golang.org/protobuf v1.36.4
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/xurls/v2 v2.5.0
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.22.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
|
||||
github.com/blevesearch/geo v0.2.3 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.25 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
github.com/blevesearch/vellum v1.1.0 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.22.7 // indirect
|
||||
github.com/go-webauthn/x v0.1.20 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/go-tpm v0.9.3 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/libdns/libdns v0.2.3 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/markbates/going v1.0.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.1 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rhysd/actionlint v1.6.27 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
github.com/zeebo/assert v1.3.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
||||
|
||||
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.25.1
|
||||
|
||||
replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1
|
||||
|
||||
replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616
|
||||
|
||||
replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078
|
||||
|
||||
require github.com/netkingdom/access-engine v0.0.0
|
||||
|
||||
replace github.com/netkingdom/access-engine => ./third_party/access-engine-pep
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"forgejo.org/modules/setting"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type sensitiveSQLKey struct{}
|
||||
|
||||
// WithSensitiveSQL suppresses argument, statement and driver-error disclosure
|
||||
// in the application's query hooks. The caller must separately refuse raw XORM
|
||||
// SQL logging; custom driver/server auditing remains an owner custody concern.
|
||||
func WithSensitiveSQL(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, sensitiveSQLKey{}, true)
|
||||
}
|
||||
func isSensitiveSQL(ctx context.Context) bool {
|
||||
sensitive, _ := ctx.Value(sensitiveSQLKey{}).(bool)
|
||||
return sensitive
|
||||
}
|
||||
|
||||
// SQLLoggingEnabled checks the underlying engine without changing the context or
|
||||
// assuming that a sensitive context still exposes an Engine rather than Session.
|
||||
// Unknown/uninitialized backends refuse, and query redaction never permits raw
|
||||
// XORM logging. It neither creates a transaction nor closes an existing one.
|
||||
func SQLLoggingEnabled(ctx context.Context) bool {
|
||||
if setting.Database.LogSQL {
|
||||
return true
|
||||
}
|
||||
engine := getEngine(ctx)
|
||||
if engine == nil {
|
||||
if x == nil {
|
||||
return true
|
||||
}
|
||||
engine = x
|
||||
}
|
||||
switch current := engine.(type) {
|
||||
case *xorm.Engine:
|
||||
return current == nil || current.Logger() == nil || current.Logger().IsShowSQL()
|
||||
case *xorm.Session:
|
||||
return current == nil || current.Engine() == nil || current.Engine().Logger() == nil || current.Engine().Logger().IsShowSQL()
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/modules/setting"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func TestSensitiveSQLLoggingChecksUnderlyingEngine(t *testing.T) {
|
||||
engine, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer engine.Close()
|
||||
engine.SetLogger(NewXORMLogger(false))
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
previous := setting.Database.LogSQL
|
||||
defer func() { setting.Database.LogSQL = previous }()
|
||||
for name, backend := range map[string]Engine{"engine": engine, "session": session} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx := WithSensitiveSQL(newContext(context.Background(), backend, false))
|
||||
setting.Database.LogSQL = false
|
||||
engine.ShowSQL(false)
|
||||
if SQLLoggingEnabled(ctx) {
|
||||
t.Fatal("private context with disabled actual SQL logger refused")
|
||||
}
|
||||
engine.ShowSQL(true)
|
||||
if !SQLLoggingEnabled(ctx) {
|
||||
t.Fatal("actual raw SQL logger escaped refusal")
|
||||
}
|
||||
engine.ShowSQL(false)
|
||||
setting.Database.LogSQL = true
|
||||
if !SQLLoggingEnabled(ctx) {
|
||||
t.Fatal("configured raw SQL logger escaped refusal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,205 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/markbates/goth"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
"github.com/netkingdom/access-engine/pkg/decisionpep"
|
||||
)
|
||||
|
||||
var errApplicationDecision = errors.New("current authenticated application access refused")
|
||||
|
||||
// AccessEngineAdmission contains owner-admitted public trust, with only a file
|
||||
// reference to the private PDP caller token. It is not persisted OAuth source
|
||||
// configuration and cannot be replaced by token claims or provider discovery.
|
||||
type AccessEngineAdmission struct {
|
||||
SourceName string `json:"source_name"`
|
||||
Issuer string `json:"issuer"`
|
||||
Audience string `json:"audience"`
|
||||
TokenType string `json:"token_type"`
|
||||
Algorithms []string `json:"algorithms"`
|
||||
IdentityKeys map[string]string `json:"identity_keys"`
|
||||
TenantClaim string `json:"tenant_claim"`
|
||||
PrincipalTypeClaim string `json:"principal_type_claim"`
|
||||
AssuranceClaim string `json:"assurance_claim"`
|
||||
MaxIdentityAge string `json:"max_identity_age"`
|
||||
Decision decisionpep.Admission `json:"decision"`
|
||||
}
|
||||
|
||||
// VerifiedApplicationIdentity is derived only by VerifyApplicationIdentity from
|
||||
// independently admitted JOSE keys. RawData and UserInfo never supply these
|
||||
// fields. This snapshot is an identity fact, never an authorization verdict.
|
||||
type VerifiedApplicationIdentity struct {
|
||||
Issuer string
|
||||
Subject string
|
||||
Tenant string
|
||||
PrincipalType api.SubjectType
|
||||
Assurance string
|
||||
NotAfter time.Time
|
||||
}
|
||||
|
||||
func loadAccessEngineAdmission(path string) (AccessEngineAdmission, error) {
|
||||
var a AccessEngineAdmission
|
||||
if !filepath.IsAbs(path) {
|
||||
return a, errApplicationDecision
|
||||
}
|
||||
f, e := os.Open(path)
|
||||
if e != nil {
|
||||
return a, errApplicationDecision
|
||||
}
|
||||
defer f.Close()
|
||||
stat, e := f.Stat()
|
||||
if e != nil || !stat.Mode().IsRegular() || stat.Mode().Perm()&0022 != 0 {
|
||||
return a, errApplicationDecision
|
||||
}
|
||||
raw, e := io.ReadAll(io.LimitReader(f, 1<<20+1))
|
||||
if e != nil || len(raw) > 1<<20 {
|
||||
return a, errApplicationDecision
|
||||
}
|
||||
d := json.NewDecoder(strings.NewReader(string(raw)))
|
||||
d.DisallowUnknownFields()
|
||||
if d.Decode(&a) != nil {
|
||||
return a, errApplicationDecision
|
||||
}
|
||||
if _, e = d.Token(); e != io.EOF {
|
||||
return a, errApplicationDecision
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// VerifyApplicationIdentity validates the ID token independently of Goth's
|
||||
// decode/claim checks and of the UserInfo merge. Keys and accepted algorithm,
|
||||
// issuer, audience and type are admitted separately; none come from the JWT URL.
|
||||
func VerifyApplicationIdentity(raw string, a AccessEngineAdmission, now time.Time) (VerifiedApplicationIdentity, error) {
|
||||
var out VerifiedApplicationIdentity
|
||||
age, e := time.ParseDuration(a.MaxIdentityAge)
|
||||
if e != nil || age <= 0 || a.Issuer == "" || a.Audience == "" || a.TokenType == "" || len(a.Algorithms) == 0 || len(a.IdentityKeys) == 0 || a.TenantClaim == "" || a.PrincipalTypeClaim == "" || a.AssuranceClaim == "" {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
for _, alg := range a.Algorithms {
|
||||
if alg != "RS256" && alg != "RS384" && alg != "RS512" && alg != "ES256" && alg != "ES384" && alg != "ES512" && alg != "EdDSA" {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
}
|
||||
claims := jwt.MapClaims{}
|
||||
token, e := jwt.ParseWithClaims(raw, claims, func(t *jwt.Token) (any, error) {
|
||||
if typ, ok := t.Header["typ"].(string); !ok || typ != a.TokenType {
|
||||
return nil, errApplicationDecision
|
||||
}
|
||||
kid, ok := t.Header["kid"].(string)
|
||||
if !ok || kid == "" {
|
||||
return nil, errApplicationDecision
|
||||
}
|
||||
text, ok := a.IdentityKeys[kid]
|
||||
if !ok {
|
||||
return nil, errApplicationDecision
|
||||
}
|
||||
block, rest := pem.Decode([]byte(text))
|
||||
if block == nil || block.Type != "PUBLIC KEY" || len(strings.TrimSpace(string(rest))) != 0 {
|
||||
return nil, errApplicationDecision
|
||||
}
|
||||
key, e := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if e != nil {
|
||||
return nil, errApplicationDecision
|
||||
}
|
||||
var pub crypto.PublicKey = key
|
||||
return pub, nil
|
||||
}, jwt.WithValidMethods(a.Algorithms), jwt.WithIssuer(a.Issuer), jwt.WithAudience(a.Audience), jwt.WithExpirationRequired(), jwt.WithIssuedAt(), jwt.WithJSONNumber(), jwt.WithTimeFunc(func() time.Time { return now }))
|
||||
if e != nil || token == nil || !token.Valid {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
subject, e := claims.GetSubject()
|
||||
if e != nil || subject == "" {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
issued, e := claims.GetIssuedAt()
|
||||
if e != nil || issued == nil || issued.Time.After(now) || now.Sub(issued.Time) > age {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
end, e := claims.GetExpirationTime()
|
||||
if e != nil || end == nil || !now.Before(end.Time) {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
aud, e := claims.GetAudience()
|
||||
if e != nil {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
if authorizedParty, present := claims["azp"]; present {
|
||||
if azp, ok := authorizedParty.(string); !ok || azp != a.Audience {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
} else if len(aud) > 1 {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
value := func(name string) (string, bool) { v, ok := claims[name].(string); return v, ok && validClaimLiteral(v) }
|
||||
tenant, ok := value(a.TenantClaim)
|
||||
if !ok {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
principal, ok := value(a.PrincipalTypeClaim)
|
||||
if !ok {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
assurance, ok := value(a.AssuranceClaim)
|
||||
if !ok {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
return VerifiedApplicationIdentity{Issuer: a.Issuer, Subject: subject, Tenant: tenant, PrincipalType: api.SubjectType(principal), Assurance: assurance, NotAfter: end.Time}, nil
|
||||
}
|
||||
|
||||
func (source *Source) authorizeAccessEngine(ctx context.Context, user goth.User) error {
|
||||
// The guarded target extension cannot silently fall back to a local group
|
||||
// decision when admission, keys, facts, PDP or caller authentication is absent.
|
||||
if source.Provider != "openidConnect" || ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues) != nil {
|
||||
return errApplicationDecision
|
||||
}
|
||||
a, e := loadAccessEngineAdmission(os.Getenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE"))
|
||||
if e != nil || source.authSource == nil || a.SourceName != source.authSource.Name || a.Audience != source.ClientID {
|
||||
return errApplicationDecision
|
||||
}
|
||||
identity, e := VerifyApplicationIdentity(user.IDToken, a, time.Now().UTC())
|
||||
if e != nil {
|
||||
return errApplicationDecision
|
||||
}
|
||||
// RequireCurrentUserInfoClaim has refused missing/malformed current groups
|
||||
// before Goth can merge. The validated ID subject must still match that join.
|
||||
sub, ok := user.RawData["sub"].(string)
|
||||
if !ok || sub != identity.Subject || user.UserID != identity.Subject {
|
||||
return errApplicationDecision
|
||||
}
|
||||
groups, ok := strictClaimValues(user.RawData[source.RequiredClaimName])
|
||||
if !ok {
|
||||
return errApplicationDecision
|
||||
}
|
||||
client, e := decisionpep.New(a.Decision)
|
||||
if e != nil {
|
||||
return errApplicationDecision
|
||||
}
|
||||
request := api.CheckRequest{Tenant: identity.Tenant,
|
||||
Subject: api.SubjectRef{ID: identity.Subject, Type: identity.PrincipalType, Tenant: identity.Tenant,
|
||||
Attributes: map[string]any{"issuer": identity.Issuer, "assurance": identity.Assurance, "current_userinfo_groups": groups}},
|
||||
Action: "application.access", Resource: api.ResourceRef{ID: "application:forgejo", Type: "application", System: "forgejo", Tenant: identity.Tenant},
|
||||
Context: map[string]any{"identity_issuer": identity.Issuer, "identity_subject": identity.Subject, "identity_tenant": identity.Tenant, "identity_principal_type": string(identity.PrincipalType), "identity_assurance": identity.Assurance, "identity_not_after": identity.NotAfter.UTC().Format(time.RFC3339Nano), "current_userinfo_groups": groups, "required_groups_any_of": source.RequiredClaimValues, "source_name": a.SourceName, "source_id": source.authSource.ID}}
|
||||
if _, e = client.Check(ctx, request); e != nil {
|
||||
return errApplicationDecision
|
||||
}
|
||||
if !time.Now().UTC().Before(identity.NotAfter) {
|
||||
return errApplicationDecision
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package oauth2
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAccessEngineAuthorizedPartyRefusesAnotherClient(t *testing.T) {
|
||||
for name, value := range map[string]any{
|
||||
"other-client": "other-client",
|
||||
"number": 17,
|
||||
"array": []string{"forgejo"},
|
||||
"empty": "",
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
admission, key, claims, now := identityFixture(t)
|
||||
claims["azp"] = value
|
||||
token := signedIdentity(t, key, claims, nil)
|
||||
if _, err := VerifyApplicationIdentity(token, admission, now); err == nil {
|
||||
t.Fatal("a valid signature and audience concealed an invalid authorized party")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessEngineAuthorizedPartyAcceptsAdmittedClient(t *testing.T) {
|
||||
for _, multiple := range []bool{false, true} {
|
||||
admission, key, claims, now := identityFixture(t)
|
||||
claims["azp"] = admission.Audience
|
||||
if multiple {
|
||||
claims["aud"] = []string{admission.Audience, "another-resource"}
|
||||
}
|
||||
if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err != nil {
|
||||
t.Fatal("admitted authorized party refused", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
authmodel "forgejo.org/models/auth"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/gothic"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
"github.com/netkingdom/access-engine/pkg/decisionpep"
|
||||
"github.com/netkingdom/access-engine/testsupport/signfixture"
|
||||
)
|
||||
|
||||
// This fixture substitutes only Goth's completed authentication result. The
|
||||
// actual Source.Callback, JOSE verifier, HTTPS PEP client and producer signature
|
||||
// implementation execute. Actual issuer login and native admission are separate.
|
||||
func callbackFixture(t *testing.T, group, mode string) (*Source, *goth.User, *atomic.Int64) {
|
||||
t.Helper()
|
||||
admission, identityKey, claims, _ := identityFixture(t)
|
||||
identityToken := signedIdentity(t, identityKey, claims, nil)
|
||||
pub, key, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
calls := new(atomic.Int64)
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
calls.Add(1)
|
||||
if r.URL.Path != "/v1/check" || r.Method != http.MethodPost || r.Header.Get("Authorization") != "Bearer synthetic-caller" || r.Header.Get("Access-Engine-JSON-Profile") != decisionpep.JSONProfile {
|
||||
t.Error("incorrect authenticated PDP request")
|
||||
w.WriteHeader(403)
|
||||
return
|
||||
}
|
||||
var request api.CheckRequest
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
t.Error(err)
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
c := request.Context
|
||||
if request.Subject.ID != "user:synthetic" || request.Subject.Tenant != "tenant:synthetic" || request.Subject.Type != "human" || request.Action != "application.access" || request.Resource.ID != "application:forgejo" || c["identity_issuer"] != "https://issuer.invalid" || c["identity_assurance"] != "aal2" || c["identity_tenant"] != "tenant:synthetic" || c["source_id"] != json.Number("17") {
|
||||
t.Error("PDP request did not use authenticated identity and source")
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
envelope := api.DecisionEnvelope{ID: "synthetic-decision", RequestID: request.ID, Effect: api.DecisionEffectAllow, MatchedPolicyVersion: "v1", Subject: request.Subject, Resource: request.Resource, Binding: api.NewDecisionBindingFor(request, request), Provenance: api.DecisionProvenance{Evaluator: "flex-auth/local", Mode: "standalone/" + decisionpep.JSONProfile, PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyPackageDigest: "sha256:" + strings.Repeat("1", 64), RegistrySnapshotDigest: "sha256:" + strings.Repeat("2", 64), Caller: &api.CallerProvenance{Mode: "enforce", Principal: "system:serviceaccount:synthetic:forgejo", Audience: "access-engine", NotAfter: now.Add(time.Minute).Format(time.RFC3339Nano)}}}
|
||||
api.CompleteDecision(&envelope, request, api.DecisionCompletion{AllowTTL: "10s", Now: now})
|
||||
switch mode {
|
||||
case "deny":
|
||||
envelope.Effect = api.DecisionEffectDeny
|
||||
case "wrong-binding":
|
||||
envelope.Binding.Subject.ID = "other"
|
||||
case "wrong-pin":
|
||||
envelope.Provenance.PolicyPackageDigest = "sha256:" + strings.Repeat("3", 64)
|
||||
case "expired":
|
||||
envelope.Lifetime.ExpiresAt = now.Add(-time.Hour).Format(time.RFC3339Nano)
|
||||
}
|
||||
if mode != "unsigned" {
|
||||
signingKey := key
|
||||
if mode == "wrong-key" {
|
||||
_, signingKey, err = ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := signfixture.Sign(&envelope, signingKey, "synthetic-pdp"); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(envelope); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
dir := t.TempDir()
|
||||
ca := filepath.Join(dir, "ca.pem")
|
||||
token := filepath.Join(dir, "caller")
|
||||
config := filepath.Join(dir, "admission.json")
|
||||
if err := os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(token, []byte("synthetic-caller"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
admission.SourceName = "synthetic-source"
|
||||
admission.Decision = decisionpep.Admission{Endpoint: server.URL + "/v1/check", CAFile: ca, TokenFile: token, Keys: map[string]string{"synthetic-pdp": base64.RawURLEncoding.EncodeToString(pub)}, Evaluator: "flex-auth/local", PolicyPackage: "forgejo.application-access", PolicyVersion: "v1", PolicyDigest: "sha256:" + strings.Repeat("1", 64), RegistryDigest: "sha256:" + strings.Repeat("2", 64), CallerPrincipal: "system:serviceaccount:synthetic:forgejo", CallerAudience: "access-engine", MaxTTL: "30s", MaxClockSkew: "2s"}
|
||||
raw, err := json.Marshal(admission)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = os.WriteFile(config, raw, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", config)
|
||||
source := &Source{Provider: "openidConnect", ClientID: admission.Audience, RequiredClaimName: "groups", RequiredClaimValues: []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}}
|
||||
source.authSource = &authmodel.Source{ID: 17, Name: admission.SourceName}
|
||||
user := &goth.User{UserID: "user:synthetic", IDToken: identityToken, RawData: map[string]any{"sub": "user:synthetic", "groups": []string{group}}}
|
||||
previous := gothic.CompleteUserAuth
|
||||
gothic.CompleteUserAuth = func(http.ResponseWriter, *http.Request) (goth.User, error) { return *user, nil }
|
||||
t.Cleanup(func() { gothic.CompleteUserAuth = previous })
|
||||
return source, user, calls
|
||||
}
|
||||
|
||||
func TestAccessEngineActualCallbackAllowsAuthenticatedDecision(t *testing.T) {
|
||||
for _, group := range []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"} {
|
||||
t.Run(group, func(t *testing.T) {
|
||||
source, user, calls := callbackFixture(t, group, "allow")
|
||||
// Mutable UserInfo cannot supply identity assurance, issuer or tenant.
|
||||
user.RawData["acr"] = "aal1"
|
||||
user.RawData["tenant"] = "tenant:attacker"
|
||||
user.RawData["iss"] = "https://attacker.invalid"
|
||||
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
|
||||
if err != nil || got.UserID != user.UserID || calls.Load() != 1 {
|
||||
t.Fatalf("callback result: err=%v requests=%d", err, calls.Load())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessEngineActualCallbackRefusesUnverifiedPDP(t *testing.T) {
|
||||
for _, mode := range []string{"deny", "unsigned", "wrong-key", "wrong-pin", "wrong-binding", "expired"} {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
source, _, calls := callbackFixture(t, "forge-admins", mode)
|
||||
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
|
||||
if err == nil || got.UserID != "" || got.IDToken != "" || calls.Load() != 1 {
|
||||
t.Fatalf("unverified decision escaped callback: requests=%d err=%v", calls.Load(), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessEngineActualCallbackChecksIdentityBeforePDP(t *testing.T) {
|
||||
cases := map[string]func(*Source, *goth.User){
|
||||
"missing-token": func(_ *Source, u *goth.User) { u.IDToken = "" },
|
||||
"mismatched-user": func(_ *Source, u *goth.User) { u.UserID = "other" },
|
||||
"mismatched-subject": func(_ *Source, u *goth.User) { u.RawData["sub"] = "other" },
|
||||
"missing-groups": func(_ *Source, u *goth.User) { delete(u.RawData, "groups") },
|
||||
"wrong-source": func(s *Source, _ *goth.User) { s.authSource.Name = "other" },
|
||||
"wrong-provider": func(s *Source, _ *goth.User) { s.Provider = "other" },
|
||||
"wrong-audience": func(s *Source, _ *goth.User) { s.ClientID = "other" },
|
||||
"missing-admission": func(_ *Source, _ *goth.User) { t.Setenv("FORGEJO_ACCESS_ENGINE_ADMISSION_FILE", "") },
|
||||
"expired-token": func(_ *Source, u *goth.User) {
|
||||
_, key, claims, now := identityFixture(t)
|
||||
claims["exp"] = now.Add(-time.Hour).Unix()
|
||||
u.IDToken = signedIdentity(t, key, claims, nil)
|
||||
},
|
||||
"untrusted-identity": func(_ *Source, u *goth.User) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodNone, jwt.MapClaims{"sub": "user:synthetic"})
|
||||
u.IDToken, _ = token.SignedString(jwt.UnsafeAllowNoneSignatureType)
|
||||
},
|
||||
}
|
||||
for name, mutate := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
source, user, calls := callbackFixture(t, "forge-admins", "allow")
|
||||
mutate(source, user)
|
||||
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
|
||||
if err == nil || got.UserID != "" || calls.Load() != 0 {
|
||||
t.Fatalf("unverified identity reached PDP: requests=%d err=%v", calls.Load(), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func identityFixture(t *testing.T) (AccessEngineAdmission, ed25519.PrivateKey, jwt.MapClaims, time.Time) {
|
||||
t.Helper()
|
||||
pub, key, e := ed25519.GenerateKey(rand.Reader)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
der, e := x509.MarshalPKIXPublicKey(pub)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
now := time.Now().UTC().Truncate(time.Second)
|
||||
a := AccessEngineAdmission{Issuer: "https://issuer.invalid", Audience: "forgejo", TokenType: "JWT", Algorithms: []string{"EdDSA"}, IdentityKeys: map[string]string{"synthetic": string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))}, TenantClaim: "tenant", PrincipalTypeClaim: "principal_type", AssuranceClaim: "acr", MaxIdentityAge: "5m"}
|
||||
c := jwt.MapClaims{"iss": a.Issuer, "aud": a.Audience, "sub": "user:synthetic", "iat": now.Add(-time.Second).Unix(), "exp": now.Add(time.Minute).Unix(), "tenant": "tenant:synthetic", "principal_type": "human", "acr": "aal2"}
|
||||
return a, key, c, now
|
||||
}
|
||||
func signedIdentity(t *testing.T, key ed25519.PrivateKey, c jwt.MapClaims, mutate func(*jwt.Token)) string {
|
||||
t.Helper()
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, c)
|
||||
token.Header["kid"] = "synthetic"
|
||||
token.Header["typ"] = "JWT"
|
||||
if mutate != nil {
|
||||
mutate(token)
|
||||
}
|
||||
raw, e := token.SignedString(key)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
func TestVerifiedApplicationIdentityActualJOSE(t *testing.T) {
|
||||
a, key, c, now := identityFixture(t)
|
||||
raw := signedIdentity(t, key, c, nil)
|
||||
got, e := VerifyApplicationIdentity(raw, a, now)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
if got.Subject != "user:synthetic" || got.Tenant != "tenant:synthetic" || got.Assurance != "aal2" || got.Issuer != a.Issuer || string(got.PrincipalType) != "human" {
|
||||
t.Fatal("identity did not come from verified token")
|
||||
}
|
||||
cases := map[string]func(jwt.MapClaims){"issuer": func(c jwt.MapClaims) { c["iss"] = "https://other.invalid" }, "audience": func(c jwt.MapClaims) { c["aud"] = "other" }, "expired": func(c jwt.MapClaims) { c["exp"] = now.Add(-time.Nanosecond).Unix() }, "future-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(time.Minute).Unix() }, "old-issued": func(c jwt.MapClaims) { c["iat"] = now.Add(-10 * time.Minute).Unix() }, "no-exp": func(c jwt.MapClaims) { delete(c, "exp") }, "no-iat": func(c jwt.MapClaims) { delete(c, "iat") }, "no-tenant": func(c jwt.MapClaims) { delete(c, "tenant") }, "no-principal": func(c jwt.MapClaims) { delete(c, "principal_type") }, "no-assurance": func(c jwt.MapClaims) { delete(c, "acr") }, "multi-audience-no-azp": func(c jwt.MapClaims) { c["aud"] = []string{"forgejo", "other"} }, "empty-subject": func(c jwt.MapClaims) { c["sub"] = "" }}
|
||||
for name, change := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
copy := jwt.MapClaims{}
|
||||
for k, v := range c {
|
||||
copy[k] = v
|
||||
}
|
||||
change(copy)
|
||||
if _, e := VerifyApplicationIdentity(signedIdentity(t, key, copy, nil), a, now); e == nil {
|
||||
t.Fatal("unverified identity accepted")
|
||||
}
|
||||
})
|
||||
}
|
||||
t.Run("wrong-key", func(t *testing.T) {
|
||||
_, other, _ := ed25519.GenerateKey(rand.Reader)
|
||||
if _, e := VerifyApplicationIdentity(signedIdentity(t, other, c, nil), a, now); e == nil {
|
||||
t.Fatal("wrong key accepted")
|
||||
}
|
||||
})
|
||||
t.Run("unknown-key", func(t *testing.T) {
|
||||
if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["kid"] = "other" }), a, now); e == nil {
|
||||
t.Fatal("unknown key accepted")
|
||||
}
|
||||
})
|
||||
t.Run("wrong-type", func(t *testing.T) {
|
||||
if _, e := VerifyApplicationIdentity(signedIdentity(t, key, c, func(t *jwt.Token) { t.Header["typ"] = "at+jwt" }), a, now); e == nil {
|
||||
t.Fatal("access token accepted as ID token")
|
||||
}
|
||||
})
|
||||
t.Run("algorithm-confusion", func(t *testing.T) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
|
||||
token.Header["kid"] = "synthetic"
|
||||
token.Header["typ"] = "JWT"
|
||||
raw, _ := token.SignedString([]byte(a.IdentityKeys["synthetic"]))
|
||||
if _, e := VerifyApplicationIdentity(raw, a, now); e == nil {
|
||||
t.Fatal("algorithm confusion accepted")
|
||||
}
|
||||
})
|
||||
t.Run("none", func(t *testing.T) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodNone, c)
|
||||
token.Header["kid"] = "synthetic"
|
||||
token.Header["typ"] = "JWT"
|
||||
raw, _ := token.SignedString(jwt.UnsafeAllowNoneSignatureType)
|
||||
if _, e := VerifyApplicationIdentity(raw, a, now); e == nil {
|
||||
t.Fatal("unsigned identity accepted")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/gothic"
|
||||
)
|
||||
|
||||
// Callout redirects request/response pair to authenticate against the provider
|
||||
func (source *Source) Callout(request *http.Request, response http.ResponseWriter, codeChallengeS256 string) error {
|
||||
// not sure if goth is thread safe (?) when using multiple providers
|
||||
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
|
||||
|
||||
var querySuffix string
|
||||
if codeChallengeS256 != "" {
|
||||
querySuffix = "&" + url.Values{
|
||||
"code_challenge_method": []string{"S256"},
|
||||
"code_challenge": []string{codeChallengeS256},
|
||||
}.Encode()
|
||||
}
|
||||
|
||||
// don't use the default gothic begin handler to prevent issues when some error occurs
|
||||
// normally the gothic library will write some custom stuff to the response instead of our own nice error page
|
||||
// gothic.BeginAuthHandler(response, request)
|
||||
|
||||
gothRWMutex.RLock()
|
||||
defer gothRWMutex.RUnlock()
|
||||
|
||||
url, err := gothic.GetAuthURL(response, request)
|
||||
if err == nil {
|
||||
// hacky way to set the code_challenge, but no better way until
|
||||
// https://github.com/markbates/goth/issues/516 is resolved
|
||||
http.Redirect(response, request, url+querySuffix, http.StatusTemporaryRedirect)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Callback handles OAuth callback, resolve to a goth user and send back to original url
|
||||
// this will trigger a new authentication request, but because we save it in the session we can use that
|
||||
func (source *Source) Callback(request *http.Request, response http.ResponseWriter, codeVerifier string) (goth.User, error) {
|
||||
// not sure if goth is thread safe (?) when using multiple providers
|
||||
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
|
||||
|
||||
if codeVerifier != "" {
|
||||
// hacky way to set the code_verifier...
|
||||
// Will be picked up inside CompleteUserAuth: params := req.URL.Query()
|
||||
// https://github.com/markbates/goth/pull/474/files
|
||||
request = request.Clone(request.Context())
|
||||
q := request.URL.Query()
|
||||
q.Add("code_verifier", codeVerifier)
|
||||
request.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
gothRWMutex.RLock()
|
||||
defer gothRWMutex.RUnlock()
|
||||
|
||||
user, err := gothic.CompleteUserAuth(response, request)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
if source.RequiredClaimValues != nil {
|
||||
if err := source.authorizeAccessEngine(request.Context(), user); err != nil {
|
||||
return goth.User{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
BIN_DIR ?= bin
|
||||
BIN := $(BIN_DIR)/access-engine
|
||||
PKG := ./...
|
||||
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo 0.0.0-dev)
|
||||
LDFLAGS := -X main.version=$(VERSION)
|
||||
|
||||
.PHONY: all build test vet lint fmt tidy sbom clean ci overlay-render overlay-dry-run verify-posture check-layer
|
||||
|
||||
all: vet lint test build
|
||||
|
||||
build:
|
||||
@mkdir -p $(BIN_DIR)
|
||||
go build -ldflags "$(LDFLAGS)" -o $(BIN) ./cmd/access-engine
|
||||
|
||||
test: check-layer
|
||||
go test -race $(PKG)
|
||||
|
||||
check-layer:
|
||||
go run ./tools/check_layer_conformance.go
|
||||
|
||||
vet:
|
||||
go vet $(PKG)
|
||||
|
||||
fmt:
|
||||
gofmt -l -w .
|
||||
|
||||
tidy:
|
||||
go mod tidy
|
||||
|
||||
lint:
|
||||
@if command -v golangci-lint >/dev/null 2>&1; then \
|
||||
golangci-lint run $(PKG); \
|
||||
else \
|
||||
echo "golangci-lint not installed; run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; \
|
||||
echo "falling back to: go vet"; \
|
||||
go vet $(PKG); \
|
||||
fi
|
||||
|
||||
GOBIN_PATH := $(shell go env GOPATH)/bin
|
||||
|
||||
sbom:
|
||||
@mkdir -p $(BIN_DIR)
|
||||
@if command -v cyclonedx-gomod >/dev/null 2>&1; then \
|
||||
cyclonedx-gomod mod -json -output $(BIN_DIR)/sbom.cdx.json .; \
|
||||
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (cyclonedx-gomod)"; \
|
||||
elif [ -x "$(GOBIN_PATH)/cyclonedx-gomod" ]; then \
|
||||
"$(GOBIN_PATH)/cyclonedx-gomod" mod -json -output $(BIN_DIR)/sbom.cdx.json .; \
|
||||
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (cyclonedx-gomod via GOPATH)"; \
|
||||
elif command -v syft >/dev/null 2>&1; then \
|
||||
syft . -o cyclonedx-json=$(BIN_DIR)/sbom.cdx.json; \
|
||||
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (syft)"; \
|
||||
else \
|
||||
echo "no SBOM tool found. install one:"; \
|
||||
echo " go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest"; \
|
||||
echo " or syft: https://github.com/anchore/syft"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -rf $(BIN_DIR)
|
||||
|
||||
ci: vet lint test build overlay-render
|
||||
|
||||
verify-posture:
|
||||
@bash tools/verify-posture.sh
|
||||
|
||||
.PHONY: verify-emission-cadence
|
||||
verify-emission-cadence:
|
||||
python3 tools/verify_emission_cadence.py
|
||||
|
||||
overlay-render:
|
||||
@tests/stage1.sh
|
||||
|
||||
overlay-dry-run:
|
||||
@test -n "$$KUBECONFIG" || { echo "set KUBECONFIG to the railiance01 kubeconfig"; exit 1; }
|
||||
@for values in values/stage2-canary.yaml values/user-engine.yaml values/tenant-engine.yaml; do \
|
||||
echo "server-dry-run $$values"; \
|
||||
helm template flex-auth charts/flex-auth -f "$$values" --namespace flex-auth \
|
||||
| kubectl apply --dry-run=server --server-side -f -; \
|
||||
done
|
||||
|
|
@ -0,0 +1,757 @@
|
|||
// Command flex-auth is the CLI entry point for the flex-auth authorization
|
||||
// registry and control plane.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/netkingdom/access-engine/internal/audit"
|
||||
"github.com/netkingdom/access-engine/internal/callerauth"
|
||||
decisioncore "github.com/netkingdom/access-engine/internal/decision"
|
||||
"github.com/netkingdom/access-engine/internal/emission"
|
||||
"github.com/netkingdom/access-engine/internal/policy"
|
||||
"github.com/netkingdom/access-engine/internal/registry"
|
||||
"github.com/netkingdom/access-engine/internal/taskexecution"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
)
|
||||
|
||||
// version is set at build time via -ldflags "-X main.version=...".
|
||||
var version = "0.0.0-dev"
|
||||
|
||||
func main() {
|
||||
os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
|
||||
}
|
||||
|
||||
func run(args []string, stdout, stderr io.Writer) int {
|
||||
if len(args) == 0 {
|
||||
printUsage(stderr)
|
||||
return 64
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "version", "--version", "-version":
|
||||
fmt.Fprintln(stdout, version)
|
||||
return 0
|
||||
case "validate":
|
||||
return runValidate(args[1:], stdout, stderr)
|
||||
case "load-registry":
|
||||
return runLoadRegistry(args[1:], stdout, stderr)
|
||||
case "test-policy":
|
||||
return runTestPolicy(args[1:], stdout, stderr)
|
||||
case "check":
|
||||
return runCheck(args[1:], stdout, stderr)
|
||||
case "batch-check":
|
||||
return runBatchCheck(args[1:], stdout, stderr)
|
||||
case "list-allowed":
|
||||
return runListAllowed(args[1:], stdout, stderr)
|
||||
case "explain":
|
||||
return runExplain(args[1:], stdout, stderr)
|
||||
case "serve":
|
||||
return runServe(args[1:], stdout, stderr)
|
||||
case "serve-readback":
|
||||
return runServeReadback(args[1:], stdout, stderr)
|
||||
case "help", "-h", "--help":
|
||||
printUsage(stdout)
|
||||
return 0
|
||||
default:
|
||||
fmt.Fprintf(stderr, "access-engine: unknown subcommand %q\n", args[0])
|
||||
printUsage(stderr)
|
||||
return 64
|
||||
}
|
||||
}
|
||||
|
||||
func runValidate(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("validate", stderr)
|
||||
kind := fs.String("kind", "", "resource-manifest, subject-manifest, protected-system, relationship, access-descriptor, or policy")
|
||||
file := fs.String("file", "", "YAML/JSON file to validate")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *kind == "" || *file == "" {
|
||||
fmt.Fprintln(stderr, "validate requires --kind and --file")
|
||||
return 64
|
||||
}
|
||||
|
||||
switch *kind {
|
||||
case "resource-manifest":
|
||||
var manifest api.ResourceManifest
|
||||
if err := readYAML(*file, &manifest); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := registry.NewStore().ImportResourceManifest(manifest); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
case "subject-manifest":
|
||||
var manifest api.SubjectManifest
|
||||
if err := readYAML(*file, &manifest); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := registry.NewStore().ImportSubjectManifest(manifest); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
case "protected-system":
|
||||
var manifest api.ProtectedSystemManifest
|
||||
if err := readYAML(*file, &manifest); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := registry.NewStore().PutProtectedSystem(manifest); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
case "relationship":
|
||||
var fact api.RelationshipFact
|
||||
if err := readYAML(*file, &fact); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := registry.NewStore().PutRelationship(fact); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
case "access-descriptor":
|
||||
var descriptor api.CaringAccessDescriptor
|
||||
if err := readYAML(*file, &descriptor); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := validateDescriptor(descriptor); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
case "policy", "policy-package":
|
||||
pkg, err := policy.LoadAndValidateFile(context.Background(), *file)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := writeJSON(stdout, pkg.Validation); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if !pkg.Valid {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
fmt.Fprintf(stderr, "unsupported validate kind %q\n", *kind)
|
||||
return 64
|
||||
}
|
||||
|
||||
return writeStatus(stdout, "valid", map[string]any{"kind": *kind, "file": *file})
|
||||
}
|
||||
|
||||
func runLoadRegistry(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("load-registry", stderr)
|
||||
file := fs.String("file", "", "registry snapshot JSON file")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *file == "" {
|
||||
fmt.Fprintln(stderr, "load-registry requires --file")
|
||||
return 64
|
||||
}
|
||||
|
||||
store, err := registry.LoadFile(*file)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
snapshot := store.Snapshot()
|
||||
return writeStatus(stdout, "loaded", map[string]any{
|
||||
"file": *file,
|
||||
"systems": len(snapshot.Systems),
|
||||
"resource_manifests": len(snapshot.ResourceManifests),
|
||||
"subjects": len(snapshot.Subjects),
|
||||
"groups": len(snapshot.Groups),
|
||||
"teams": len(snapshot.Teams),
|
||||
"tenants": len(snapshot.Tenants),
|
||||
"relationships": len(snapshot.Relationships),
|
||||
})
|
||||
}
|
||||
|
||||
func runTestPolicy(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("test-policy", stderr)
|
||||
file := fs.String("file", "", "policy package Markdown file")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *file == "" {
|
||||
fmt.Fprintln(stderr, "test-policy requires --file")
|
||||
return 64
|
||||
}
|
||||
|
||||
pkg, err := policy.LoadAndValidateFile(context.Background(), *file)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := writeJSON(stdout, pkg.Validation); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if !pkg.Valid {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func runCheck(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("check", stderr)
|
||||
registryPath := fs.String("registry", "", "registry snapshot JSON file")
|
||||
policyPath := fs.String("policy", "", "policy package Markdown file")
|
||||
requestPath := fs.String("request", "", "check request YAML/JSON file")
|
||||
logPath := fs.String("log", "", "optional JSONL decision log path")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *registryPath == "" || *policyPath == "" || *requestPath == "" {
|
||||
fmt.Fprintln(stderr, "check requires --registry, --policy, and --request")
|
||||
return 64
|
||||
}
|
||||
|
||||
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
var request api.CheckRequest
|
||||
if err := readYAML(*requestPath, &request); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
decision, err := engine.Check(context.Background(), request)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := writeJSON(stdout, decision); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func runBatchCheck(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("batch-check", stderr)
|
||||
registryPath := fs.String("registry", "", "registry snapshot JSON file")
|
||||
policyPath := fs.String("policy", "", "policy package Markdown file")
|
||||
requestPath := fs.String("request", "", "batch check request YAML/JSON file")
|
||||
logPath := fs.String("log", "", "optional JSONL decision log path")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *registryPath == "" || *policyPath == "" || *requestPath == "" {
|
||||
fmt.Fprintln(stderr, "batch-check requires --registry, --policy, and --request")
|
||||
return 64
|
||||
}
|
||||
|
||||
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
var request api.BatchCheckRequest
|
||||
if err := readYAML(*requestPath, &request); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
decisions, err := engine.BatchCheck(context.Background(), request)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := writeJSON(stdout, decisions); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func runListAllowed(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("list-allowed", stderr)
|
||||
registryPath := fs.String("registry", "", "registry snapshot JSON file")
|
||||
policyPath := fs.String("policy", "", "policy package Markdown file")
|
||||
subject := fs.String("subject", "", "subject id")
|
||||
action := fs.String("action", "", "action name")
|
||||
system := fs.String("system", "", "protected system id")
|
||||
resourceType := fs.String("resource-type", "", "resource type")
|
||||
logPath := fs.String("log", "", "optional JSONL decision log path")
|
||||
var filters keyValueFlags
|
||||
fs.Var(&filters, "filter", "resource filter as key=value; may be repeated")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *registryPath == "" || *policyPath == "" || *subject == "" || *action == "" {
|
||||
fmt.Fprintln(stderr, "list-allowed requires --registry, --policy, --subject, and --action")
|
||||
return 64
|
||||
}
|
||||
|
||||
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
decisions, err := engine.ListAllowed(context.Background(), decisioncore.ListAllowedRequest{
|
||||
Subject: api.SubjectRef{ID: *subject},
|
||||
Action: *action,
|
||||
System: *system,
|
||||
ResourceType: *resourceType,
|
||||
Filters: filters.Map(),
|
||||
})
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := writeJSON(stdout, decisions); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func runExplain(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("explain", stderr)
|
||||
logPath := fs.String("decision-log", "", "JSONL decision log path")
|
||||
decisionID := fs.String("decision-id", "", "decision id to explain")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *logPath == "" || *decisionID == "" {
|
||||
fmt.Fprintln(stderr, "explain requires --decision-log and --decision-id")
|
||||
return 64
|
||||
}
|
||||
|
||||
log := audit.NewJSONLDecisionLog(*logPath)
|
||||
envelope, ok, err := log.Find(*decisionID)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if !ok {
|
||||
return fail(stderr, fmt.Errorf("decision %q not found", *decisionID))
|
||||
}
|
||||
if err := writeJSON(stdout, decisioncore.ExplainEnvelope(envelope)); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func runServe(args []string, stdout, stderr io.Writer) int {
|
||||
fs := newFlagSet("serve", stderr)
|
||||
addr := fs.String("addr", "127.0.0.1:8080", "HTTP listen address")
|
||||
registryPath := fs.String("registry", "", "registry snapshot JSON file")
|
||||
policyPath := fs.String("policy", "", "policy package Markdown file")
|
||||
logPath := fs.String("log", "", "optional JSONL decision log path")
|
||||
signingKeyFile := fs.String("envelope-signing-key-file", "", "owner-mounted base64url Ed25519 seed file")
|
||||
signingKID := fs.String("envelope-signing-kid", "", "independently admitted envelope signing key id")
|
||||
approvalSourceFile := fs.String("approval-source-file", "", "owner fixed Approval HTTPS reader configuration file")
|
||||
outboxMaxBytes := fs.Int64("outbox-max-bytes", emission.MaxOutboxBytes, "bounded outbox payload bytes, maximum67108864")
|
||||
outboxDir := fs.String("outbox-dir", "", "durable emission outbox directory (FLEX-WP-0031); exclusive with --log")
|
||||
emissionSource := fs.String("emission-source", "", "audit-core source name for emitted decision events, e.g. flex-auth.tenant-engine")
|
||||
auditURL := fs.String("audit-url", "", "admitted audit-core /v1/events endpoint; empty disables drain and heartbeats")
|
||||
auditTokenFile := fs.String("audit-token-file", "", "mounted sender token file, never a token value")
|
||||
callerAuthMode := fs.String("caller-auth-mode", "disabled", "disabled, warn, or enforce")
|
||||
callerAudience := fs.String("caller-audience", "flex-auth", "required caller token audience")
|
||||
callerKubernetesURL := fs.String("caller-kubernetes-url", "https://kubernetes.default.svc", "Kubernetes API base URL for TokenReview")
|
||||
callerReviewerTokenFile := fs.String("caller-reviewer-token-file", "/var/run/secrets/flex-auth-reviewer/token", "projected Kubernetes API credential")
|
||||
callerCAFile := fs.String("caller-ca-file", "/var/run/secrets/flex-auth-reviewer/ca.crt", "Kubernetes API CA bundle")
|
||||
var callerBindings keyValueFlags
|
||||
fs.Var(&callerBindings, "caller-binding", "resource system=authenticated Kubernetes principal (repeatable)")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return 64
|
||||
}
|
||||
if *registryPath == "" || *policyPath == "" {
|
||||
fmt.Fprintln(stderr, "serve requires --registry and --policy")
|
||||
return 64
|
||||
}
|
||||
|
||||
if *outboxDir != "" && (*logPath != "" || *emissionSource == "") {
|
||||
fmt.Fprintln(stderr, "serve --outbox-dir requires --emission-source and excludes --log")
|
||||
return 64
|
||||
}
|
||||
delivery := emission.DeliveryConfig{URL: *auditURL, TokenFile: *auditTokenFile}
|
||||
if *auditURL != "" || *auditTokenFile != "" {
|
||||
if *outboxDir == "" {
|
||||
fmt.Fprintln(stderr, "audit delivery requires --outbox-dir")
|
||||
return 64
|
||||
}
|
||||
if err := delivery.Validate(); err != nil {
|
||||
fmt.Fprintln(stderr, err)
|
||||
return 64
|
||||
}
|
||||
}
|
||||
|
||||
engine, err := buildEngine(context.Background(), *registryPath, *policyPath, *logPath)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
if err := configureEnvelopeSigning(engine, *signingKeyFile, *signingKID); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
var outbox *emission.Outbox
|
||||
if *outboxDir != "" {
|
||||
outbox, err = emission.OpenBounded(*outboxDir, *emissionSource, *outboxMaxBytes)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
defer outbox.Close()
|
||||
engine.SetDecisionLog(outbox)
|
||||
}
|
||||
|
||||
authenticator, err := buildCallerAuthenticator(
|
||||
callerauth.Mode(*callerAuthMode),
|
||||
*callerAudience,
|
||||
*callerKubernetesURL,
|
||||
*callerReviewerTokenFile,
|
||||
*callerCAFile,
|
||||
callerBindings.StringMap(),
|
||||
stderr,
|
||||
)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
|
||||
serviceInputs, err := buildServicePilotInputs(engine, callerauth.Mode(*callerAuthMode), *callerAudience, *callerKubernetesURL, *callerReviewerTokenFile, *callerCAFile, callerBindings.StringMap())
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
var approvalReader *taskexecution.ApprovalReader
|
||||
if engine.IsTrustedServicePilot() {
|
||||
if *signingKeyFile == "" || *approvalSourceFile == "" {
|
||||
return fail(stderr, fmt.Errorf("trusted task source requires signing and fixed Approval reader configuration"))
|
||||
}
|
||||
approvalReader, err = taskexecution.LoadApprovalReader(*approvalSourceFile)
|
||||
if err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
} else if *approvalSourceFile != "" {
|
||||
return fail(stderr, fmt.Errorf("Approval reader requires exact trusted task source package"))
|
||||
}
|
||||
mux := newServeMuxWithTaskFacts(engine, authenticator, serviceInputs, approvalReader)
|
||||
if outbox != nil {
|
||||
mountEmission(mux, outbox)
|
||||
}
|
||||
if *auditURL != "" {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
runEmissionDelivery(ctx, outbox, delivery, stderr)
|
||||
}()
|
||||
defer func() { cancel(); <-done }()
|
||||
}
|
||||
|
||||
fmt.Fprintf(stderr, "access-engine serving on http://%s\n", *addr)
|
||||
if err := http.ListenAndServe(*addr, mux); err != nil {
|
||||
return fail(stderr, err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func runEmissionDelivery(ctx context.Context, outbox *emission.Outbox, config emission.DeliveryConfig, stderr io.Writer) {
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if err := outbox.AppendDueHeartbeats(); err != nil {
|
||||
fmt.Fprintf(stderr, "emission heartbeat commit: %v\n", err)
|
||||
}
|
||||
n, err := outbox.Drain(ctx, config, 100)
|
||||
if err != nil && ctx.Err() == nil {
|
||||
fmt.Fprintf(stderr, "emission drain: %v\n", err)
|
||||
}
|
||||
if err == nil && n == 100 {
|
||||
continue // Drain the next bounded batch without imposing a rate cap.
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newServeMux(engine *decisioncore.Engine) *http.ServeMux {
|
||||
return newServeMuxWithCallerAuth(engine, callerauth.Disabled())
|
||||
}
|
||||
|
||||
func newServeMuxWithCallerAuth(engine *decisioncore.Engine, authenticator *callerauth.Authenticator) *http.ServeMux {
|
||||
return newServeMuxWithServicePilotInputs(engine, authenticator, nil)
|
||||
}
|
||||
|
||||
func newServeMuxWithServicePilotInputs(engine *decisioncore.Engine, authenticator *callerauth.Authenticator, serviceInputs *taskexecution.ServicePilotInputs) *http.ServeMux {
|
||||
return newServeMuxWithTaskFacts(engine, authenticator, serviceInputs, nil)
|
||||
}
|
||||
func newServeMuxWithTaskFacts(engine *decisioncore.Engine, authenticator *callerauth.Authenticator, serviceInputs *taskexecution.ServicePilotInputs, approvalReader *taskexecution.ApprovalReader) *http.ServeMux {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
||||
})
|
||||
mux.HandleFunc("/v1/check", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if engine.IsTaskPin() {
|
||||
if serviceInputs == nil {
|
||||
http.Error(w, "service input unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
decode := taskexecution.DecodeServiceRequest
|
||||
if engine.IsTrustedServicePilot() {
|
||||
decode = taskexecution.DecodeTrustedServiceRequest
|
||||
}
|
||||
request, err := decode(r.Body)
|
||||
if err != nil || request.Purpose != "" {
|
||||
http.Error(w, "invalid service task request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
input, err := serviceInputs.Prepare(r.Context(), r.Header.Get("Authorization"), request, time.Now().UTC())
|
||||
if err != nil {
|
||||
http.Error(w, "service task input refused", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
var decision api.DecisionEnvelope
|
||||
if engine.IsTrustedServicePilot() {
|
||||
decision, err = engine.CheckTrustedService(r.Context(), input, approvalReader)
|
||||
} else {
|
||||
decision, err = engine.CheckServicePilot(r.Context(), input)
|
||||
}
|
||||
writeHTTP(w, decision, err)
|
||||
return
|
||||
}
|
||||
var request api.CheckRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if request.Purpose != "" {
|
||||
// Only the readback pin issues purpose-bearing decisions
|
||||
// (FLEX-DEC-2026-019); an enforcement pin never does.
|
||||
http.Error(w, "purpose is not accepted on /v1/check", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
identity, err := authenticator.Authorize(r.Context(), r.Header.Get("Authorization"), []string{request.Resource.System})
|
||||
if err != nil {
|
||||
writeCallerAuthError(w, err)
|
||||
return
|
||||
}
|
||||
decision, err := engine.Check(callerauth.WithRecord(r.Context(), authenticator.Record(identity)), request)
|
||||
writeHTTP(w, decision, err)
|
||||
})
|
||||
mux.HandleFunc("/v1/batch_check", func(w http.ResponseWriter, r *http.Request) {
|
||||
if engine.IsTaskPin() {
|
||||
http.Error(w, "service task batch unavailable", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var request api.BatchCheckRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
systems := make([]string, 0, len(request.Resources))
|
||||
for _, resource := range request.Resources {
|
||||
systems = append(systems, resource.System)
|
||||
}
|
||||
identity, err := authenticator.Authorize(r.Context(), r.Header.Get("Authorization"), systems)
|
||||
if err != nil {
|
||||
writeCallerAuthError(w, err)
|
||||
return
|
||||
}
|
||||
decisions, err := engine.BatchCheck(callerauth.WithRecord(r.Context(), authenticator.Record(identity)), request)
|
||||
writeHTTP(w, decisions, err)
|
||||
})
|
||||
return mux
|
||||
}
|
||||
|
||||
// Only the existing exact service pilot package selects this source path.
|
||||
// This is not an eligibility toggle; successful preparation still only DENYs.
|
||||
func buildServicePilotInputs(engine *decisioncore.Engine, mode callerauth.Mode, audience, kubernetesURL, reviewerTokenFile, caFile string, bindings map[string]string) (*taskexecution.ServicePilotInputs, error) {
|
||||
if !engine.IsTaskPin() {
|
||||
return nil, nil
|
||||
}
|
||||
if mode != callerauth.ModeEnforce || audience != "flex-auth" || len(bindings) != 1 || bindings["secrets-engine"] != taskexecution.ServicePilotPrincipal {
|
||||
return nil, fmt.Errorf("service pilot requires exact enforced retained caller mapping")
|
||||
}
|
||||
reviewer, err := callerauth.NewKubernetesTokenReviewer(kubernetesURL, audience, reviewerTokenFile, caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if engine.IsTrustedServicePilot() {
|
||||
return taskexecution.NewTrustedServiceInputs(reviewer)
|
||||
}
|
||||
return taskexecution.NewServicePilotInputs(reviewer)
|
||||
}
|
||||
|
||||
func buildCallerAuthenticator(mode callerauth.Mode, audience, kubernetesURL, reviewerTokenFile, caFile string, bindings map[string]string, stderr io.Writer) (*callerauth.Authenticator, error) {
|
||||
if mode == callerauth.ModeDisabled {
|
||||
return callerauth.New(mode, nil, "", nil, nil)
|
||||
}
|
||||
reviewer, err := callerauth.NewKubernetesTokenReviewer(kubernetesURL, audience, reviewerTokenFile, caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return callerauth.New(mode, reviewer, audience, bindings, func(format string, args ...any) {
|
||||
fmt.Fprintf(stderr, format+"\n", args...)
|
||||
})
|
||||
}
|
||||
|
||||
func writeCallerAuthError(w http.ResponseWriter, err error) {
|
||||
switch {
|
||||
case errors.Is(err, callerauth.ErrUnauthenticated):
|
||||
http.Error(w, "unauthenticated", http.StatusUnauthorized)
|
||||
case errors.Is(err, callerauth.ErrForbidden):
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
default:
|
||||
http.Error(w, "caller authentication unavailable", http.StatusServiceUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
func buildEngine(ctx context.Context, registryPath, policyPath, logPath string) (*decisioncore.Engine, error) {
|
||||
pkg, err := policy.LoadAndValidateFile(ctx, policyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
load := registry.LoadFile
|
||||
if pkg.Metadata.ID == taskexecution.ServicePilotPackageID && pkg.Metadata.Version == taskexecution.TrustedServicePilotPackageVersion {
|
||||
load = registry.LoadFilePreservingNumbers
|
||||
}
|
||||
store, err := load(registryPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
engine, err := decisioncore.NewEngine(store, pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if logPath != "" {
|
||||
engine.SetDecisionLog(audit.NewJSONLDecisionLog(logPath))
|
||||
}
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
func validateDescriptor(descriptor api.CaringAccessDescriptor) error {
|
||||
if descriptor.Profile != api.CaringProfileCaring040RC2 {
|
||||
return fmt.Errorf("unsupported CARING profile %q", descriptor.Profile)
|
||||
}
|
||||
if descriptor.SubjectType == "" {
|
||||
return fmt.Errorf("subject_type is required")
|
||||
}
|
||||
if descriptor.OrganizationRelation == "" {
|
||||
return fmt.Errorf("organization_relation is required")
|
||||
}
|
||||
if descriptor.CanonicalRole == "" {
|
||||
return fmt.Errorf("canonical_role is required")
|
||||
}
|
||||
if descriptor.Scope.Level == "" || descriptor.Scope.ID == "" {
|
||||
return fmt.Errorf("scope.level and scope.id are required")
|
||||
}
|
||||
if len(descriptor.Planes) == 0 {
|
||||
return fmt.Errorf("at least one plane is required")
|
||||
}
|
||||
if len(descriptor.Capabilities) == 0 {
|
||||
return fmt.Errorf("at least one capability is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readYAML(path string, out any) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := yaml.Unmarshal(data, out); err != nil {
|
||||
return fmt.Errorf("unmarshal %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeJSON(w io.Writer, value any) error {
|
||||
encoder := json.NewEncoder(w)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(value); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeStatus(w io.Writer, status string, extra map[string]any) int {
|
||||
out := map[string]any{"status": status}
|
||||
for key, value := range extra {
|
||||
out[key] = value
|
||||
}
|
||||
if err := writeJSON(w, out); err != nil {
|
||||
return fail(w, err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// mountEmission exposes the local half of reconciliation: committed and
|
||||
// released-uncommitted counts per class. Counts only, never records.
|
||||
func mountEmission(mux *http.ServeMux, outbox *emission.Outbox) {
|
||||
mux.HandleFunc("/v1/emission", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
writeHTTP(w, outbox.Status(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func writeHTTP(w http.ResponseWriter, value any, err error) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
if errors.Is(err, decisioncore.ErrRecordNotCommitted) || errors.Is(err, decisioncore.ErrServicePilotUnavailable) {
|
||||
// Unavailable evaluation is not an authorization verdict (GH-DEC-2026-024).
|
||||
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(value)
|
||||
}
|
||||
|
||||
func newFlagSet(name string, stderr io.Writer) *flag.FlagSet {
|
||||
fs := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||
fs.SetOutput(stderr)
|
||||
return fs
|
||||
}
|
||||
|
||||
func fail(stderr io.Writer, err error) int {
|
||||
fmt.Fprintln(stderr, "access-engine:", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
func printUsage(w io.Writer) {
|
||||
fmt.Fprintln(w, "usage: access-engine <command> [options]")
|
||||
fmt.Fprintln(w, "commands: version, validate, load-registry, test-policy, check, batch-check, list-allowed, explain, serve, serve-readback")
|
||||
}
|
||||
|
||||
type keyValueFlags []string
|
||||
|
||||
func (f *keyValueFlags) String() string {
|
||||
return strings.Join(*f, ",")
|
||||
}
|
||||
|
||||
func (f *keyValueFlags) Set(value string) error {
|
||||
if !strings.Contains(value, "=") {
|
||||
return fmt.Errorf("filter must be key=value")
|
||||
}
|
||||
*f = append(*f, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f keyValueFlags) Map() map[string]any {
|
||||
out := make(map[string]any, len(f))
|
||||
for _, item := range f {
|
||||
key, value, _ := strings.Cut(item, "=")
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (f keyValueFlags) StringMap() map[string]string {
|
||||
out := make(map[string]string, len(f))
|
||||
for _, item := range f {
|
||||
key, value, _ := strings.Cut(item, "=")
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
@ -0,0 +1,729 @@
|
|||
package decision
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/netkingdom/access-engine/internal/callerauth"
|
||||
"github.com/netkingdom/access-engine/internal/policy"
|
||||
"github.com/netkingdom/access-engine/internal/registry"
|
||||
"github.com/netkingdom/access-engine/internal/sign"
|
||||
"github.com/netkingdom/access-engine/internal/taskexecution"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
)
|
||||
|
||||
// Engine evaluates deterministic standalone authorization checks against a
|
||||
// local registry and one validated policy package.
|
||||
type Engine struct {
|
||||
store *registry.Store
|
||||
policy *policy.Package
|
||||
mu sync.RWMutex
|
||||
history map[string]api.DecisionEnvelope
|
||||
log DecisionRecorder
|
||||
clock func() time.Time
|
||||
signingKey ed25519.PrivateKey
|
||||
signingKID string
|
||||
}
|
||||
|
||||
// DecisionRecorder persists decision envelopes.
|
||||
type DecisionRecorder interface {
|
||||
Append(api.DecisionEnvelope) error
|
||||
}
|
||||
|
||||
// BatchRecorder commits several decisions in one durable write.
|
||||
type BatchRecorder interface {
|
||||
AppendBatch([]api.DecisionEnvelope) error
|
||||
}
|
||||
|
||||
// ErrRecordNotCommitted means a decision was withheld because its required
|
||||
// record did not commit (GH-DEC-2026-024). The service answers 503; this is
|
||||
// unavailability, never a substitute authorization verdict.
|
||||
var ErrRecordNotCommitted = errors.New("decision withheld: record not committed")
|
||||
|
||||
// ListAllowedRequest describes a deterministic list_allowed call.
|
||||
type ListAllowedRequest struct {
|
||||
Subject api.SubjectRef `json:"subject"`
|
||||
Action string `json:"action"`
|
||||
System string `json:"system,omitempty"`
|
||||
ResourceType string `json:"resource_type,omitempty"`
|
||||
Filters map[string]any `json:"filters,omitempty"`
|
||||
Context map[string]any `json:"context,omitempty"`
|
||||
PolicyVersion string `json:"policy_version,omitempty"`
|
||||
}
|
||||
|
||||
// Explanation is a compact explanation view over a recorded decision.
|
||||
type Explanation struct {
|
||||
DecisionID string `json:"decision_id"`
|
||||
Effect api.DecisionEffect `json:"effect"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Summary string `json:"summary"`
|
||||
Subject api.SubjectRef `json:"subject"`
|
||||
Resource api.ResourceRef `json:"resource"`
|
||||
PolicyPackage string `json:"policy_package,omitempty"`
|
||||
PolicyVersion string `json:"policy_version,omitempty"`
|
||||
MatchedRule string `json:"matched_rule,omitempty"`
|
||||
Diagnostics map[string]any `json:"diagnostics,omitempty"`
|
||||
Caring *api.CaringDecisionMetadata `json:"caring,omitempty"`
|
||||
}
|
||||
|
||||
// NewEngine creates a standalone decision engine.
|
||||
func NewEngine(store *registry.Store, policyPackage *policy.Package) (*Engine, error) {
|
||||
if store == nil {
|
||||
return nil, fmt.Errorf("registry store is required")
|
||||
}
|
||||
if policyPackage == nil {
|
||||
return nil, fmt.Errorf("policy package is required")
|
||||
}
|
||||
if !policyPackage.Valid {
|
||||
return nil, fmt.Errorf("policy package %q is not valid", policyPackage.Metadata.ID)
|
||||
}
|
||||
return &Engine{
|
||||
store: store,
|
||||
policy: policyPackage,
|
||||
history: make(map[string]api.DecisionEnvelope),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetEnvelopeSigningKey installs an owner-custodied key before serving requests.
|
||||
// The key is copied so caller mutation cannot change the admitted signer.
|
||||
func (e *Engine) SetEnvelopeSigningKey(key ed25519.PrivateKey, kid string) error {
|
||||
if len(key) != ed25519.PrivateKeySize || strings.TrimSpace(kid) == "" {
|
||||
return fmt.Errorf("valid Ed25519 private key and kid are required")
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.signingKey = append(ed25519.PrivateKey(nil), key...)
|
||||
e.signingKID = kid
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDecisionLog attaches a local decision recorder to the engine.
|
||||
func (e *Engine) SetDecisionLog(log DecisionRecorder) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.log = log
|
||||
}
|
||||
|
||||
// SetClock overrides the engine clock. Tests use this to pin allow lifetimes.
|
||||
func (e *Engine) SetClock(clock func() time.Time) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.clock = clock
|
||||
}
|
||||
|
||||
func (e *Engine) now() time.Time {
|
||||
e.mu.RLock()
|
||||
clock := e.clock
|
||||
e.mu.RUnlock()
|
||||
if clock != nil {
|
||||
return clock().UTC()
|
||||
}
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
// decide evaluates one request without recording it.
|
||||
func (e *Engine) decide(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) {
|
||||
normalized, facts := e.normalizeRequest(request)
|
||||
|
||||
expectation, err := e.policy.Evaluate(ctx, normalized)
|
||||
if err != nil {
|
||||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
|
||||
envelope := e.envelope(ctx, normalized, request, expectation, facts)
|
||||
e.mu.RLock()
|
||||
key, kid := e.signingKey, e.signingKID
|
||||
e.mu.RUnlock()
|
||||
if len(key) != 0 {
|
||||
if err := sign.Sign(&envelope, key, kid); err != nil {
|
||||
return api.DecisionEnvelope{}, fmt.Errorf("decision signing failed: %w", err)
|
||||
}
|
||||
}
|
||||
return envelope, nil
|
||||
}
|
||||
|
||||
// IsServicePilot selects the existing exact source package, never a request flag.
|
||||
func (e *Engine) IsServicePilot() bool {
|
||||
return e.policy.Metadata.ID == taskexecution.ServicePilotPackageID && e.policy.Metadata.Version == taskexecution.ServicePilotPackageVersion
|
||||
}
|
||||
func (e *Engine) IsTrustedServicePilot() bool {
|
||||
return e.policy.Metadata.ID == taskexecution.ServicePilotPackageID && e.policy.Metadata.Version == taskexecution.TrustedServicePilotPackageVersion
|
||||
}
|
||||
func (e *Engine) IsTaskPin() bool { return e.IsServicePilot() || e.IsTrustedServicePilot() }
|
||||
|
||||
// ErrServicePilotUnavailable withholds any result that is not this B1 slice's
|
||||
// signed DENY. Provider joins and executable eligibility remain unimplemented.
|
||||
var ErrServicePilotUnavailable = errors.New("service pilot source path unavailable")
|
||||
|
||||
// CheckServicePilot records only a signed DENY after authenticating the exact
|
||||
// immutable service input. It does not validate Approval facts or grant an act.
|
||||
func (e *Engine) CheckServicePilot(ctx context.Context, input taskexecution.ServiceInput) (api.DecisionEnvelope, error) {
|
||||
if !e.IsServicePilot() {
|
||||
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
|
||||
}
|
||||
request, err := input.RequestAt(e.now())
|
||||
if err != nil {
|
||||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
envelope, err := e.decide(callerauth.WithRecord(ctx, input.Caller()), request)
|
||||
if err != nil {
|
||||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
if envelope.Effect != api.DecisionEffectDeny || envelope.Signature == nil || envelope.Signature.Mode != api.EnvelopeSignatureSigned {
|
||||
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
|
||||
}
|
||||
if err := e.recordDecisions([]api.DecisionEnvelope{envelope}); err != nil {
|
||||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
return envelope, nil
|
||||
}
|
||||
|
||||
// Check evaluates one subject/action/resource request. The decision is
|
||||
// released only after its required record commits (GH-DEC-2026-024).
|
||||
func (e *Engine) Check(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) {
|
||||
if e.IsTaskPin() || request.Action == "execute-ccr" {
|
||||
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
|
||||
}
|
||||
decision, err := e.decide(ctx, request)
|
||||
if err != nil {
|
||||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
if err := e.recordDecisions([]api.DecisionEnvelope{decision}); err != nil {
|
||||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
return decision, nil
|
||||
}
|
||||
|
||||
// BatchCheck evaluates one subject/action/context tuple against resources in
|
||||
// request order, and commits the whole batch in one durable write.
|
||||
func (e *Engine) BatchCheck(ctx context.Context, request api.BatchCheckRequest) ([]api.DecisionEnvelope, error) {
|
||||
if e.IsTaskPin() || request.Action == "execute-ccr" {
|
||||
return nil, ErrServicePilotUnavailable
|
||||
}
|
||||
decisions := make([]api.DecisionEnvelope, 0, len(request.Resources))
|
||||
for _, resource := range request.Resources {
|
||||
decision, err := e.decide(ctx, api.CheckRequest{
|
||||
ID: request.ID,
|
||||
Tenant: request.Tenant,
|
||||
Subject: request.Subject,
|
||||
Action: request.Action,
|
||||
Resource: resource,
|
||||
Context: request.Context,
|
||||
PolicyVersion: request.PolicyVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decisions = append(decisions, decision)
|
||||
}
|
||||
if err := e.recordDecisions(decisions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decisions, nil
|
||||
}
|
||||
|
||||
// ListAllowed evaluates candidate resources and returns only allow decisions.
|
||||
func (e *Engine) ListAllowed(ctx context.Context, request ListAllowedRequest) ([]api.DecisionEnvelope, error) {
|
||||
if e.IsTaskPin() || request.Action == "execute-ccr" {
|
||||
return nil, ErrServicePilotUnavailable
|
||||
}
|
||||
candidates := e.store.ResourceRefs(request.System, request.ResourceType)
|
||||
decisions := make([]api.DecisionEnvelope, 0, len(candidates))
|
||||
for _, resource := range candidates {
|
||||
if !resourceMatchesFilters(resource, request.Filters) {
|
||||
continue
|
||||
}
|
||||
decision, err := e.decide(ctx, api.CheckRequest{
|
||||
Subject: request.Subject,
|
||||
Action: request.Action,
|
||||
Resource: resource,
|
||||
Context: request.Context,
|
||||
PolicyVersion: request.PolicyVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decisions = append(decisions, decision)
|
||||
}
|
||||
if err := e.recordDecisions(decisions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allowed := make([]api.DecisionEnvelope, 0, len(decisions))
|
||||
for _, decision := range decisions {
|
||||
if decision.Effect == api.DecisionEffectAllow {
|
||||
allowed = append(allowed, decision)
|
||||
}
|
||||
}
|
||||
return allowed, nil
|
||||
}
|
||||
|
||||
// Explain returns a CARING-aware explanation for a decision recorded by this
|
||||
// engine instance. P2.6 replaces this in-memory history with the local log.
|
||||
func (e *Engine) Explain(decisionID string) (Explanation, error) {
|
||||
e.mu.RLock()
|
||||
decision, ok := e.history[decisionID]
|
||||
e.mu.RUnlock()
|
||||
if !ok {
|
||||
return Explanation{}, fmt.Errorf("decision %q not found", decisionID)
|
||||
}
|
||||
|
||||
return ExplainEnvelope(decision), nil
|
||||
}
|
||||
|
||||
// ExplainEnvelope returns the same explanation shape for an already-loaded
|
||||
// decision envelope.
|
||||
func ExplainEnvelope(decision api.DecisionEnvelope) Explanation {
|
||||
return Explanation{
|
||||
DecisionID: decision.ID,
|
||||
Effect: decision.Effect,
|
||||
Reason: decision.Reason,
|
||||
Summary: explanationSummary(decision),
|
||||
Subject: decision.Subject,
|
||||
Resource: decision.Resource,
|
||||
PolicyPackage: decision.Provenance.PolicyPackage,
|
||||
PolicyVersion: decision.Provenance.PolicyVersion,
|
||||
MatchedRule: decision.MatchedRule,
|
||||
Diagnostics: decision.Diagnostics,
|
||||
Caring: decision.Caring,
|
||||
}
|
||||
}
|
||||
|
||||
type registryFacts struct {
|
||||
subjectFound bool
|
||||
resourceFound bool
|
||||
subject api.Subject
|
||||
resource api.Resource
|
||||
matchedRelationship string
|
||||
descriptor *api.CaringAccessDescriptor
|
||||
// overriddenByRegistry names request attribute keys whose caller-supplied
|
||||
// value was displaced by a registry fact. Empty on an honest request; a
|
||||
// non-empty list means the caller asserted something the registry contradicts,
|
||||
// which is worth seeing even though the registry won (FLEX-DEC-2026-012).
|
||||
overriddenByRegistry []string
|
||||
}
|
||||
|
||||
func (e *Engine) normalizeRequest(request api.CheckRequest) (api.CheckRequest, registryFacts) {
|
||||
normalized := request
|
||||
facts := registryFacts{}
|
||||
|
||||
overridden := []string{}
|
||||
|
||||
if normalized.Tenant != "" {
|
||||
if normalized.Subject.Tenant == "" {
|
||||
normalized.Subject.Tenant = normalized.Tenant
|
||||
}
|
||||
if normalized.Resource.Tenant == "" {
|
||||
normalized.Resource.Tenant = normalized.Tenant
|
||||
}
|
||||
}
|
||||
|
||||
if subject, ok := e.store.Subject(request.Subject.ID); ok {
|
||||
facts.subjectFound = true
|
||||
facts.subject = subject
|
||||
normalized.Subject = enrichSubjectRef(request.Subject, subject, &overridden)
|
||||
}
|
||||
|
||||
if resource, ok := e.store.Resource(request.Resource.System, request.Resource.ID); ok {
|
||||
facts.resourceFound = true
|
||||
facts.resource = resource
|
||||
normalized.Resource = enrichResourceRef(request.Resource, resource, &overridden)
|
||||
}
|
||||
|
||||
if normalized.CaringContext != nil {
|
||||
descriptor := *normalized.CaringContext
|
||||
facts.descriptor = &descriptor
|
||||
facts.overriddenByRegistry = overridden
|
||||
return normalized, facts
|
||||
}
|
||||
|
||||
facts.overriddenByRegistry = overridden
|
||||
|
||||
if descriptor, relationshipID := e.matchCaringDescriptor(normalized, facts); descriptor != nil {
|
||||
facts.descriptor = descriptor
|
||||
facts.matchedRelationship = relationshipID
|
||||
normalized.CaringContext = descriptor
|
||||
} else if facts.resourceFound && facts.resource.Caring != nil {
|
||||
descriptor := *facts.resource.Caring
|
||||
facts.descriptor = &descriptor
|
||||
normalized.CaringContext = &descriptor
|
||||
}
|
||||
|
||||
return normalized, facts
|
||||
}
|
||||
|
||||
func (e *Engine) matchCaringDescriptor(request api.CheckRequest, facts registryFacts) (*api.CaringAccessDescriptor, string) {
|
||||
candidates := []string{request.Subject.ID}
|
||||
if facts.subjectFound {
|
||||
candidates = append(candidates, facts.subject.Groups...)
|
||||
}
|
||||
|
||||
for _, relationship := range e.store.RelationshipsForObject(request.Resource.ID) {
|
||||
if relationship.Caring == nil {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(candidates, relationship.Subject) {
|
||||
continue
|
||||
}
|
||||
if relationship.System != "" && relationship.System != request.Resource.System {
|
||||
continue
|
||||
}
|
||||
if relationship.Tenant != "" && request.Resource.Tenant != "" && relationship.Tenant != request.Resource.Tenant {
|
||||
continue
|
||||
}
|
||||
descriptor := *relationship.Caring
|
||||
return &descriptor, relationship.ID
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// enrichSubjectRef overlays registry facts onto the subject the caller sent.
|
||||
//
|
||||
// Registry facts WIN. A value in the registry is an authority statement; the
|
||||
// same key on the request is the caller's proposal about itself. Letting the
|
||||
// proposal survive made a registry ceiling advisory — see FLEX-DEC-2026-012,
|
||||
// where a caller supplying resource.attributes.allowed_subjects authorized a
|
||||
// subject the registry did not know.
|
||||
//
|
||||
// Keys the registry does not define still pass through from the request. That
|
||||
// residual is stated in docs/request-enrichment.md rather than fixed here: a
|
||||
// policy reading a key its manifest omits is consuming caller input, and the
|
||||
// structural fix is separating the two namespaces (FLEX-WP-0025).
|
||||
func enrichSubjectRef(ref api.SubjectRef, subject api.Subject, overridden *[]string) api.SubjectRef {
|
||||
out := ref
|
||||
// subject.Type is deliberately NOT overwritten, unlike every other registry
|
||||
// fact here. The registry's Type is CARING vocabulary (Human, Agent,
|
||||
// Automation, Service); the request's is the protected system's actor
|
||||
// vocabulary (service, adm, agt, atm). They are two different fields sharing
|
||||
// a name, and substituting one for the other is translation rather than
|
||||
// identity — the error GH-DEC-2026-008 ruled against. Making the registry win
|
||||
// here denied every secrets-engine allow, because "Service" is not "service".
|
||||
//
|
||||
// The residual is that a caller can still assert its own subject.type and
|
||||
// packages branch on it. That is not fixable by overwriting from a different
|
||||
// vocabulary; it needs the two fields separated. FLEX-WP-0025.
|
||||
if out.Type == "" {
|
||||
out.Type = subject.Type
|
||||
}
|
||||
if subject.Tenant != "" {
|
||||
out.Tenant = subject.Tenant
|
||||
}
|
||||
out.Attributes = copyMap(out.Attributes)
|
||||
setAuthoritative(out.Attributes, "display_name", subject.DisplayName, "subject", overridden)
|
||||
setAuthoritative(out.Attributes, "organization_relation", subject.OrganizationRelation, "subject", overridden)
|
||||
setAuthoritative(out.Attributes, "roles", subject.Roles, "subject", overridden)
|
||||
setAuthoritative(out.Attributes, "groups", subject.Groups, "subject", overridden)
|
||||
setAuthoritatives(out.Attributes, subject.Claims, "subject", overridden)
|
||||
setAuthoritatives(out.Attributes, subject.Metadata, "subject", overridden)
|
||||
return out
|
||||
}
|
||||
|
||||
// enrichResourceRef overlays registry facts onto the resource the caller sent.
|
||||
// Registry facts win, for the reason given on enrichSubjectRef: ops-warden's
|
||||
// max_ttl_hours, allowed_subjects, and allowed_principals are ceilings and
|
||||
// allowlists, and a ceiling a caller can raise is not a ceiling.
|
||||
func enrichResourceRef(ref api.ResourceRef, resource api.Resource, overridden *[]string) api.ResourceRef {
|
||||
out := ref
|
||||
if resource.Type != "" {
|
||||
out.Type = resource.Type
|
||||
}
|
||||
out.Attributes = copyMap(out.Attributes)
|
||||
setAuthoritative(out.Attributes, "path", resource.Path, "resource", overridden)
|
||||
setAuthoritative(out.Attributes, "parent", resource.Parent, "resource", overridden)
|
||||
setAuthoritative(out.Attributes, "labels", resource.Labels, "resource", overridden)
|
||||
setAuthoritative(out.Attributes, "trust_zone", resource.TrustZone, "resource", overridden)
|
||||
setAuthoritative(out.Attributes, "owner", resource.Owner, "resource", overridden)
|
||||
setAuthoritatives(out.Attributes, resource.Attributes, "resource", overridden)
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *Engine) envelope(ctx context.Context, request, submitted api.CheckRequest, expectation api.DecisionExpectation, facts registryFacts) api.DecisionEnvelope {
|
||||
envelope := api.DecisionEnvelope{
|
||||
RequestID: request.ID,
|
||||
Effect: expectation.Effect,
|
||||
Reason: expectation.Reason,
|
||||
MatchedPolicyVersion: e.policy.Metadata.Version,
|
||||
MatchedRule: expectation.Reason,
|
||||
Resource: request.Resource,
|
||||
Subject: request.Subject,
|
||||
Binding: api.NewDecisionBindingFor(request, submitted),
|
||||
Obligations: expectation.Obligations,
|
||||
Diagnostics: map[string]any{
|
||||
"action": request.Action,
|
||||
"policy_package": e.policy.Metadata.ID,
|
||||
"policy_status": e.policy.Metadata.Status,
|
||||
"registry_subject": facts.subjectFound,
|
||||
"registry_resource": facts.resourceFound,
|
||||
"matched_relationship": facts.matchedRelationship,
|
||||
"registry_overrode": facts.overriddenByRegistry,
|
||||
},
|
||||
Provenance: api.DecisionProvenance{
|
||||
Evaluator: "flex-auth/local",
|
||||
Mode: "standalone",
|
||||
PolicyPackage: e.policy.Metadata.ID,
|
||||
PolicyVersion: e.policy.Metadata.Version,
|
||||
PolicyPackageDigest: e.policy.Digest(),
|
||||
RegistrySnapshotDigest: e.store.Digest(),
|
||||
Caller: callerProvenance(ctx),
|
||||
},
|
||||
Caring: e.caringDecisionMetadata(facts.descriptor, expectation.ConformanceFindings),
|
||||
}
|
||||
api.CompleteDecision(&envelope, request, api.DecisionCompletion{
|
||||
AllowTTL: e.policy.Metadata.AllowTTL,
|
||||
Now: e.now(),
|
||||
})
|
||||
envelope.ID = decisionID(e.policy.Metadata, request, envelope)
|
||||
if envelope.Signature == nil {
|
||||
envelope.Signature = api.UnsignedEnvelopeSignature()
|
||||
}
|
||||
return envelope
|
||||
}
|
||||
|
||||
func callerProvenance(ctx context.Context) *api.CallerProvenance {
|
||||
record, ok := callerauth.RecordFromContext(ctx)
|
||||
if !ok {
|
||||
record = callerauth.Record{Mode: callerauth.ModeDisabled}
|
||||
}
|
||||
out := &api.CallerProvenance{Mode: string(record.Mode)}
|
||||
if record.Principal != "" {
|
||||
out.Principal = record.Principal
|
||||
out.Audience = record.Audience
|
||||
if !record.NotAfter.IsZero() {
|
||||
out.NotAfter = record.NotAfter.UTC().Format(time.RFC3339)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// recordDecisions releases no effect until its required record commits.
|
||||
// GH-DEC-2026-024 refuses the former restrictive-effect failure exception.
|
||||
// Failed decisions must not enter Explain's history as if they were released.
|
||||
func (e *Engine) recordDecisions(decisions []api.DecisionEnvelope) error {
|
||||
e.mu.RLock()
|
||||
log := e.log
|
||||
e.mu.RUnlock()
|
||||
if log != nil && len(decisions) != 0 {
|
||||
if err := appendAll(log, decisions); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrRecordNotCommitted, err)
|
||||
}
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
for _, decision := range decisions {
|
||||
e.history[decision.ID] = decision
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendAll(log DecisionRecorder, decisions []api.DecisionEnvelope) error {
|
||||
if batch, ok := log.(BatchRecorder); ok {
|
||||
return batch.AppendBatch(decisions)
|
||||
}
|
||||
for _, decision := range decisions {
|
||||
if err := log.Append(decision); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) caringDecisionMetadata(descriptor *api.CaringAccessDescriptor, findings []api.CaringConformanceFinding) *api.CaringDecisionMetadata {
|
||||
profile := e.policy.Metadata.Caring.Profile
|
||||
if descriptor != nil && descriptor.Profile != "" {
|
||||
profile = descriptor.Profile
|
||||
}
|
||||
|
||||
metadata := &api.CaringDecisionMetadata{
|
||||
Profile: profile,
|
||||
ConformanceFindings: append([]api.CaringConformanceFinding(nil), findings...),
|
||||
}
|
||||
if descriptor == nil {
|
||||
metadata.ConformanceFindings = append(metadata.ConformanceFindings, api.CaringConformanceFinding{
|
||||
Code: "CARING-DESCRIPTOR-MISSING",
|
||||
Severity: "warning",
|
||||
Message: "no CARING descriptor matched the request",
|
||||
Fields: []string{"caring_context"},
|
||||
})
|
||||
return metadata
|
||||
}
|
||||
|
||||
descriptorCopy := *descriptor
|
||||
metadata.Descriptor = &descriptorCopy
|
||||
metadata.RestrictionsEvaluated = append([]api.Restriction(nil), descriptor.Restrictions...)
|
||||
metadata.ExposureModes = append([]api.ExposureMode(nil), descriptor.ExposureModes...)
|
||||
metadata.DerivedCapabilities = append([]api.CaringDerivedCapability(nil), descriptor.DerivedCapabilities...)
|
||||
return metadata
|
||||
}
|
||||
|
||||
func decisionID(metadata api.PolicyPackageMetadata, request api.CheckRequest, envelope api.DecisionEnvelope) string {
|
||||
data, _ := json.Marshal(struct {
|
||||
PolicyID string `json:"policy_id"`
|
||||
PolicyVersion string `json:"policy_version"`
|
||||
Request api.CheckRequest `json:"request"`
|
||||
Effect api.DecisionEffect `json:"effect"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}{
|
||||
PolicyID: metadata.ID,
|
||||
PolicyVersion: metadata.Version,
|
||||
Request: request,
|
||||
Effect: envelope.Effect,
|
||||
Reason: envelope.Reason,
|
||||
})
|
||||
sum := sha256.Sum256(data)
|
||||
return "decision:" + hex.EncodeToString(sum[:8])
|
||||
}
|
||||
|
||||
func copyMap(in map[string]any) map[string]any {
|
||||
out := make(map[string]any, len(in))
|
||||
for key, value := range in {
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// setAuthoritative writes a registry fact over whatever the request carried for
|
||||
// that key, and records the key when a caller value was displaced so the
|
||||
// override is visible in diagnostics rather than silent.
|
||||
func setAuthoritative(target map[string]any, key string, value any, scope string, overridden *[]string) {
|
||||
if isEmptyAttribute(value) {
|
||||
return
|
||||
}
|
||||
if existing, exists := target[key]; exists && overridden != nil && !equalAttribute(existing, value) {
|
||||
*overridden = append(*overridden, scope+"."+key)
|
||||
}
|
||||
target[key] = value
|
||||
}
|
||||
|
||||
func setAuthoritatives(target map[string]any, attrs map[string]any, scope string, overridden *[]string) {
|
||||
for _, key := range sortedKeys(attrs) {
|
||||
setAuthoritative(target, key, attrs[key], scope, overridden)
|
||||
}
|
||||
}
|
||||
|
||||
func equalAttribute(a, b any) bool {
|
||||
left, errLeft := json.Marshal(a)
|
||||
right, errRight := json.Marshal(b)
|
||||
if errLeft != nil || errRight != nil {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(left, right)
|
||||
}
|
||||
|
||||
func sortedKeys(attrs map[string]any) []string {
|
||||
keys := make([]string, 0, len(attrs))
|
||||
for key := range attrs {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func addAttributes(target map[string]any, attrs map[string]any) {
|
||||
for key, value := range attrs {
|
||||
addAttribute(target, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func addAttribute(target map[string]any, key string, value any) {
|
||||
if isEmptyAttribute(value) {
|
||||
return
|
||||
}
|
||||
if _, exists := target[key]; !exists {
|
||||
target[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
func resourceMatchesFilters(resource api.ResourceRef, filters map[string]any) bool {
|
||||
for key, want := range filters {
|
||||
var got any
|
||||
switch key {
|
||||
case "id":
|
||||
got = resource.ID
|
||||
case "type", "resource_type":
|
||||
got = resource.Type
|
||||
case "system":
|
||||
got = resource.System
|
||||
case "tenant":
|
||||
got = resource.Tenant
|
||||
default:
|
||||
got = resource.Attributes[key]
|
||||
}
|
||||
if !valuesEqual(got, want) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func valuesEqual(got, want any) bool {
|
||||
if reflect.DeepEqual(got, want) {
|
||||
return true
|
||||
}
|
||||
return fmt.Sprint(got) == fmt.Sprint(want)
|
||||
}
|
||||
|
||||
func explanationSummary(decision api.DecisionEnvelope) string {
|
||||
action, _ := decision.Diagnostics["action"].(string)
|
||||
actor := decision.Subject.ID
|
||||
capability := action
|
||||
plane := ""
|
||||
if decision.Caring != nil && decision.Caring.Descriptor != nil {
|
||||
descriptor := decision.Caring.Descriptor
|
||||
if descriptor.OrganizationRelation != "" || descriptor.CanonicalRole != "" {
|
||||
actor = strings.TrimSpace(fmt.Sprintf("%s %s", descriptor.OrganizationRelation, descriptor.CanonicalRole))
|
||||
}
|
||||
if len(descriptor.Capabilities) > 0 {
|
||||
capability = string(descriptor.Capabilities[0])
|
||||
}
|
||||
if len(descriptor.Planes) > 0 {
|
||||
plane = string(descriptor.Planes[0]) + " Plane "
|
||||
}
|
||||
}
|
||||
if capability == "" {
|
||||
capability = "access"
|
||||
}
|
||||
|
||||
verb := "may"
|
||||
switch decision.Effect {
|
||||
case api.DecisionEffectDeny:
|
||||
verb = "may not"
|
||||
case api.DecisionEffectRedact:
|
||||
verb = "receives redacted"
|
||||
case api.DecisionEffectAuditOnly:
|
||||
verb = "is audit-only for"
|
||||
case api.DecisionEffectNotApplicable:
|
||||
verb = "has no applicable policy for"
|
||||
}
|
||||
|
||||
reason := decision.Reason
|
||||
if reason == "" {
|
||||
reason = string(decision.Effect)
|
||||
}
|
||||
return fmt.Sprintf("%s %s %s %sresource %s because %s.", actor, verb, capability, plane, decision.Resource.ID, reason)
|
||||
}
|
||||
|
||||
func isEmptyAttribute(value any) bool {
|
||||
switch typed := value.(type) {
|
||||
case string:
|
||||
return typed == ""
|
||||
case api.OrganizationRelation:
|
||||
return typed == ""
|
||||
case []api.CanonicalRole:
|
||||
return len(typed) == 0
|
||||
case []string:
|
||||
return len(typed) == 0
|
||||
default:
|
||||
return value == nil
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,372 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/services/auth/source/oauth2"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
var (
|
||||
oauthCLIFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
Usage: "Application Name",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "provider",
|
||||
Value: "",
|
||||
Usage: "OAuth2 Provider",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "key",
|
||||
Value: "",
|
||||
Usage: "Client ID (Key)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "secret",
|
||||
Value: "",
|
||||
Usage: "Client Secret",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "secret-file",
|
||||
Usage: "Read a 64-byte base64url client secret from an admitted read-only regular file",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "auto-discover-url",
|
||||
Value: "",
|
||||
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "use-custom-urls",
|
||||
Value: "false",
|
||||
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-tenant-id",
|
||||
Value: "",
|
||||
Usage: "Use custom Tenant ID for OAuth endpoints",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-auth-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-token-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-profile-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "custom-email-url",
|
||||
Value: "",
|
||||
Usage: "Use a custom Email URL (option for GitHub)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "icon-url",
|
||||
Value: "",
|
||||
Usage: "Custom icon URL for OAuth2 login source",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "scopes",
|
||||
Value: nil,
|
||||
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "required-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name that has to be set to allow users to login with this source",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "required-claim-value",
|
||||
Value: "",
|
||||
Usage: "Claim value that has to be set to allow users to login with this source",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "required-claim-values",
|
||||
Usage: "Require any one exact array value from current OpenID Connect UserInfo (cannot combine with required-claim-value)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "group-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name providing group names for this source",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "admin-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for administrator users",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "restricted-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for restricted users",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "group-team-map",
|
||||
Value: "",
|
||||
Usage: "JSON mapping between groups and org teams",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "group-team-map-removal",
|
||||
Usage: "Activate automatic team membership removal depending on groups",
|
||||
},
|
||||
}
|
||||
|
||||
microcmdAuthAddOauth = &cli.Command{
|
||||
Name: "add-oauth",
|
||||
Usage: "Add new Oauth authentication source",
|
||||
Action: runAddOauth,
|
||||
Flags: oauthCLIFlags,
|
||||
}
|
||||
|
||||
microcmdAuthUpdateOauth = &cli.Command{
|
||||
Name: "update-oauth",
|
||||
Usage: "Update existing Oauth authentication source",
|
||||
Action: runUpdateOauth,
|
||||
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
|
||||
}
|
||||
)
|
||||
|
||||
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||
var customURLMapping *oauth2.CustomURLMapping
|
||||
if c.IsSet("use-custom-urls") {
|
||||
customURLMapping = &oauth2.CustomURLMapping{
|
||||
TokenURL: c.String("custom-token-url"),
|
||||
AuthURL: c.String("custom-auth-url"),
|
||||
ProfileURL: c.String("custom-profile-url"),
|
||||
EmailURL: c.String("custom-email-url"),
|
||||
Tenant: c.String("custom-tenant-id"),
|
||||
}
|
||||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
return &oauth2.Source{
|
||||
Provider: c.String("provider"),
|
||||
ClientID: c.String("key"),
|
||||
ClientSecret: c.String("secret"),
|
||||
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
||||
CustomURLMapping: customURLMapping,
|
||||
IconURL: c.String("icon-url"),
|
||||
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
|
||||
Scopes: c.StringSlice("scopes"),
|
||||
RequiredClaimName: c.String("required-claim-name"),
|
||||
RequiredClaimValue: c.String("required-claim-value"),
|
||||
RequiredClaimValues: c.StringSlice("required-claim-values"),
|
||||
GroupClaimName: c.String("group-claim-name"),
|
||||
AdminGroup: c.String("admin-group"),
|
||||
RestrictedGroup: c.String("restricted-group"),
|
||||
GroupTeamMap: c.String("group-team-map"),
|
||||
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
|
||||
}
|
||||
}
|
||||
|
||||
func runAddOauth(c *cli.Context) error {
|
||||
secret, selected, err := oauthSecretInput(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
ctx = db.WithSensitiveSQL(ctx)
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
engine, ok := db.GetEngine(ctx).(*xorm.Engine)
|
||||
if !ok || setting.Database.LogSQL || engine.Logger().IsShowSQL() {
|
||||
return errors.New("OAuth configuration requires SQL logging disabled")
|
||||
}
|
||||
|
||||
config := parseOAuth2Config(c)
|
||||
if selected {
|
||||
config.ClientSecret = secret
|
||||
}
|
||||
if err := oauth2.ValidateRequiredClaimValues(config.RequiredClaimName, config.RequiredClaimValue, config.RequiredClaimValues); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.RequiredClaimValues != nil && config.Provider != "openidConnect" {
|
||||
return errors.New("required-claim-values requires openidConnect")
|
||||
}
|
||||
if config.Provider == "openidConnect" {
|
||||
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
|
||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
|
||||
}
|
||||
}
|
||||
|
||||
err = auth_model.CreateSource(ctx, &auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActive: true,
|
||||
Cfg: config,
|
||||
})
|
||||
if err != nil && c.IsSet("secret-file") {
|
||||
return errors.New("OAuth source creation failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func runUpdateOauth(c *cli.Context) error {
|
||||
secret, selected, err := oauthSecretInput(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !c.IsSet("id") {
|
||||
return errors.New("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
ctx = db.WithSensitiveSQL(ctx)
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
engine, ok := db.GetEngine(ctx).(*xorm.Engine)
|
||||
if !ok || setting.Database.LogSQL || engine.Logger().IsShowSQL() {
|
||||
return errors.New("OAuth configuration requires SQL logging disabled")
|
||||
}
|
||||
|
||||
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oAuth2Config, ok := source.Cfg.(*oauth2.Source)
|
||||
if !ok {
|
||||
return errors.New("authentication source is not OAuth2")
|
||||
}
|
||||
|
||||
if c.IsSet("name") {
|
||||
source.Name = c.String("name")
|
||||
}
|
||||
|
||||
if c.IsSet("provider") {
|
||||
oAuth2Config.Provider = c.String("provider")
|
||||
}
|
||||
|
||||
if c.IsSet("key") {
|
||||
oAuth2Config.ClientID = c.String("key")
|
||||
}
|
||||
|
||||
if selected {
|
||||
oAuth2Config.ClientSecret = secret
|
||||
}
|
||||
|
||||
if c.IsSet("auto-discover-url") {
|
||||
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
|
||||
}
|
||||
|
||||
if c.IsSet("icon-url") {
|
||||
oAuth2Config.IconURL = c.String("icon-url")
|
||||
}
|
||||
|
||||
if c.IsSet("scopes") {
|
||||
oAuth2Config.Scopes = c.StringSlice("scopes")
|
||||
}
|
||||
|
||||
if c.IsSet("required-claim-name") {
|
||||
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
|
||||
}
|
||||
if c.IsSet("required-claim-value") {
|
||||
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
|
||||
}
|
||||
|
||||
if c.IsSet("required-claim-values") {
|
||||
oAuth2Config.RequiredClaimValues = c.StringSlice("required-claim-values")
|
||||
}
|
||||
|
||||
if c.IsSet("group-claim-name") {
|
||||
oAuth2Config.GroupClaimName = c.String("group-claim-name")
|
||||
}
|
||||
if c.IsSet("admin-group") {
|
||||
oAuth2Config.AdminGroup = c.String("admin-group")
|
||||
}
|
||||
if c.IsSet("restricted-group") {
|
||||
oAuth2Config.RestrictedGroup = c.String("restricted-group")
|
||||
}
|
||||
if c.IsSet("group-team-map") {
|
||||
oAuth2Config.GroupTeamMap = c.String("group-team-map")
|
||||
}
|
||||
if c.IsSet("group-team-map-removal") {
|
||||
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
|
||||
}
|
||||
|
||||
// update custom URL mapping
|
||||
customURLMapping := &oauth2.CustomURLMapping{}
|
||||
|
||||
if oAuth2Config.CustomURLMapping != nil {
|
||||
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
|
||||
customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
|
||||
customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
|
||||
customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
|
||||
customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
|
||||
}
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
|
||||
customURLMapping.TokenURL = c.String("custom-token-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
|
||||
customURLMapping.AuthURL = c.String("custom-auth-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
|
||||
customURLMapping.ProfileURL = c.String("custom-profile-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
|
||||
customURLMapping.EmailURL = c.String("custom-email-url")
|
||||
}
|
||||
|
||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
|
||||
customURLMapping.Tenant = c.String("custom-tenant-id")
|
||||
}
|
||||
|
||||
oAuth2Config.CustomURLMapping = customURLMapping
|
||||
if err := oauth2.ValidateRequiredClaimValues(oAuth2Config.RequiredClaimName, oAuth2Config.RequiredClaimValue, oAuth2Config.RequiredClaimValues); err != nil {
|
||||
return err
|
||||
}
|
||||
if oAuth2Config.RequiredClaimValues != nil && oAuth2Config.Provider != "openidConnect" {
|
||||
return errors.New("required-claim-values requires openidConnect")
|
||||
}
|
||||
source.Cfg = oAuth2Config
|
||||
|
||||
err = auth_model.UpdateSource(ctx, source)
|
||||
if err != nil && c.IsSet("secret-file") {
|
||||
return errors.New("OAuth source update failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve before opening the DB or mutating a loaded source. Never store the
|
||||
// resolved bytes in the CLI context: they must not become flag/argv diagnostics.
|
||||
func oauthSecretInput(c *cli.Context) (string, bool, error) {
|
||||
if c.IsSet("secret-file") {
|
||||
if c.IsSet("secret") {
|
||||
return "", false, errors.New("secret and secret-file are mutually exclusive")
|
||||
}
|
||||
value, err := readOAuthSecretFile(c.String("secret-file"))
|
||||
return value, true, err
|
||||
}
|
||||
return c.String("secret"), c.IsSet("secret"), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
module forgejo.org
|
||||
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
code.forgejo.org/f3/gof3/v3 v3.10.6
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
code.forgejo.org/forgejo/levelqueue v1.0.0
|
||||
code.forgejo.org/forgejo/reply v1.0.2
|
||||
code.forgejo.org/go-chi/binding v1.0.0
|
||||
code.forgejo.org/go-chi/cache v1.0.0
|
||||
code.forgejo.org/go-chi/captcha v1.0.1
|
||||
code.forgejo.org/go-chi/session v1.0.1
|
||||
code.gitea.io/actions-proto-go v0.4.0
|
||||
code.gitea.io/sdk/gitea v0.20.0
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
connectrpc.com/connect v1.17.0
|
||||
github.com/42wim/httpsig v1.2.2
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/ProtonMail/go-crypto v1.1.6
|
||||
github.com/PuerkitoBio/goquery v1.10.2
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
||||
github.com/alecthomas/chroma/v2 v2.15.0
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.5.2
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||
github.com/caddyserver/certmagic v0.22.2
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
github.com/djherbis/nio/v3 v3.0.1
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
|
||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
||||
github.com/go-chi/chi/v5 v5.2.0
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.2
|
||||
github.com/go-git/go-git/v5 v5.13.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-openapi/spec v0.20.14
|
||||
github.com/go-sql-driver/mysql v1.9.1
|
||||
github.com/go-webauthn/webauthn v0.12.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/go-github/v64 v64.0.0
|
||||
github.com/google/pprof v0.0.0-20241017200806-017d972448fc
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||
github.com/jhillyerd/enmime/v2 v2.1.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.17.11
|
||||
github.com/klauspost/cpuid/v2 v2.2.10
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.80.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.28
|
||||
github.com/meilisearch/meilisearch-go v0.31.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/minio/minio-go/v7 v7.0.88
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/nektos/act v0.2.52
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/redis/go-redis/v9 v9.7.3
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.12
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
github.com/valyala/fastjson v1.6.4
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go v0.126.0
|
||||
go.uber.org/mock v0.5.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/image v0.25.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.12.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/text v0.23.0
|
||||
google.golang.org/protobuf v1.36.4
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/xurls/v2 v2.5.0
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.22.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
|
||||
github.com/blevesearch/geo v0.2.3 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.25 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
github.com/blevesearch/vellum v1.1.0 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.22.7 // indirect
|
||||
github.com/go-webauthn/x v0.1.20 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/go-tpm v0.9.3 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/libdns/libdns v0.2.3 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/markbates/going v1.0.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.1 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rhysd/actionlint v1.6.27 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
github.com/zeebo/assert v1.3.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
||||
|
||||
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.25.1
|
||||
|
||||
replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1
|
||||
|
||||
replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616
|
||||
|
||||
replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package db
|
||||
|
||||
import "context"
|
||||
|
||||
type sensitiveSQLKey struct{}
|
||||
|
||||
// WithSensitiveSQL suppresses argument, statement and driver-error disclosure
|
||||
// in the application's query hooks. The caller must separately refuse raw XORM
|
||||
// SQL logging; custom driver/server auditing remains an owner custody concern.
|
||||
func WithSensitiveSQL(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, sensitiveSQLKey{}, true)
|
||||
}
|
||||
func isSensitiveSQL(ctx context.Context) bool {
|
||||
sensitive, _ := ctx.Value(sensitiveSQLKey{}).(bool)
|
||||
return sensitive
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/gothic"
|
||||
)
|
||||
|
||||
// Callout redirects request/response pair to authenticate against the provider
|
||||
func (source *Source) Callout(request *http.Request, response http.ResponseWriter, codeChallengeS256 string) error {
|
||||
// not sure if goth is thread safe (?) when using multiple providers
|
||||
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
|
||||
|
||||
var querySuffix string
|
||||
if codeChallengeS256 != "" {
|
||||
querySuffix = "&" + url.Values{
|
||||
"code_challenge_method": []string{"S256"},
|
||||
"code_challenge": []string{codeChallengeS256},
|
||||
}.Encode()
|
||||
}
|
||||
|
||||
// don't use the default gothic begin handler to prevent issues when some error occurs
|
||||
// normally the gothic library will write some custom stuff to the response instead of our own nice error page
|
||||
// gothic.BeginAuthHandler(response, request)
|
||||
|
||||
gothRWMutex.RLock()
|
||||
defer gothRWMutex.RUnlock()
|
||||
|
||||
url, err := gothic.GetAuthURL(response, request)
|
||||
if err == nil {
|
||||
// hacky way to set the code_challenge, but no better way until
|
||||
// https://github.com/markbates/goth/issues/516 is resolved
|
||||
http.Redirect(response, request, url+querySuffix, http.StatusTemporaryRedirect)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Callback handles OAuth callback, resolve to a goth user and send back to original url
|
||||
// this will trigger a new authentication request, but because we save it in the session we can use that
|
||||
func (source *Source) Callback(request *http.Request, response http.ResponseWriter, codeVerifier string) (goth.User, error) {
|
||||
// not sure if goth is thread safe (?) when using multiple providers
|
||||
request.Header.Set(ProviderHeaderKey, source.authSource.Name)
|
||||
|
||||
if codeVerifier != "" {
|
||||
// hacky way to set the code_verifier...
|
||||
// Will be picked up inside CompleteUserAuth: params := req.URL.Query()
|
||||
// https://github.com/markbates/goth/pull/474/files
|
||||
request = request.Clone(request.Context())
|
||||
q := request.URL.Query()
|
||||
q.Add("code_verifier", codeVerifier)
|
||||
request.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
gothRWMutex.RLock()
|
||||
defer gothRWMutex.RUnlock()
|
||||
|
||||
user, err := gothic.CompleteUserAuth(response, request)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
"""Reconstruct the complete pinned consumer with all reviewed source candidates."""
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
BASE = HERE.parent
|
||||
INPUTS = Path('/tmp/custodian-forgejo-successor')
|
||||
WORK = Path('/tmp/custodian-forgejo-successor-attempt2')
|
||||
WORK.mkdir(exist_ok=False)
|
||||
SOURCE = WORK / 'source'
|
||||
assert not SOURCE.exists()
|
||||
for name, pin in [('forgejo-v11.0.3.tar.gz', '1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c'),
|
||||
('go1.24.3.tar.gz', '3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8')]:
|
||||
assert hashlib.sha256((INPUTS / name).read_bytes()).hexdigest() == pin, name
|
||||
with tarfile.open(INPUTS / name) as archive:
|
||||
archive.extractall(WORK, filter='data')
|
||||
(WORK / 'forgejo').rename(SOURCE)
|
||||
# The interrupted packet's context snapshots are empty, but its frozen patch
|
||||
# and inventory retain the exact source. Recover only if both hashes match.
|
||||
operation = BASE / 'apps-operation-backend-r1'
|
||||
row = next(row for row in json.loads((operation / 'source-inventory.json').read_text())['paths'] if row['path'] == 'models/db/context.go')
|
||||
target = SOURCE / row['path']
|
||||
assert hashlib.sha256(target.read_bytes()).hexdigest() == row['before_sha256']
|
||||
original_context = target.read_bytes()
|
||||
patch = (operation / 'forgejo-operation-source.patch').read_text()
|
||||
start = patch.index('--- a/models/db/context.go\n')
|
||||
end = patch.index('--- /dev/null\n', start)
|
||||
subprocess.run(['patch', '--fuzz=0', '-p1'], input=patch[start:end], text=True, cwd=SOURCE, check=True, capture_output=True)
|
||||
assert hashlib.sha256(target.read_bytes()).hexdigest() == row['after_sha256']
|
||||
recovered = HERE / 'recovered-source/models/db/context.go'
|
||||
recovered.parent.mkdir(parents=True, exist_ok=True)
|
||||
recovered.write_bytes(target.read_bytes())
|
||||
target.write_bytes(original_context)
|
||||
applied = []
|
||||
for packet, before, after in [
|
||||
(BASE / 'forgejo-required-claim-values-r1', 'preimages', 'postimages'),
|
||||
(BASE / 'forgejo-private-input-r1', 'preimages/forgejo', 'postimages/forgejo'),
|
||||
(BASE / 'forgejo-private-input-r1', 'preimages/shared', 'postimages/shared'),
|
||||
(BASE / 'apps-operation-backend-r1', 'preimages', 'postimages'),
|
||||
(HERE, 'preimages/forgejo', 'forgejo-source'),
|
||||
]:
|
||||
for item in (packet / after).rglob('*'):
|
||||
if not item.is_file():
|
||||
continue
|
||||
name = item.relative_to(packet / after)
|
||||
target = SOURCE / name
|
||||
original = packet / before / name
|
||||
if packet == operation and str(name) == 'models/db/context.go':
|
||||
item = recovered
|
||||
assert hashlib.sha256(target.read_bytes()).hexdigest() == row['before_sha256']
|
||||
original = target
|
||||
if original.exists() and target.exists() and original.read_bytes() != target.read_bytes():
|
||||
# Shared SQL redaction was already composed from the same source.
|
||||
identical_shared = name.as_posix().startswith('models/db/') and item.read_bytes() == target.read_bytes()
|
||||
expanded_hook_test = (packet == operation and str(name) == 'models/db/sensitive_sql_test.go'
|
||||
and hashlib.sha256(target.read_bytes()).hexdigest() == 'f0f74c09a9e96a6529db7b9ce459a2ea37e204de38c5ad01855b80c9a992a769'
|
||||
and hashlib.sha256(item.read_bytes()).hexdigest() == 'de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b')
|
||||
assert identical_shared or expanded_hook_test, str(name)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(item, target)
|
||||
applied.append({'packet': packet.name, 'path': str(name), 'sha256': hashlib.sha256(item.read_bytes()).hexdigest()})
|
||||
shutil.copytree(HERE / 'access-engine-source/adapters/go/decisionpep', SOURCE / 'third_party/access-engine-pep')
|
||||
(WORK / 'home').mkdir()
|
||||
(HERE / 'consumer-reconstruction.json').write_text(json.dumps({'workspace': str(WORK), 'inputs_verified': True, 'paths': applied}, indent=2) + '\n')
|
||||
print(json.dumps({'workspace': str(WORK), 'composed_paths': len(applied)}))
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"target": "producer-all",
|
||||
"source_revision": "d328a2601095686f74376d87791bff01e2a3a2a3",
|
||||
"returncode": 1,
|
||||
"seconds": 169.79,
|
||||
"argv": [
|
||||
"bwrap",
|
||||
"--unshare-net",
|
||||
"--unshare-pid",
|
||||
"--die-with-parent",
|
||||
"--new-session",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--symlink",
|
||||
"usr/bin",
|
||||
"/bin",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--bind",
|
||||
"/tmp/custodian-resume-producer-all-i56scyc5",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
|
||||
"/toolchain",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod",
|
||||
"/modules",
|
||||
"--chdir",
|
||||
"/work/source",
|
||||
"--clearenv",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/toolchain/bin:/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/work/home",
|
||||
"--setenv",
|
||||
"GOTOOLCHAIN",
|
||||
"local",
|
||||
"--setenv",
|
||||
"GOPROXY",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOSUMDB",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOMODCACHE",
|
||||
"/modules",
|
||||
"--setenv",
|
||||
"GOCACHE",
|
||||
"/work/gocache",
|
||||
"--setenv",
|
||||
"GOMAXPROCS",
|
||||
"4",
|
||||
"--setenv",
|
||||
"CGO_ENABLED",
|
||||
"0",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"4",
|
||||
"-json",
|
||||
"./...",
|
||||
"-count=1"
|
||||
],
|
||||
"workspace": "/tmp/custodian-resume-producer-all-i56scyc5",
|
||||
"network": "disabled",
|
||||
"pid_namespace": "private",
|
||||
"real_credentials": false,
|
||||
"canonical_owner_modified": false,
|
||||
"candidate_files": {
|
||||
"adapters/go/decisionpep/LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"adapters/go/decisionpep/go.mod": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3",
|
||||
"adapters/go/decisionpep/internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde",
|
||||
"adapters/go/decisionpep/pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19",
|
||||
"adapters/go/decisionpep/pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3",
|
||||
"adapters/go/decisionpep/pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f",
|
||||
"adapters/go/decisionpep/pkg/decisionpep/client.go": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba",
|
||||
"adapters/go/decisionpep/pkg/decisionpep/client_test.go": "bff576b84df6aea607feea19c6372f543b29dbcc74df76ed43a852c9dc3e48ee",
|
||||
"adapters/go/decisionpep/producer-source.json": "09039298bb6e2ad32a6c8c9248468ae8a0e20376b52e05be5d6e5ab9bfd5ecfa",
|
||||
"adapters/go/decisionpep/testsupport/signfixture/sign.go": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29",
|
||||
"cmd/access-engine/forgejo_application_profile_test.go": "bb42f6695fa353fe36ebbdb05d9d12462b2a29fec54b5fd5dc7b1e07d357d225",
|
||||
"cmd/access-engine/main.go": "e4b6ad3b3dd4bb72e9a908fa7eb1296c8202ae92b81922c640c632415dbd6445",
|
||||
"examples/forgejo-application-access/policy.md": "1ad11259f7ea8aae160f2542d88719cdb247037a2754c339c3a83e807953fa31",
|
||||
"examples/forgejo-application-access/registry.synthetic.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
|
||||
"examples/forgejo-application-access/registry_snapshot.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
|
||||
"internal/decision/engine.go": "ecbb73644d979b1f15e1df6945536c20507fb3d3f9640f755a7081a344782c7d",
|
||||
"internal/decision/json_profile.go": "430d2c89f47d218ec843af76a9766b42e9d938a15063fc8e979f14c56306f4bc"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,140 @@
|
|||
{"Time":"2026-10-17T23:06:19.442563952Z","Action":"start","Package":"github.com/netkingdom/access-engine/pkg/decisionpep"}
|
||||
{"Time":"2026-10-17T23:06:19.448497431Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision"}
|
||||
{"Time":"2026-10-17T23:06:19.448664574Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision","Output":"=== RUN TestAuthenticDecisionProducerCanonicalPrecision\n"}
|
||||
{"Time":"2026-10-17T23:06:19.453799879Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision","Output":"--- PASS: TestAuthenticDecisionProducerCanonicalPrecision (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.453867851Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestAuthenticDecisionProducerCanonicalPrecision","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:06:19.453955742Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings"}
|
||||
{"Time":"2026-10-17T23:06:19.453966716Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings","Output":"=== RUN TestDecisionAdverseBindings\n"}
|
||||
{"Time":"2026-10-17T23:06:19.454079315Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation"}
|
||||
{"Time":"2026-10-17T23:06:19.454091921Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation","Output":"=== RUN TestDecisionAdverseBindings/obligation\n"}
|
||||
{"Time":"2026-10-17T23:06:19.456028146Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation","Output":"--- PASS: TestDecisionAdverseBindings/obligation (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.456096479Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/obligation","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.456105713Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect"}
|
||||
{"Time":"2026-10-17T23:06:19.456109554Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect","Output":"=== RUN TestDecisionAdverseBindings/effect\n"}
|
||||
{"Time":"2026-10-17T23:06:19.458184694Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect","Output":"--- PASS: TestDecisionAdverseBindings/effect (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.458252765Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/effect","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.458263874Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context"}
|
||||
{"Time":"2026-10-17T23:06:19.458269384Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context","Output":"=== RUN TestDecisionAdverseBindings/context\n"}
|
||||
{"Time":"2026-10-17T23:06:19.460167821Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context","Output":"--- PASS: TestDecisionAdverseBindings/context (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.460238931Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/context","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.460249698Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn"}
|
||||
{"Time":"2026-10-17T23:06:19.460254288Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn","Output":"=== RUN TestDecisionAdverseBindings/caller-warn\n"}
|
||||
{"Time":"2026-10-17T23:06:19.461705185Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn","Output":"--- PASS: TestDecisionAdverseBindings/caller-warn (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.461765916Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-warn","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.461783085Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired"}
|
||||
{"Time":"2026-10-17T23:06:19.461788351Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired","Output":"=== RUN TestDecisionAdverseBindings/caller-expired\n"}
|
||||
{"Time":"2026-10-17T23:06:19.462928131Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired","Output":"--- PASS: TestDecisionAdverseBindings/caller-expired (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.462975309Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-expired","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.462983732Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL"}
|
||||
{"Time":"2026-10-17T23:06:19.462988502Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL","Output":"=== RUN TestDecisionAdverseBindings/excess-TTL\n"}
|
||||
{"Time":"2026-10-17T23:06:19.46429199Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL","Output":"--- PASS: TestDecisionAdverseBindings/excess-TTL (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.464345003Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/excess-TTL","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.464353875Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator"}
|
||||
{"Time":"2026-10-17T23:06:19.4643587Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator","Output":"=== RUN TestDecisionAdverseBindings/evaluator\n"}
|
||||
{"Time":"2026-10-17T23:06:19.465901766Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator","Output":"--- PASS: TestDecisionAdverseBindings/evaluator (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.465970478Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/evaluator","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.46598293Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy"}
|
||||
{"Time":"2026-10-17T23:06:19.465994049Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy","Output":"=== RUN TestDecisionAdverseBindings/policy\n"}
|
||||
{"Time":"2026-10-17T23:06:19.468933438Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy","Output":"--- PASS: TestDecisionAdverseBindings/policy (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.469071023Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.469510802Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest"}
|
||||
{"Time":"2026-10-17T23:06:19.469558296Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest","Output":"=== RUN TestDecisionAdverseBindings/policy-digest\n"}
|
||||
{"Time":"2026-10-17T23:06:19.473045303Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest","Output":"--- PASS: TestDecisionAdverseBindings/policy-digest (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.473092761Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/policy-digest","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.473129371Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant"}
|
||||
{"Time":"2026-10-17T23:06:19.473139606Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant","Output":"=== RUN TestDecisionAdverseBindings/tenant\n"}
|
||||
{"Time":"2026-10-17T23:06:19.474352633Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant","Output":"--- PASS: TestDecisionAdverseBindings/tenant (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.474412255Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/tenant","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.474419595Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action"}
|
||||
{"Time":"2026-10-17T23:06:19.474424537Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action","Output":"=== RUN TestDecisionAdverseBindings/action\n"}
|
||||
{"Time":"2026-10-17T23:06:19.475560197Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action","Output":"--- PASS: TestDecisionAdverseBindings/action (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.475602199Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/action","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.475608276Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal"}
|
||||
{"Time":"2026-10-17T23:06:19.47561228Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal","Output":"=== RUN TestDecisionAdverseBindings/caller-principal\n"}
|
||||
{"Time":"2026-10-17T23:06:19.476730761Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal","Output":"--- PASS: TestDecisionAdverseBindings/caller-principal (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.47677959Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-principal","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.476786876Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience"}
|
||||
{"Time":"2026-10-17T23:06:19.476791538Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience","Output":"=== RUN TestDecisionAdverseBindings/caller-audience\n"}
|
||||
{"Time":"2026-10-17T23:06:19.478109229Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience","Output":"--- PASS: TestDecisionAdverseBindings/caller-audience (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.47815262Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/caller-audience","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.478159401Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin"}
|
||||
{"Time":"2026-10-17T23:06:19.478163693Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin","Output":"=== RUN TestDecisionAdverseBindings/future-begin\n"}
|
||||
{"Time":"2026-10-17T23:06:19.479169558Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin","Output":"--- PASS: TestDecisionAdverseBindings/future-begin (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.479198819Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/future-begin","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.479205014Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned"}
|
||||
{"Time":"2026-10-17T23:06:19.47920918Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned","Output":"=== RUN TestDecisionAdverseBindings/unsigned\n"}
|
||||
{"Time":"2026-10-17T23:06:19.480044655Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned","Output":"--- PASS: TestDecisionAdverseBindings/unsigned (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.480074547Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/unsigned","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.480081553Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version"}
|
||||
{"Time":"2026-10-17T23:06:19.480085963Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version","Output":"=== RUN TestDecisionAdverseBindings/version\n"}
|
||||
{"Time":"2026-10-17T23:06:19.480985154Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version","Output":"--- PASS: TestDecisionAdverseBindings/version (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.48103685Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/version","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.481055146Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose"}
|
||||
{"Time":"2026-10-17T23:06:19.481073135Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose","Output":"=== RUN TestDecisionAdverseBindings/purpose\n"}
|
||||
{"Time":"2026-10-17T23:06:19.482676732Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose","Output":"--- PASS: TestDecisionAdverseBindings/purpose (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.482733595Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/purpose","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.482741611Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted"}
|
||||
{"Time":"2026-10-17T23:06:19.482746426Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted","Output":"=== RUN TestDecisionAdverseBindings/submitted\n"}
|
||||
{"Time":"2026-10-17T23:06:19.483891878Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted","Output":"--- PASS: TestDecisionAdverseBindings/submitted (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.483942474Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/submitted","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.483949787Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision"}
|
||||
{"Time":"2026-10-17T23:06:19.483961419Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision","Output":"=== RUN TestDecisionAdverseBindings/stale-decision\n"}
|
||||
{"Time":"2026-10-17T23:06:19.485034972Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision","Output":"--- PASS: TestDecisionAdverseBindings/stale-decision (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.485071672Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/stale-decision","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.485077461Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry"}
|
||||
{"Time":"2026-10-17T23:06:19.485080626Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry","Output":"=== RUN TestDecisionAdverseBindings/subnanosecond-expiry\n"}
|
||||
{"Time":"2026-10-17T23:06:19.486299262Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry","Output":"--- PASS: TestDecisionAdverseBindings/subnanosecond-expiry (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.486346044Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/subnanosecond-expiry","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.486352906Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL"}
|
||||
{"Time":"2026-10-17T23:06:19.486357171Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL","Output":"=== RUN TestDecisionAdverseBindings/missing-TTL\n"}
|
||||
{"Time":"2026-10-17T23:06:19.487498926Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL","Output":"--- PASS: TestDecisionAdverseBindings/missing-TTL (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.487526329Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/missing-TTL","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.487532786Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry"}
|
||||
{"Time":"2026-10-17T23:06:19.487536916Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry","Output":"=== RUN TestDecisionAdverseBindings/registry\n"}
|
||||
{"Time":"2026-10-17T23:06:19.4885092Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry","Output":"--- PASS: TestDecisionAdverseBindings/registry (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.488538515Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/registry","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.488546766Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor"}
|
||||
{"Time":"2026-10-17T23:06:19.488552627Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor","Output":"=== RUN TestDecisionAdverseBindings/actor\n"}
|
||||
{"Time":"2026-10-17T23:06:19.489720478Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor","Output":"--- PASS: TestDecisionAdverseBindings/actor (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.489760731Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/actor","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.489776944Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource"}
|
||||
{"Time":"2026-10-17T23:06:19.489800533Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource","Output":"=== RUN TestDecisionAdverseBindings/resource\n"}
|
||||
{"Time":"2026-10-17T23:06:19.490681293Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource","Output":"--- PASS: TestDecisionAdverseBindings/resource (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.490718823Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/resource","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.490727119Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile"}
|
||||
{"Time":"2026-10-17T23:06:19.490732755Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile","Output":"=== RUN TestDecisionAdverseBindings/legacy-numeric-profile\n"}
|
||||
{"Time":"2026-10-17T23:06:19.491622432Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile","Output":"--- PASS: TestDecisionAdverseBindings/legacy-numeric-profile (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.491666662Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/legacy-numeric-profile","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.491689277Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired"}
|
||||
{"Time":"2026-10-17T23:06:19.491694345Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired","Output":"=== RUN TestDecisionAdverseBindings/expired\n"}
|
||||
{"Time":"2026-10-17T23:06:19.492690363Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired","Output":"--- PASS: TestDecisionAdverseBindings/expired (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.492722266Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings/expired","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.492729633Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings","Output":"--- PASS: TestDecisionAdverseBindings (0.04s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.49273526Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestDecisionAdverseBindings","Elapsed":0.04}
|
||||
{"Time":"2026-10-17T23:06:19.492764701Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay"}
|
||||
{"Time":"2026-10-17T23:06:19.492776244Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay","Output":"=== RUN TestSignatureForgeryAndReplay\n"}
|
||||
{"Time":"2026-10-17T23:06:19.492788092Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload"}
|
||||
{"Time":"2026-10-17T23:06:19.492792862Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload","Output":"=== RUN TestSignatureForgeryAndReplay/forged-payload\n"}
|
||||
{"Time":"2026-10-17T23:06:19.493799896Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload","Output":"--- PASS: TestSignatureForgeryAndReplay/forged-payload (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.493847209Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/forged-payload","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.493856587Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key"}
|
||||
{"Time":"2026-10-17T23:06:19.493878878Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key","Output":"=== RUN TestSignatureForgeryAndReplay/wrong-key\n"}
|
||||
{"Time":"2026-10-17T23:06:19.494746653Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key","Output":"--- PASS: TestSignatureForgeryAndReplay/wrong-key (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.494777546Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/wrong-key","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.494785048Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay"}
|
||||
{"Time":"2026-10-17T23:06:19.494796834Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay","Output":"=== RUN TestSignatureForgeryAndReplay/replay\n"}
|
||||
{"Time":"2026-10-17T23:06:19.496075426Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay","Output":"--- PASS: TestSignatureForgeryAndReplay/replay (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.4961153Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay/replay","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.496123596Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay","Output":"--- PASS: TestSignatureForgeryAndReplay (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.496129169Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestSignatureForgeryAndReplay","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.496134678Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse"}
|
||||
{"Time":"2026-10-17T23:06:19.496139385Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse","Output":"=== RUN TestUnavailableAndPublicTokenFileRefuse\n"}
|
||||
{"Time":"2026-10-17T23:06:19.496836346Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse","Output":"--- PASS: TestUnavailableAndPublicTokenFileRefuse (0.00s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.496868132Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestUnavailableAndPublicTokenFileRefuse","Elapsed":0}
|
||||
{"Time":"2026-10-17T23:06:19.496875598Z","Action":"run","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource"}
|
||||
{"Time":"2026-10-17T23:06:19.496881252Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource","Output":"=== RUN TestConsumerExportMatchesProducerSource\n"}
|
||||
{"Time":"2026-10-17T23:06:19.556997898Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource","Output":"--- PASS: TestConsumerExportMatchesProducerSource (0.06s)\n"}
|
||||
{"Time":"2026-10-17T23:06:19.557063985Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Test":"TestConsumerExportMatchesProducerSource","Elapsed":0.06}
|
||||
{"Time":"2026-10-17T23:06:19.557079477Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Output":"PASS\n"}
|
||||
{"Time":"2026-10-17T23:06:19.559095012Z","Action":"output","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Output":"ok \tgithub.com/netkingdom/access-engine/pkg/decisionpep\t0.116s\n"}
|
||||
{"Time":"2026-10-17T23:06:19.559188025Z","Action":"pass","Package":"github.com/netkingdom/access-engine/pkg/decisionpep","Elapsed":0.117}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"target": "producer",
|
||||
"source_revision": "d328a2601095686f74376d87791bff01e2a3a2a3",
|
||||
"returncode": 0,
|
||||
"seconds": 65.82,
|
||||
"argv": [
|
||||
"bwrap",
|
||||
"--unshare-net",
|
||||
"--unshare-pid",
|
||||
"--die-with-parent",
|
||||
"--new-session",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--symlink",
|
||||
"usr/bin",
|
||||
"/bin",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--bind",
|
||||
"/tmp/custodian-resume-producer-xtoe0khs",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
|
||||
"/toolchain",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod",
|
||||
"/modules",
|
||||
"--chdir",
|
||||
"/work/source",
|
||||
"--clearenv",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/toolchain/bin:/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/work/home",
|
||||
"--setenv",
|
||||
"GOTOOLCHAIN",
|
||||
"local",
|
||||
"--setenv",
|
||||
"GOPROXY",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOSUMDB",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOMODCACHE",
|
||||
"/modules",
|
||||
"--setenv",
|
||||
"GOCACHE",
|
||||
"/work/gocache",
|
||||
"--setenv",
|
||||
"GOMAXPROCS",
|
||||
"4",
|
||||
"--setenv",
|
||||
"CGO_ENABLED",
|
||||
"0",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"4",
|
||||
"-json",
|
||||
"./cmd/access-engine",
|
||||
"-run",
|
||||
"TestForgejoActualPolicySignedLosslessAndLegacy|TestTrustedServiceActualHTTPPositiveAndRefusals|TestTrustedServiceRetainedRegistryPreapprovalAndRefusals",
|
||||
"-count=1"
|
||||
],
|
||||
"workspace": "/tmp/custodian-resume-producer-xtoe0khs",
|
||||
"network": "disabled",
|
||||
"pid_namespace": "private",
|
||||
"real_credentials": false,
|
||||
"canonical_owner_modified": false,
|
||||
"candidate_files": {
|
||||
"adapters/go/decisionpep/LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"adapters/go/decisionpep/go.mod": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3",
|
||||
"adapters/go/decisionpep/internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde",
|
||||
"adapters/go/decisionpep/pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19",
|
||||
"adapters/go/decisionpep/pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3",
|
||||
"adapters/go/decisionpep/pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f",
|
||||
"adapters/go/decisionpep/pkg/decisionpep/client.go": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba",
|
||||
"adapters/go/decisionpep/pkg/decisionpep/client_test.go": "bff576b84df6aea607feea19c6372f543b29dbcc74df76ed43a852c9dc3e48ee",
|
||||
"adapters/go/decisionpep/producer-source.json": "09039298bb6e2ad32a6c8c9248468ae8a0e20376b52e05be5d6e5ab9bfd5ecfa",
|
||||
"adapters/go/decisionpep/testsupport/signfixture/sign.go": "24574585bd63b603ecf256eb945b2d4a894e4432b56d885d07234b80d39fddb5",
|
||||
"cmd/access-engine/forgejo_application_profile_test.go": "931464acd69c34110c3cce3ddbdf732608f1b7249f5f111209a27df205b245e1",
|
||||
"cmd/access-engine/main.go": "e4b6ad3b3dd4bb72e9a908fa7eb1296c8202ae92b81922c640c632415dbd6445",
|
||||
"examples/forgejo-application-access/policy.md": "1ad11259f7ea8aae160f2542d88719cdb247037a2754c339c3a83e807953fa31",
|
||||
"examples/forgejo-application-access/registry.synthetic.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
|
||||
"examples/forgejo-application-access/registry_snapshot.json": "f83c53afad98ba72dc199ccada552c9e844d903a94a3168229d985e827fa2f0e",
|
||||
"internal/decision/engine.go": "ecbb73644d979b1f15e1df6945536c20507fb3d3f9640f755a7081a344782c7d",
|
||||
"internal/decision/json_profile.go": "430d2c89f47d218ec843af76a9766b42e9d938a15063fc8e979f14c56306f4bc"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"target": "producer",
|
||||
"source_revision": "d328a2601095686f74376d87791bff01e2a3a2a3",
|
||||
"returncode": 1,
|
||||
"seconds": 66.81,
|
||||
"argv": [
|
||||
"bwrap",
|
||||
"--unshare-net",
|
||||
"--unshare-pid",
|
||||
"--die-with-parent",
|
||||
"--new-session",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--symlink",
|
||||
"usr/bin",
|
||||
"/bin",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--bind",
|
||||
"/tmp/custodian-resume-producer-1ir3az_t",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
|
||||
"/toolchain",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod",
|
||||
"/modules",
|
||||
"--chdir",
|
||||
"/work/source",
|
||||
"--clearenv",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/toolchain/bin:/usr/bin:/bin",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/work/home",
|
||||
"--setenv",
|
||||
"GOTOOLCHAIN",
|
||||
"local",
|
||||
"--setenv",
|
||||
"GOPROXY",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOSUMDB",
|
||||
"off",
|
||||
"--setenv",
|
||||
"GOMODCACHE",
|
||||
"/modules",
|
||||
"--setenv",
|
||||
"GOCACHE",
|
||||
"/work/gocache",
|
||||
"--setenv",
|
||||
"GOMAXPROCS",
|
||||
"4",
|
||||
"--setenv",
|
||||
"CGO_ENABLED",
|
||||
"0",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"4",
|
||||
"-json",
|
||||
"./cmd/access-engine",
|
||||
"-run",
|
||||
"TestForgejoActualPolicySignedLosslessAndLegacy|TestTrustedServiceActualHTTPPositiveAndRefusals|TestTrustedServiceRetainedRegistryPreapprovalAndRefusals",
|
||||
"-count=1"
|
||||
],
|
||||
"workspace": "/tmp/custodian-resume-producer-1ir3az_t",
|
||||
"network": "disabled",
|
||||
"pid_namespace": "private",
|
||||
"real_credentials": false,
|
||||
"canonical_owner_modified": false
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
{"Time":"2026-10-17T22:50:49.514394056Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
|
||||
{"Time":"2026-10-17T22:50:49.533137271Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
|
||||
{"Time":"2026-10-17T22:50:49.533213845Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
|
||||
{"Time":"2026-10-17T22:50:49.595710681Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins"}
|
||||
{"Time":"2026-10-17T22:50:49.596022136Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins\n"}
|
||||
{"Time":"2026-10-17T22:50:49.612730726Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.61278262Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:50:49.613566436Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-17T22:50:49.613613217Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-17T22:50:49.630240237Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.630295386Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:50:49.630308777Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-17T22:50:49.630315558Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-17T22:50:49.649236847Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.64929958Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:50:49.649318183Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action"}
|
||||
{"Time":"2026-10-17T22:50:49.649328111Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action\n"}
|
||||
{"Time":"2026-10-17T22:50:49.663413081Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.663487049Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.663504985Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource"}
|
||||
{"Time":"2026-10-17T22:50:49.66351356Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource\n"}
|
||||
{"Time":"2026-10-17T22:50:49.677835919Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.677896956Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.677916939Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut"}
|
||||
{"Time":"2026-10-17T22:50:49.677924982Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut\n"}
|
||||
{"Time":"2026-10-17T22:50:49.693350107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.693413931Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:50:49.693435563Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant"}
|
||||
{"Time":"2026-10-17T22:50:49.69345661Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant\n"}
|
||||
{"Time":"2026-10-17T22:50:49.706861893Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.706927268Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.706957774Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance"}
|
||||
{"Time":"2026-10-17T22:50:49.706967107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance\n"}
|
||||
{"Time":"2026-10-17T22:50:49.721171004Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.721239517Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.721252565Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider"}
|
||||
{"Time":"2026-10-17T22:50:49.721258985Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/outsider\n"}
|
||||
{"Time":"2026-10-17T22:50:49.73814155Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/outsider (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.738203643Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:50:49.738226051Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups"}
|
||||
{"Time":"2026-10-17T22:50:49.738234284Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups\n"}
|
||||
{"Time":"2026-10-17T22:50:49.751882211Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.752014882Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.752033728Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer"}
|
||||
{"Time":"2026-10-17T22:50:49.752040897Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer\n"}
|
||||
{"Time":"2026-10-17T22:50:49.76587286Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.765931914Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.765961888Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer"}
|
||||
{"Time":"2026-10-17T22:50:49.765971194Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer\n"}
|
||||
{"Time":"2026-10-17T22:50:49.779735065Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.779787906Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.801093703Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy (0.27s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.801141071Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.27}
|
||||
{"Time":"2026-10-17T22:50:49.803536211Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals"}
|
||||
{"Time":"2026-10-17T22:50:49.803569097Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals\n"}
|
||||
{"Time":"2026-10-17T22:50:49.893766116Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
|
||||
{"Time":"2026-10-17T22:50:49.895266983Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect"}
|
||||
{"Time":"2026-10-17T22:50:49.895314585Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/redirect\n"}
|
||||
{"Time":"2026-10-17T22:50:49.903250446Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/redirect (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.903329068Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.903352819Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate"}
|
||||
{"Time":"2026-10-17T22:50:49.903370863Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate\n"}
|
||||
{"Time":"2026-10-17T22:50:49.913845124Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.913902419Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.91393517Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed"}
|
||||
{"Time":"2026-10-17T22:50:49.913946811Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/changed\n"}
|
||||
{"Time":"2026-10-17T22:50:49.920414259Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/changed (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.920481501Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.920521339Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired"}
|
||||
{"Time":"2026-10-17T22:50:49.920534261Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/expired\n"}
|
||||
{"Time":"2026-10-17T22:50:49.929199396Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/expired (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.929263328Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.92930102Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh"}
|
||||
{"Time":"2026-10-17T22:50:49.929324771Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh\n"}
|
||||
{"Time":"2026-10-17T22:50:49.957675043Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh (0.03s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.957739111Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Elapsed":0.03}
|
||||
{"Time":"2026-10-17T22:50:49.957763097Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale"}
|
||||
{"Time":"2026-10-17T22:50:49.957771961Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale\n"}
|
||||
{"Time":"2026-10-17T22:50:49.963378736Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.963429648Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:49.964168162Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind"}
|
||||
{"Time":"2026-10-17T22:50:49.96420993Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind\n"}
|
||||
{"Time":"2026-10-17T22:50:49.97145752Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:49.971524068Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:50.106157584Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals (0.30s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.106252464Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Elapsed":0.3}
|
||||
{"Time":"2026-10-17T22:50:50.106287162Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals"}
|
||||
{"Time":"2026-10-17T22:50:50.106400202Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals\n"}
|
||||
{"Time":"2026-10-17T22:50:50.209683094Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
|
||||
{"Time":"2026-10-17T22:50:50.21441439Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect"}
|
||||
{"Time":"2026-10-17T22:50:50.214469269Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect\n"}
|
||||
{"Time":"2026-10-17T22:50:50.218687389Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect (0.00s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.218731366Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Elapsed":0}
|
||||
{"Time":"2026-10-17T22:50:50.218874326Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate"}
|
||||
{"Time":"2026-10-17T22:50:50.218895047Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate\n"}
|
||||
{"Time":"2026-10-17T22:50:50.229670287Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.22973522Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:50.229754544Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed"}
|
||||
{"Time":"2026-10-17T22:50:50.229763426Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed\n"}
|
||||
{"Time":"2026-10-17T22:50:50.234511774Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed (0.00s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.234559611Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Elapsed":0}
|
||||
{"Time":"2026-10-17T22:50:50.234580594Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired"}
|
||||
{"Time":"2026-10-17T22:50:50.234586626Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired\n"}
|
||||
{"Time":"2026-10-17T22:50:50.241561501Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.241626894Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:50.241646632Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh"}
|
||||
{"Time":"2026-10-17T22:50:50.241655587Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh\n"}
|
||||
{"Time":"2026-10-17T22:50:50.265060146Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.265120128Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:50:50.265144998Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act"}
|
||||
{"Time":"2026-10-17T22:50:50.265301637Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act\n"}
|
||||
{"Time":"2026-10-17T22:50:50.270375383Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act (0.00s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.270440064Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Elapsed":0}
|
||||
{"Time":"2026-10-17T22:50:50.270458793Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale"}
|
||||
{"Time":"2026-10-17T22:50:50.27047478Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale\n"}
|
||||
{"Time":"2026-10-17T22:50:50.277250946Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.277301379Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:50.277329964Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind"}
|
||||
{"Time":"2026-10-17T22:50:50.277337664Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind\n"}
|
||||
{"Time":"2026-10-17T22:50:50.282770016Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.28292834Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:50:50.420567507Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals (0.31s)\n"}
|
||||
{"Time":"2026-10-17T22:50:50.420620754Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Elapsed":0.31}
|
||||
{"Time":"2026-10-17T22:50:50.420926195Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"PASS\n"}
|
||||
{"Time":"2026-10-17T22:50:50.425558858Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"ok \tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.910s\n"}
|
||||
{"Time":"2026-10-17T22:50:50.425685686Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.911}
|
||||
|
|
@ -0,0 +1,339 @@
|
|||
{"Time":"2026-10-17T22:49:08.070924685Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
|
||||
{"Time":"2026-10-17T22:49:08.081350257Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
|
||||
{"Time":"2026-10-17T22:49:08.081401358Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124026957Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" forgejo_application_profile_test.go:39: policy: \u003cnil\u003e \u0026{Source:../../examples/forgejo-application-access/policy.md Metadata:{ID:forgejo.application-access Name:Forgejo authenticated application access Namespace:forgejo:application Version:v1 Status:draft Package:flexauth.forgejo.application Actions:[application.access] Owner:railiance-apps Fixtures:[] Caring:{Profile:caring-0.4.0-rc2 Enforce:false CanonicalRoles:[] OrganizationRelations:[] Scopes:[] Planes:[] Capabilities:[] ExposureModes:[] Conditions:[] Restrictions:[] Metadata:map[]} Activation:map[] Metadata:map[] AllowTTL:10s} Prose:This source policy enforces the original three-group any-of in the sole PDP.\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124075109Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" The registry identifies the application tenant and admitted issuer. Fresh identity\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124081908Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" and UserInfo facts arrive only in request context from the admitted Forgejo caller,\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124087445Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" which verifies issuer JOSE and current UserInfo before this request. They are not\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124093162Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" static subject registry attributes. This policy requires aal2 assurance.\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124098356Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" The PEP independently verifies the ID token and supplies fresh UserInfo groups;\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124103541Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" the authenticated workload caller may represent only the Forgejo system. A\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124108816Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" decision authorizes one login callback, never a later HTTP/Git/SSH/API operation.\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124114118Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" The accompanying registry is synthetic test data, not an estate admission. RuleBlocks:[{Language:rego Tags:[] Info:rego Body:import rego.v1\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124123279Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.124129907Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" default decision := {\"effect\": \"deny\", \"reason\": \"application_access_refused\"}\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124153667Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.124162144Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" original_groups := {\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"}\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124179322Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.124188429Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" decision := {\"effect\": \"allow\", \"reason\": \"current_authenticated_application_access\"} if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12419604Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.action == \"application.access\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124218312Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" object.get(input, \"purpose\", \"\") == \"\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124235752Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.id == \"application:forgejo\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124249584Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.type == \"application\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124254814Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.system == \"forgejo\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124259828Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124264787Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.attributes.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124269954Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.tenant == input.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124274904Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_tenant == input.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124279837Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.id == input.context.identity_subject\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124284905Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == input.context.identity_principal_type\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12429008Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == \"human\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124307718Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_issuer == input.resource.attributes.identity_issuer\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124313318Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_assurance == \"aal2\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124318656Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.required_groups_any_of) == 3\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124323643Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {g | g := input.context.required_groups_any_of[_]} == original_groups\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124330144Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" some group in input.context.current_userinfo_groups\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124338079Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" group in original_groups\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124357403Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" is_string(input.context.decisionpep_request_nonce)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124371759Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.decisionpep_request_nonce) == 64\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124379099Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" } StartLine:12}] TestBlocks:[{Language:rego Tags:[test] Info:rego test Body:package flexauth.forgejo.application_test\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124390614Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import rego.v1\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124396682Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import data.flexauth.forgejo.application\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124401561Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.124406385Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" valid_request := {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124411254Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124416277Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124421254Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124426223Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124440569Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124445962Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12445084Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124455817Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124460759Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124465809Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124470813Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124475863Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124482193Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124488Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124493005Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124498172Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124514213Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124519173Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124524195Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124544087Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_subject\": \"user:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124550832Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_principal_type\": \"human\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124561743Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124567199Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_assurance\": \"aal2\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124572447Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"current_userinfo_groups\": [\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124577388Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124582284Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
|
||||
{"Time":"2026-10-17T22:49:08.1245871Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"required_groups_any_of\": [\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12459233Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12459728Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-owners\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124602213Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-readers\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124609507Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124616469Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"decisionpep_request_nonce\": \"0000000000000000000000000000000000000000000000000000000000000000\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124633475Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124641816Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124647614Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.124654026Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_original_groups_allow if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124660906Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" every group in [\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"] {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12467812Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"allow\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [group]})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12470151Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124718706Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124727805Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.124733107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_outsider_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124738625Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [\"outsider\"]})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124744153Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124749031Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.12478923Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_missing_identity_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124795164Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.remove(valid_request.context, [\"identity_issuer\"])})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124800583Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124805389Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.124810213Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_low_assurance_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12481529Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"identity_assurance\": \"aal1\"})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124820655Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124825371Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.12483217Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_changed_group_contract_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124839907Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"required_groups_any_of\": [\"outsider\"]})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124862252Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" } StartLine:43}] FixtureBlocks:[{Language:yaml Tags:[fixture] Info:yaml fixture Body:[\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124870169Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124876671Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"allowed-original-group\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124887942Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"request\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124899601Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124913154Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124919845Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124926879Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124934128Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124941171Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124947627Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12495513Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124962731Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124969683Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124975869Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12498274Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.124988836Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125009359Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125026258Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125035194Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125042426Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125047953Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125055762Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125062769Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_subject\": \"user:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12506808Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_principal_type\": \"human\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125073147Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125078179Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_assurance\": \"aal2\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125083265Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"current_userinfo_groups\": [\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125088332Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125093211Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125098035Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"required_groups_any_of\": [\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12510359Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125108486Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-owners\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125113382Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-readers\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125125691Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125131182Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"decisionpep_request_nonce\": \"0000000000000000000000000000000000000000000000000000000000000000\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125136638Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125141489Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125146277Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"expect\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125151264Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"effect\": \"allow\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125158802Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"reason\": \"current_authenticated_application_access\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125163825Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125169163Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125173897Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125178694Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"missing-identity-context\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125183681Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"request\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125188568Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125193546Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125198424Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125203393Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125208334Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125213248Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125236143Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125241391Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125246666Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125251734Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125256856Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12526177Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12526838Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125274602Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125281644Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125286712Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12529159Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125296396Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {}\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125301771Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125306568Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"expect\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125311554Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"effect\": \"deny\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125316577Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"reason\": \"application_access_refused\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.1253216Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125331609Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125336676Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ] StartLine:107}] RegoModule:package flexauth.forgejo.application\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125341618Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125346496Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import rego.v1\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125351248Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125356217Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" default decision := {\"effect\": \"deny\", \"reason\": \"application_access_refused\"}\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125361474Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125366668Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" original_groups := {\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"}\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12537188Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125376948Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" decision := {\"effect\": \"allow\", \"reason\": \"current_authenticated_application_access\"} if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125382241Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.action == \"application.access\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125387245Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" object.get(input, \"purpose\", \"\") == \"\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125392403Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.id == \"application:forgejo\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12539748Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.type == \"application\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125402638Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.resource.system == \"forgejo\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125407696Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125412773Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.tenant == input.resource.attributes.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125424306Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.tenant == input.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125431881Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_tenant == input.tenant\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125443801Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.id == input.context.identity_subject\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125459023Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == input.context.identity_principal_type\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125467436Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.subject.type == \"human\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125523523Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_issuer == input.resource.attributes.identity_issuer\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125539123Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" input.context.identity_assurance == \"aal2\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125544308Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.required_groups_any_of) == 3\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125551341Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" {g | g := input.context.required_groups_any_of[_]} == original_groups\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125559042Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" some group in input.context.current_userinfo_groups\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125566598Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" group in original_groups\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125573767Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" is_string(input.context.decisionpep_request_nonce)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125581423Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" count(input.context.decisionpep_request_nonce) == 64\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125586806Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125592117Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" TestModule:package flexauth.forgejo.application_test\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125597095Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125601874Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import rego.v1\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125617392Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" import data.flexauth.forgejo.application\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125629683Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125634823Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" valid_request := {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125639665Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125644688Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"subject\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125649701Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"user:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125654607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"human\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125659539Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125664544Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125669332Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"action\": \"application.access\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125677628Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"resource\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125684634Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"id\": \"application:forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125689783Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"type\": \"application\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125695581Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"system\": \"forgejo\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125700955Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125706203Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"attributes\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125711587Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125716699Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125730712Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125736249Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" },\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125741163Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"context\": {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125755861Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_tenant\": \"tenant:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12576101Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_subject\": \"user:synthetic\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125766105Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_principal_type\": \"human\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125771173Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_issuer\": \"https://issuer.invalid\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125776159Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"identity_assurance\": \"aal2\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125782543Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"current_userinfo_groups\": [\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125787683Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125792553Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12579744Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"required_groups_any_of\": [\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125803139Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-admins\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125808062Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-owners\",\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125813139Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"forge-coulomb-readers\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125817972Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" ],\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125823085Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \"decisionpep_request_nonce\": \"0000000000000000000000000000000000000000000000000000000000000000\"\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125834203Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125839073Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12584378Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125851174Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_original_groups_allow if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125856187Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" every group in [\"forge-admins\", \"forge-coulomb-owners\", \"forge-coulomb-readers\"] {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125861607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"allow\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [group]})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125867297Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125872004Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125876701Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125881472Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_outsider_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125886675Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"current_userinfo_groups\": [\"outsider\"]})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125892094Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125896801Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.12590158Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_missing_identity_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125906657Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.remove(valid_request.context, [\"identity_issuer\"])})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125911923Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125924024Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.1259303Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_low_assurance_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125935674Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"identity_assurance\": \"aal1\"})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12594104Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12594581Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" \n"}
|
||||
{"Time":"2026-10-17T22:49:08.125950706Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" test_changed_group_contract_denied if {\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125956107Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" application.decision.effect == \"deny\" with input as object.union(valid_request, {\"context\": object.union(valid_request.context, {\"required_groups_any_of\": [\"outsider\"]})})\n"}
|
||||
{"Time":"2026-10-17T22:49:08.12596214Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" }\n"}
|
||||
{"Time":"2026-10-17T22:49:08.125978506Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":" TestPackage:flexauth.forgejo.application_test Fixtures:[{ID:allowed-original-group Request:{ID: Tenant:tenant:synthetic Subject:{ID:user:synthetic Type:human Tenant:tenant:synthetic Attributes:map[]} Action:application.access Resource:{ID:application:forgejo Type:application System:forgejo Tenant:tenant:synthetic Attributes:map[identity_issuer:https://issuer.invalid tenant:tenant:synthetic]} Context:map[current_userinfo_groups:[forge-admins] decisionpep_request_nonce:0000000000000000000000000000000000000000000000000000000000000000 identity_assurance:aal2 identity_issuer:https://issuer.invalid identity_principal_type:human identity_subject:user:synthetic identity_tenant:tenant:synthetic required_groups_any_of:[forge-admins forge-coulomb-owners forge-coulomb-readers]] CaringContext:\u003cnil\u003e PolicyVersion: Purpose:} Expect:{Effect:allow Reason:current_authenticated_application_access Obligations:[] ConformanceFindings:[]} Metadata:map[]} {ID:missing-identity-context Request:{ID: Tenant:tenant:synthetic Sub"}
|
||||
{"Time":"2026-10-17T22:49:08.125988768Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"ject:{ID:user:synthetic Type:human Tenant:tenant:synthetic Attributes:map[]} Action:application.access Resource:{ID:application:forgejo Type:application System:forgejo Tenant:tenant:synthetic Attributes:map[identity_issuer:https://issuer.invalid tenant:tenant:synthetic]} Context:map[] CaringContext:\u003cnil\u003e PolicyVersion: Purpose:} Expect:{Effect:deny Reason:application_access_refused Obligations:[] ConformanceFindings:[]} Metadata:map[]}] Validation:{Valid:false Diagnostics:[] CaringFindings:[{Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed canonical roles Fields:[caring.canonical_roles] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed organization relations Fields:[caring.organization_relations] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed scopes Fields:[caring.scopes] Descriptor: Metadata:map[]"}
|
||||
{"Time":"2026-10-17T22:49:08.126002032Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed planes Fields:[caring.planes] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed capabilities Fields:[caring.capabilities] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed exposure modes Fields:[caring.exposure_modes] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed conditions Fields:[caring.conditions] Descriptor: Metadata:map[]} {Code:CARING-POLICY-MISSING-DIMENSION Severity:warning Message:policy package should declare governed restrictions Fields:[caring.restrictions] Descriptor: Metadata:map[]}] Tests:[{Name:test_changed_group_contract_denied Passed:true Error:} {Name:test_low_assurance_denied Passed:true Error:} {Name:test_missing_identity_denied Passed:false Error"}
|
||||
{"Time":"2026-10-17T22:49:08.126009895Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":":test rule evaluated to false or undefined} {Name:test_original_groups_allow Passed:true Error:} {Name:test_outsider_denied Passed:true Error:}] Fixtures:[{ID:allowed-original-group Passed:true Expected:{Effect:allow Reason:current_authenticated_application_access Obligations:[] ConformanceFindings:[]} Actual:{Effect:allow Reason:current_authenticated_application_access Obligations:[] ConformanceFindings:[]} Error:} {ID:missing-identity-context Passed:true Expected:{Effect:deny Reason:application_access_refused Obligations:[] ConformanceFindings:[]} Actual:{Effect:deny Reason:application_access_refused Obligations:[] ConformanceFindings:[]} Error:}]} Valid:false}\n"}
|
||||
{"Time":"2026-10-17T22:49:08.126023953Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy (0.04s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.126031239Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.04}
|
||||
{"Time":"2026-10-17T22:49:08.126051131Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals"}
|
||||
{"Time":"2026-10-17T22:49:08.12605674Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals\n"}
|
||||
{"Time":"2026-10-17T22:49:08.202270945Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
|
||||
{"Time":"2026-10-17T22:49:08.204055053Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect"}
|
||||
{"Time":"2026-10-17T22:49:08.204094017Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/redirect\n"}
|
||||
{"Time":"2026-10-17T22:49:08.208897544Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/redirect (0.00s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.20896887Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/redirect","Elapsed":0}
|
||||
{"Time":"2026-10-17T22:49:08.208984912Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate"}
|
||||
{"Time":"2026-10-17T22:49:08.208992595Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate\n"}
|
||||
{"Time":"2026-10-17T22:49:08.218181791Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.218242747Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/duplicate","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.218278041Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed"}
|
||||
{"Time":"2026-10-17T22:49:08.218291864Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/changed\n"}
|
||||
{"Time":"2026-10-17T22:49:08.224853212Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/changed (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.224898136Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/changed","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.224909984Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired"}
|
||||
{"Time":"2026-10-17T22:49:08.224915584Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/expired\n"}
|
||||
{"Time":"2026-10-17T22:49:08.229229607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/expired (0.00s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.229282024Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/expired","Elapsed":0}
|
||||
{"Time":"2026-10-17T22:49:08.229308571Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh"}
|
||||
{"Time":"2026-10-17T22:49:08.229317426Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh\n"}
|
||||
{"Time":"2026-10-17T22:49:08.248287502Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.248353689Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/short-fresh","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:49:08.248370686Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale"}
|
||||
{"Time":"2026-10-17T22:49:08.248376566Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale\n"}
|
||||
{"Time":"2026-10-17T22:49:08.253598824Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.253636814Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/original-stale","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.253644641Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind"}
|
||||
{"Time":"2026-10-17T22:49:08.253649366Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind\n"}
|
||||
{"Time":"2026-10-17T22:49:08.257235488Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind (0.00s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.257274596Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals/native-number-kind","Elapsed":0}
|
||||
{"Time":"2026-10-17T22:49:08.364592231Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Output":"--- PASS: TestTrustedServiceActualHTTPPositiveAndRefusals (0.24s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.364652466Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceActualHTTPPositiveAndRefusals","Elapsed":0.24}
|
||||
{"Time":"2026-10-17T22:49:08.364672593Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals"}
|
||||
{"Time":"2026-10-17T22:49:08.364681754Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals\n"}
|
||||
{"Time":"2026-10-17T22:49:08.457647438Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":" trusted_service_http_test.go:237: actual HTTP source policy pin: secrets-engine.task-execution.service-pilot v2 sha256:de5683e5a34161b9349b10cd8e4abef704e0136e3b14b97719fcd7a79850fc4d\n"}
|
||||
{"Time":"2026-10-17T22:49:08.459500203Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect"}
|
||||
{"Time":"2026-10-17T22:49:08.459549032Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect\n"}
|
||||
{"Time":"2026-10-17T22:49:08.464519381Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect (0.00s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.464585405Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/redirect","Elapsed":0}
|
||||
{"Time":"2026-10-17T22:49:08.464608814Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate"}
|
||||
{"Time":"2026-10-17T22:49:08.464624711Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate\n"}
|
||||
{"Time":"2026-10-17T22:49:08.476276202Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.476329233Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/duplicate","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.476353092Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed"}
|
||||
{"Time":"2026-10-17T22:49:08.476360378Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed\n"}
|
||||
{"Time":"2026-10-17T22:49:08.4818022Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.481870759Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/changed","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.481894357Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired"}
|
||||
{"Time":"2026-10-17T22:49:08.481904131Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired\n"}
|
||||
{"Time":"2026-10-17T22:49:08.49030167Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.490371446Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/expired","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.490396189Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh"}
|
||||
{"Time":"2026-10-17T22:49:08.490405748Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh\n"}
|
||||
{"Time":"2026-10-17T22:49:08.513151322Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh (0.02s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.513213793Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/short-fresh","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T22:49:08.513233866Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act"}
|
||||
{"Time":"2026-10-17T22:49:08.51323979Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act\n"}
|
||||
{"Time":"2026-10-17T22:49:08.519855417Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.519905689Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/registry-drift-cannot-rebind-approved-act","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.519922019Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale"}
|
||||
{"Time":"2026-10-17T22:49:08.519930197Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale\n"}
|
||||
{"Time":"2026-10-17T22:49:08.525507841Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.525556011Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/original-stale","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.52576936Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind"}
|
||||
{"Time":"2026-10-17T22:49:08.525788287Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"=== RUN TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind\n"}
|
||||
{"Time":"2026-10-17T22:49:08.532503419Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind (0.01s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.532560778Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals/native-number-kind","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T22:49:08.668954317Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Output":"--- PASS: TestTrustedServiceRetainedRegistryPreapprovalAndRefusals (0.30s)\n"}
|
||||
{"Time":"2026-10-17T22:49:08.715893357Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestTrustedServiceRetainedRegistryPreapprovalAndRefusals","Elapsed":0.3}
|
||||
{"Time":"2026-10-17T22:49:08.715974395Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\n"}
|
||||
{"Time":"2026-10-17T22:49:08.71609491Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.644s\n"}
|
||||
{"Time":"2026-10-17T22:49:08.716114874Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.645}
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// DefaultContext is the default context to run xorm queries in
|
||||
// will be overwritten by Init with HammerContext
|
||||
var DefaultContext context.Context
|
||||
|
||||
// contextKey is a value for use with context.WithValue.
|
||||
type contextKey struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// enginedContextKey is a context key. It is used with context.Value() to get the current Engined for the context
|
||||
var (
|
||||
enginedContextKey = &contextKey{"engined"}
|
||||
_ Engined = &Context{}
|
||||
)
|
||||
|
||||
// Context represents a db context
|
||||
type Context struct {
|
||||
context.Context
|
||||
e Engine
|
||||
transaction bool
|
||||
}
|
||||
|
||||
func newContext(ctx context.Context, e Engine, transaction bool) *Context {
|
||||
return &Context{
|
||||
Context: ctx,
|
||||
e: e,
|
||||
transaction: transaction,
|
||||
}
|
||||
}
|
||||
|
||||
// InTransaction if context is in a transaction
|
||||
func (ctx *Context) InTransaction() bool {
|
||||
return ctx.transaction
|
||||
}
|
||||
|
||||
// Engine returns db engine
|
||||
func (ctx *Context) Engine() Engine {
|
||||
return ctx.e
|
||||
}
|
||||
|
||||
// Value shadows Value for context.Context but allows us to get ourselves and an Engined object
|
||||
func (ctx *Context) Value(key any) any {
|
||||
if key == enginedContextKey {
|
||||
return ctx
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// WithContext returns this engine tied to this context
|
||||
func (ctx *Context) WithContext(other context.Context) *Context {
|
||||
return newContext(ctx, ctx.e.Context(other), ctx.transaction)
|
||||
}
|
||||
|
||||
// Engined structs provide an Engine
|
||||
type Engined interface {
|
||||
Engine() Engine
|
||||
}
|
||||
|
||||
// GetEngine will get a db Engine from this context or return an Engine restricted to this context
|
||||
func GetEngine(ctx context.Context) Engine {
|
||||
if e := getEngine(ctx); e != nil {
|
||||
// A private-query marker must reach XORM's real query hooks, including
|
||||
// inside an existing engine/transaction context. Wrapping the Go
|
||||
// context alone does not change XORM's stored session context.
|
||||
if isSensitiveSQL(ctx) {
|
||||
return e.Context(ctx)
|
||||
}
|
||||
return e
|
||||
}
|
||||
return x.Context(ctx)
|
||||
}
|
||||
|
||||
// getEngine will get a db Engine from this context or return nil
|
||||
func getEngine(ctx context.Context) Engine {
|
||||
if engined, ok := ctx.(Engined); ok {
|
||||
return engined.Engine()
|
||||
}
|
||||
enginedInterface := ctx.Value(enginedContextKey)
|
||||
if enginedInterface != nil {
|
||||
return enginedInterface.(Engined).Engine()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Committer represents an interface to Commit or Close the Context
|
||||
type Committer interface {
|
||||
Commit() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// halfCommitter is a wrapper of Committer.
|
||||
// It can be closed early, but can't be committed early, it is useful for reusing a transaction.
|
||||
type halfCommitter struct {
|
||||
committer Committer
|
||||
committed bool
|
||||
}
|
||||
|
||||
func (c *halfCommitter) Commit() error {
|
||||
c.committed = true
|
||||
// should do nothing, and the parent committer will commit later
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *halfCommitter) Close() error {
|
||||
if c.committed {
|
||||
// it's "commit and close", should do nothing, and the parent committer will commit later
|
||||
return nil
|
||||
}
|
||||
|
||||
// it's "rollback and close", let the parent committer rollback right now
|
||||
return c.committer.Close()
|
||||
}
|
||||
|
||||
// TxContext represents a transaction Context,
|
||||
// it will reuse the existing transaction in the parent context or create a new one.
|
||||
// Some tips to use:
|
||||
//
|
||||
// 1 It's always recommended to use `WithTx` in new code instead of `TxContext`, since `WithTx` will handle the transaction automatically.
|
||||
// 2. To maintain the old code which uses `TxContext`:
|
||||
// a. Always call `Close()` before returning regardless of whether `Commit()` has been called.
|
||||
// b. Always call `Commit()` before returning if there are no errors, even if the code did not change any data.
|
||||
// c. Remember the `Committer` will be a halfCommitter when a transaction is being reused.
|
||||
// So calling `Commit()` will do nothing, but calling `Close()` without calling `Commit()` will rollback the transaction.
|
||||
// And all operations submitted by the caller stack will be rollbacked as well, not only the operations in the current function.
|
||||
// d. It doesn't mean rollback is forbidden, but always do it only when there is an error, and you do want to rollback.
|
||||
func TxContext(parentCtx context.Context) (*Context, Committer, error) {
|
||||
if sess, ok := inTransaction(parentCtx); ok {
|
||||
return newContext(parentCtx, sess, true), &halfCommitter{committer: sess}, nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
sess.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return newContext(DefaultContext, sess, true), sess, nil
|
||||
}
|
||||
|
||||
// WithTx represents executing database operations on a transaction, if the transaction exist,
|
||||
// this function will reuse it otherwise will create a new one and close it when finished.
|
||||
func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error {
|
||||
if sess, ok := inTransaction(parentCtx); ok {
|
||||
err := f(newContext(parentCtx, sess, true))
|
||||
if err != nil {
|
||||
// rollback immediately, in case the caller ignores returned error and tries to commit the transaction.
|
||||
_ = sess.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
return txWithNoCheck(parentCtx, f)
|
||||
}
|
||||
|
||||
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f(newContext(parentCtx, sess, true)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// Insert inserts records into database
|
||||
func Insert(ctx context.Context, beans ...any) error {
|
||||
_, err := GetEngine(ctx).Insert(beans...)
|
||||
return err
|
||||
}
|
||||
|
||||
// Exec executes a sql with args
|
||||
func Exec(ctx context.Context, sqlAndArgs ...any) (sql.Result, error) {
|
||||
return GetEngine(ctx).Exec(sqlAndArgs...)
|
||||
}
|
||||
|
||||
func Get[T any](ctx context.Context, cond builder.Cond) (object *T, exist bool, err error) {
|
||||
if !cond.IsValid() {
|
||||
panic("cond is invalid in db.Get(ctx, cond). This should not be possible.")
|
||||
}
|
||||
|
||||
var bean T
|
||||
has, err := GetEngine(ctx).Where(cond).NoAutoCondition().Get(&bean)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
} else if !has {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &bean, true, nil
|
||||
}
|
||||
|
||||
func GetByID[T any](ctx context.Context, id int64) (object *T, exist bool, err error) {
|
||||
var bean T
|
||||
has, err := GetEngine(ctx).ID(id).NoAutoCondition().Get(&bean)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
} else if !has {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &bean, true, nil
|
||||
}
|
||||
|
||||
func Exist[T any](ctx context.Context, cond builder.Cond) (bool, error) {
|
||||
if !cond.IsValid() {
|
||||
panic("cond is invalid in db.Exist(ctx, cond). This should not be possible.")
|
||||
}
|
||||
|
||||
var bean T
|
||||
return GetEngine(ctx).Where(cond).NoAutoCondition().Exist(&bean)
|
||||
}
|
||||
|
||||
func ExistByID[T any](ctx context.Context, id int64) (bool, error) {
|
||||
var bean T
|
||||
return GetEngine(ctx).ID(id).NoAutoCondition().Exist(&bean)
|
||||
}
|
||||
|
||||
// DeleteByID deletes the given bean with the given ID
|
||||
func DeleteByID[T any](ctx context.Context, id int64) (int64, error) {
|
||||
var bean T
|
||||
return GetEngine(ctx).ID(id).NoAutoCondition().NoAutoTime().Delete(&bean)
|
||||
}
|
||||
|
||||
func DeleteByIDs[T any](ctx context.Context, ids ...int64) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var bean T
|
||||
_, err := GetEngine(ctx).In("id", ids).NoAutoCondition().NoAutoTime().Delete(&bean)
|
||||
return err
|
||||
}
|
||||
|
||||
func Delete[T any](ctx context.Context, opts FindOptions) (int64, error) {
|
||||
if opts == nil || !opts.ToConds().IsValid() {
|
||||
panic("opts are empty or invalid in db.Delete(ctx, opts). This should not be possible.")
|
||||
}
|
||||
|
||||
var bean T
|
||||
return GetEngine(ctx).Where(opts.ToConds()).NoAutoCondition().NoAutoTime().Delete(&bean)
|
||||
}
|
||||
|
||||
// DeleteByBean deletes all records according non-empty fields of the bean as conditions.
|
||||
func DeleteByBean(ctx context.Context, bean any) (int64, error) {
|
||||
return GetEngine(ctx).Delete(bean)
|
||||
}
|
||||
|
||||
// FindIDs finds the IDs for the given table name satisfying the given condition
|
||||
// By passing a different value than "id" for "idCol", you can query for foreign IDs, i.e. the repo IDs which satisfy the condition
|
||||
func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([]int64, error) {
|
||||
ids := make([]int64, 0, 10)
|
||||
if err := GetEngine(ctx).Table(tableName).
|
||||
Cols(idCol).
|
||||
Where(cond).
|
||||
Find(&ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one
|
||||
// Timestamps of the entities won't be updated
|
||||
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteBeans deletes all given beans, beans must contain delete conditions.
|
||||
func DeleteBeans(ctx context.Context, beans ...any) (err error) {
|
||||
e := GetEngine(ctx)
|
||||
for i := range beans {
|
||||
if _, err = e.Delete(beans[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TruncateBeans deletes all given beans, beans may contain delete conditions.
|
||||
func TruncateBeans(ctx context.Context, beans ...any) (err error) {
|
||||
e := GetEngine(ctx)
|
||||
for i := range beans {
|
||||
if _, err = e.Truncate(beans[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountByBean counts the number of database records according non-empty fields of the bean as conditions.
|
||||
func CountByBean(ctx context.Context, bean any) (int64, error) {
|
||||
return GetEngine(ctx).Count(bean)
|
||||
}
|
||||
|
||||
// TableName returns the table name according a bean object
|
||||
func TableName(bean any) string {
|
||||
return x.TableName(bean)
|
||||
}
|
||||
|
||||
// InTransaction returns true if the engine is in a transaction otherwise return false
|
||||
func InTransaction(ctx context.Context) bool {
|
||||
_, ok := inTransaction(ctx)
|
||||
return ok
|
||||
}
|
||||
|
||||
func inTransaction(ctx context.Context) (*xorm.Session, bool) {
|
||||
e := getEngine(ctx)
|
||||
if e == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
switch t := e.(type) {
|
||||
case *xorm.Engine:
|
||||
return nil, false
|
||||
case *xorm.Session:
|
||||
if t.IsInTx() {
|
||||
return t, true
|
||||
}
|
||||
return nil, false
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
"""Reconstruct interrupted public source tests without touching owner checkouts."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
PACKET = HERE
|
||||
OWNER = Path('/home/worsch/access-engine')
|
||||
REVISION = 'd328a2601095686f74376d87791bff01e2a3a2a3'
|
||||
TOOLCHAIN = Path('/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64')
|
||||
MODULES = Path('/home/worsch/go/pkg/mod')
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('target', choices=['adapter', 'producer', 'producer-all', 'producer-race'])
|
||||
parser.add_argument('--attempt', type=int, default=1)
|
||||
args = parser.parse_args()
|
||||
suffix = '' if args.attempt == 1 else '-attempt' + str(args.attempt)
|
||||
result_path = HERE / (args.target + '-result' + suffix + '.json')
|
||||
log_path = HERE / (args.target + '-test' + suffix + '.jsonl')
|
||||
if result_path.exists() or log_path.exists():
|
||||
raise SystemExit('Refusing to overwrite an earlier attempt')
|
||||
work = Path(tempfile.mkdtemp(prefix='custodian-resume-' + args.target + '-'))
|
||||
source = work / 'source'
|
||||
overlay = PACKET / 'access-engine-source'
|
||||
candidate_pins = {str(f.relative_to(overlay)): hashlib.sha256(f.read_bytes()).hexdigest()
|
||||
for f in sorted(overlay.rglob('*')) if f.is_file()}
|
||||
pins = json.loads((overlay / 'adapters/go/decisionpep/producer-source.json').read_text())
|
||||
for name, digest in pins['exact_unchanged_files'].items():
|
||||
raw = (overlay / 'adapters/go/decisionpep' / name).read_bytes()
|
||||
assert hashlib.sha256(raw).hexdigest() == digest, name
|
||||
assert raw == subprocess.check_output(['git', '-C', str(OWNER), 'show', REVISION + ':' + name]), name
|
||||
if args.target == 'adapter':
|
||||
shutil.copytree(overlay / 'adapters/go/decisionpep', source)
|
||||
test = ['go', 'test', '-p', '4', '-json', './...', '-count=1']
|
||||
else:
|
||||
source.mkdir()
|
||||
archive = subprocess.check_output(['git', '-C', str(OWNER), 'archive', REVISION])
|
||||
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
|
||||
tar.extractall(source, filter='data')
|
||||
for preimage in (PACKET / 'preimages/access-engine').rglob('*'):
|
||||
if preimage.is_file():
|
||||
assert preimage.read_bytes() == (source / preimage.relative_to(PACKET / 'preimages/access-engine')).read_bytes()
|
||||
for item in overlay.rglob('*'):
|
||||
if item.is_file():
|
||||
target = source / item.relative_to(overlay)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(item, target)
|
||||
# Test the exact producer-owned adapter in the real producer module.
|
||||
for item in (overlay / 'adapters/go/decisionpep').rglob('*.go'):
|
||||
target = source / item.relative_to(overlay / 'adapters/go/decisionpep')
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
if target.exists():
|
||||
assert target.read_bytes() == item.read_bytes()
|
||||
else:
|
||||
shutil.copyfile(item, target)
|
||||
test = ['go', 'test', '-p', '4', '-json', './cmd/access-engine', '-run',
|
||||
'TestForgejoActualPolicySignedLosslessAndLegacy|TestTrustedServiceActualHTTPPositiveAndRefusals|TestTrustedServiceRetainedRegistryPreapprovalAndRefusals', '-count=1']
|
||||
if args.target in ['producer-all', 'producer-race']:
|
||||
test = ['go', 'test', '-p', '4', '-json', './...', '-count=1']
|
||||
if args.target == 'producer-race':
|
||||
test.insert(2, '-race')
|
||||
(work / 'home').mkdir()
|
||||
fixture_binding = []
|
||||
fixture_pins = {}
|
||||
if args.target in ['producer-all', 'producer-race']:
|
||||
# Existing full-suite tests execute real sibling producer generators.
|
||||
# Supply committed public source and only the required package code;
|
||||
# never mount a host HOME, full virtualenv, custody file or credential.
|
||||
fixture = work / 'public-fixtures'
|
||||
secret_source = fixture / 'secrets-engine'
|
||||
secret_source.mkdir(parents=True)
|
||||
secret_revision = subprocess.check_output(['git', '-C', '/home/worsch/secrets-engine', 'rev-parse', 'HEAD'], text=True).strip()
|
||||
archive = subprocess.check_output(['git', '-C', '/home/worsch/secrets-engine', 'archive', secret_revision, 'src'])
|
||||
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
|
||||
tar.extractall(secret_source, filter='data')
|
||||
(secret_source / '.venv/bin').mkdir(parents=True)
|
||||
(secret_source / '.venv/bin/python').symlink_to('/usr/bin/python3')
|
||||
shutil.copytree('/home/worsch/secrets-engine/.venv/lib/python3.12/site-packages/yaml', fixture / 'python/yaml', ignore=shutil.ignore_patterns('__pycache__'))
|
||||
registry = subprocess.check_output(['git', '-C', '/home/worsch/informed-decision', 'show', 'HEAD:deploy/intake-submitters.json'])
|
||||
registry_file = work / 'home/informed-decision/deploy/intake-submitters.json'
|
||||
registry_file.parent.mkdir(parents=True)
|
||||
registry_file.write_bytes(registry)
|
||||
fixture_pins = {str(f.relative_to(fixture)): hashlib.sha256(f.read_bytes()).hexdigest()
|
||||
for f in sorted(fixture.rglob('*')) if f.is_file() and not f.is_symlink()}
|
||||
fixture_pins['intake-submitters.json'] = hashlib.sha256(registry).hexdigest()
|
||||
fixture_pins['secrets_source_revision'] = secret_revision
|
||||
fixture_binding = ['--ro-bind', str(secret_source), '/home/worsch/secrets-engine']
|
||||
argv = ['bwrap', '--unshare-net', '--unshare-pid', '--die-with-parent', '--new-session']
|
||||
for name in ['/usr', '/lib', '/lib64']:
|
||||
argv += ['--ro-bind', name, name]
|
||||
argv += ['--symlink', 'usr/bin', '/bin', '--proc', '/proc', '--dev', '/dev',
|
||||
'--tmpfs', '/tmp', '--bind', str(work), '/work',
|
||||
'--ro-bind', str(TOOLCHAIN), '/toolchain', '--ro-bind', str(MODULES), '/modules',
|
||||
'--chdir', '/work/source', '--clearenv']
|
||||
argv += fixture_binding
|
||||
environment = {'PATH': '/toolchain/bin:/usr/bin:/bin', 'HOME': '/work/home',
|
||||
'GOTOOLCHAIN': 'local', 'GOPROXY': 'off', 'GOSUMDB': 'off',
|
||||
'GOMODCACHE': '/modules', 'GOCACHE': '/work/gocache',
|
||||
'GOMAXPROCS': '4', 'CGO_ENABLED': '1' if args.target == 'producer-race' else '0'}
|
||||
if fixture_binding:
|
||||
environment['PYTHONPATH'] = '/work/public-fixtures/python'
|
||||
for key, value in environment.items():
|
||||
argv += ['--setenv', key, value]
|
||||
argv += ['--'] + test
|
||||
start = time.monotonic()
|
||||
with log_path.open('xb') as log:
|
||||
run = subprocess.run(argv, stdout=log, stderr=subprocess.STDOUT, timeout=900)
|
||||
result = {'target': args.target, 'source_revision': REVISION, 'returncode': run.returncode,
|
||||
'seconds': round(time.monotonic() - start, 2), 'argv': argv,
|
||||
'workspace': str(work), 'network': 'disabled', 'pid_namespace': 'private',
|
||||
'real_credentials': False, 'canonical_owner_modified': False,
|
||||
'candidate_files': candidate_pins, 'public_fixture_pins': fixture_pins}
|
||||
result_path.write_text(json.dumps(result, indent=2) + '\n')
|
||||
print(json.dumps({'target': args.target, 'returncode': run.returncode, 'seconds': result['seconds']}))
|
||||
raise SystemExit(run.returncode)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
"""Reproduce shape counterexamples and their correction in the actual producer."""
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
label = sys.argv[1]
|
||||
assert label in ['shapes-before', 'shapes-after']
|
||||
prior = json.loads((HERE / 'producer-all-result.json').read_text())
|
||||
assert prior['returncode'] != 0 # Completed initial full run with missing fixtures.
|
||||
source = Path(prior['workspace']) / 'source'
|
||||
overlay = HERE / 'access-engine-source'
|
||||
pins = {}
|
||||
for item in overlay.rglob('*'):
|
||||
if item.is_file():
|
||||
target = source / item.relative_to(overlay)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(item, target)
|
||||
pins[str(item.relative_to(overlay))] = hashlib.sha256(item.read_bytes()).hexdigest()
|
||||
argv = prior['argv'][:prior['argv'].index('--') + 1] + ['go', 'test', '-p', '4', '-json', './cmd/access-engine', '-run', '^TestForgejoActualPolicySignedLosslessAndLegacy$', '-count=1']
|
||||
log = HERE / (label + '.jsonl')
|
||||
assert not log.exists()
|
||||
start = time.monotonic()
|
||||
with log.open('xb') as output:
|
||||
run = subprocess.run(argv, stdout=output, stderr=subprocess.STDOUT, timeout=180)
|
||||
(HERE / (label + '.json')).write_text(json.dumps({'returncode': run.returncode, 'seconds': round(time.monotonic() - start, 2), 'argv': argv, 'source_pins': pins, 'network': 'disabled', 'pid_namespace': 'private'}, indent=2) + '\n')
|
||||
print(json.dumps({'phase': label, 'returncode': run.returncode}))
|
||||
raise SystemExit(run.returncode)
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
{"Time":"2026-10-17T23:02:42.636996993Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
|
||||
{"Time":"2026-10-17T23:02:42.646026443Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
|
||||
{"Time":"2026-10-17T23:02:42.64607436Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
|
||||
{"Time":"2026-10-17T23:02:42.687480707Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins"}
|
||||
{"Time":"2026-10-17T23:02:42.687522808Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins\n"}
|
||||
{"Time":"2026-10-17T23:02:42.698710773Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.698759565Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.698777257Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-17T23:02:42.698784318Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-17T23:02:42.709737369Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.709802834Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.709814349Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-17T23:02:42.709821274Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-17T23:02:42.719256293Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.719301379Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.719310919Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups"}
|
||||
{"Time":"2026-10-17T23:02:42.719316573Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups\n"}
|
||||
{"Time":"2026-10-17T23:02:42.72680925Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.726880585Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.726891929Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant"}
|
||||
{"Time":"2026-10-17T23:02:42.726898106Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant\n"}
|
||||
{"Time":"2026-10-17T23:02:42.735786052Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.735825124Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.735834159Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer"}
|
||||
{"Time":"2026-10-17T23:02:42.735838559Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer\n"}
|
||||
{"Time":"2026-10-17T23:02:42.743414049Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.743461958Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.743471634Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance"}
|
||||
{"Time":"2026-10-17T23:02:42.743476873Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance\n"}
|
||||
{"Time":"2026-10-17T23:02:42.752150093Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.752196875Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.752208281Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups"}
|
||||
{"Time":"2026-10-17T23:02:42.752215351Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups\n"}
|
||||
{"Time":"2026-10-17T23:02:42.760682007Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.76073492Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.76074391Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider"}
|
||||
{"Time":"2026-10-17T23:02:42.760747995Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/outsider\n"}
|
||||
{"Time":"2026-10-17T23:02:42.770777151Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/outsider (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.770835709Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.770846935Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups"}
|
||||
{"Time":"2026-10-17T23:02:42.770852427Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups\n"}
|
||||
{"Time":"2026-10-17T23:02:42.777920512Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.777985842Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.778012271Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer"}
|
||||
{"Time":"2026-10-17T23:02:42.778026735Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer\n"}
|
||||
{"Time":"2026-10-17T23:02:42.786891809Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.786932116Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.786939682Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action"}
|
||||
{"Time":"2026-10-17T23:02:42.78694383Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action\n"}
|
||||
{"Time":"2026-10-17T23:02:42.795039209Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.795082023Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.795104494Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource"}
|
||||
{"Time":"2026-10-17T23:02:42.79510893Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource\n"}
|
||||
{"Time":"2026-10-17T23:02:42.80195675Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.802007698Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.80201832Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut"}
|
||||
{"Time":"2026-10-17T23:02:42.80202805Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut\n"}
|
||||
{"Time":"2026-10-17T23:02:42.811920714Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut (0.01s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.811968316Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Elapsed":0.01}
|
||||
{"Time":"2026-10-17T23:02:42.828555999Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy (0.18s)\n"}
|
||||
{"Time":"2026-10-17T23:02:42.828606441Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.18}
|
||||
{"Time":"2026-10-17T23:02:42.828622194Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"PASS\n"}
|
||||
{"Time":"2026-10-17T23:02:42.832859699Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"ok \tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.195s\n"}
|
||||
{"Time":"2026-10-17T23:02:42.832918438Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.196}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
{"Time":"2026-10-17T23:01:47.490515679Z","Action":"start","Package":"github.com/netkingdom/access-engine/cmd/access-engine"}
|
||||
{"Time":"2026-10-17T23:01:47.528993671Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy"}
|
||||
{"Time":"2026-10-17T23:01:47.529062716Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy\n"}
|
||||
{"Time":"2026-10-17T23:01:47.678493323Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins"}
|
||||
{"Time":"2026-10-17T23:01:47.678565894Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins\n"}
|
||||
{"Time":"2026-10-17T23:01:47.714806242Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins (0.03s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.714959986Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-admins","Elapsed":0.03}
|
||||
{"Time":"2026-10-17T23:01:47.714984675Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-17T23:01:47.714994666Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-17T23:01:47.744208882Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners (0.03s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.744291778Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-owners","Elapsed":0.03}
|
||||
{"Time":"2026-10-17T23:01:47.744315484Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-17T23:01:47.744324077Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-17T23:01:47.767281983Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers (0.03s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.768215215Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forge-coulomb-readers","Elapsed":0.03}
|
||||
{"Time":"2026-10-17T23:01:47.768287533Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant"}
|
||||
{"Time":"2026-10-17T23:01:47.768298606Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant\n"}
|
||||
{"Time":"2026-10-17T23:01:47.786986918Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant (0.02s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.787157389Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-tenant","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T23:01:47.787183981Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer"}
|
||||
{"Time":"2026-10-17T23:01:47.787194414Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer\n"}
|
||||
{"Time":"2026-10-17T23:01:47.828058556Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer (0.04s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.828131479Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/forged-resource-issuer","Elapsed":0.04}
|
||||
{"Time":"2026-10-17T23:01:47.828155798Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance"}
|
||||
{"Time":"2026-10-17T23:01:47.828167241Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance\n"}
|
||||
{"Time":"2026-10-17T23:01:47.854622614Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance (0.03s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.854699568Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-assurance","Elapsed":0.03}
|
||||
{"Time":"2026-10-17T23:01:47.854717521Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource"}
|
||||
{"Time":"2026-10-17T23:01:47.854727025Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource\n"}
|
||||
{"Time":"2026-10-17T23:01:47.873861799Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource (0.02s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.873927661Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-resource","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T23:01:47.873944081Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut"}
|
||||
{"Time":"2026-10-17T23:01:47.873952891Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut\n"}
|
||||
{"Time":"2026-10-17T23:01:47.947872691Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut (0.07s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.948100493Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/claim-shortcut","Elapsed":0.07}
|
||||
{"Time":"2026-10-17T23:01:47.948174282Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups"}
|
||||
{"Time":"2026-10-17T23:01:47.948187591Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups\n"}
|
||||
{"Time":"2026-10-17T23:01:47.991945801Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":" forgejo_application_profile_test.go:122: actual PDP allowed adverse input\n"}
|
||||
{"Time":"2026-10-17T23:01:47.992029499Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups (0.04s)\n"}
|
||||
{"Time":"2026-10-17T23:01:47.992047624Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-current-groups","Elapsed":0.04}
|
||||
{"Time":"2026-10-17T23:01:47.992061285Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer"}
|
||||
{"Time":"2026-10-17T23:01:47.992070131Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer\n"}
|
||||
{"Time":"2026-10-17T23:01:48.03916284Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer (0.05s)\n"}
|
||||
{"Time":"2026-10-17T23:01:48.039228305Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-issuer","Elapsed":0.05}
|
||||
{"Time":"2026-10-17T23:01:48.039246312Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action"}
|
||||
{"Time":"2026-10-17T23:01:48.039256285Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action\n"}
|
||||
{"Time":"2026-10-17T23:01:48.075858483Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action (0.04s)\n"}
|
||||
{"Time":"2026-10-17T23:01:48.075935743Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/wrong-action","Elapsed":0.04}
|
||||
{"Time":"2026-10-17T23:01:48.075951325Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups"}
|
||||
{"Time":"2026-10-17T23:01:48.07595935Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups\n"}
|
||||
{"Time":"2026-10-17T23:01:48.114404291Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":" forgejo_application_profile_test.go:122: actual PDP allowed adverse input\n"}
|
||||
{"Time":"2026-10-17T23:01:48.116214261Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups (0.04s)\n"}
|
||||
{"Time":"2026-10-17T23:01:48.116275632Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/object-required-groups","Elapsed":0.04}
|
||||
{"Time":"2026-10-17T23:01:48.116289032Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider"}
|
||||
{"Time":"2026-10-17T23:01:48.116297607Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/outsider\n"}
|
||||
{"Time":"2026-10-17T23:01:48.163857554Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/outsider (0.05s)\n"}
|
||||
{"Time":"2026-10-17T23:01:48.163928177Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/outsider","Elapsed":0.05}
|
||||
{"Time":"2026-10-17T23:01:48.163944147Z","Action":"run","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups"}
|
||||
{"Time":"2026-10-17T23:01:48.163952876Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"=== RUN TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups\n"}
|
||||
{"Time":"2026-10-17T23:01:48.181178835Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Output":"--- PASS: TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups (0.02s)\n"}
|
||||
{"Time":"2026-10-17T23:01:48.181243633Z","Action":"pass","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy/empty-current-groups","Elapsed":0.02}
|
||||
{"Time":"2026-10-17T23:01:48.237903183Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Output":"--- FAIL: TestForgejoActualPolicySignedLosslessAndLegacy (0.71s)\n"}
|
||||
{"Time":"2026-10-17T23:01:48.238185189Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Test":"TestForgejoActualPolicySignedLosslessAndLegacy","Elapsed":0.71}
|
||||
{"Time":"2026-10-17T23:01:48.238415877Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\n"}
|
||||
{"Time":"2026-10-17T23:01:48.246822218Z","Action":"output","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Output":"FAIL\tgithub.com/netkingdom/access-engine/cmd/access-engine\t0.755s\n"}
|
||||
{"Time":"2026-10-17T23:01:48.247040589Z","Action":"fail","Package":"github.com/netkingdom/access-engine/cmd/access-engine","Elapsed":0.757}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import pathlib,subprocess,json,sqlite3,os,shlex,time
|
||||
w=pathlib.Path('/tmp/custodian-forgejo-successor-attempt2/cli-work');s='/tmp/forgejo-private-input-r1';r='/tmp/forgejo-independent-r1'
|
||||
fixtures=w/'fixtures'; fixtures.mkdir(exist_ok=True)
|
||||
A='AbCdEf012345678-'*4;B='ZyXwVu_987654321'*4 # synthetic exact64
|
||||
assert len(A)==64 and len(B)==64
|
||||
for name,data,mode in [('good',A,0o400),('replacement',B,0o440),('newline',A+'\n',0o400),('short',A[:-1],0o400),('long',A+'A',0o400),('badbyte',A[:-1]+'=',0o400),('world',A,0o444),('writable',A,0o600),('executable',A,0o500),('empty','',0o400),('CLIENT_SECRET','',0o400)]:
|
||||
p=fixtures/name
|
||||
if p.exists():p.chmod(0o600)
|
||||
p.write_text(data);p.chmod(mode)
|
||||
for name in ['symlink']:
|
||||
p=fixtures/name
|
||||
if not p.exists():p.symlink_to('good')
|
||||
if not (fixtures/'fifo').exists():os.mkfifo(fixtures/'fifo',0o400)
|
||||
(fixtures/'directory').mkdir(exist_ok=True)
|
||||
(fixtures/'badparent').mkdir(exist_ok=True);(fixtures/'badparent').chmod(0o777)
|
||||
if (fixtures/'badparent/value').exists():(fixtures/'badparent/value').chmod(0o600)
|
||||
(fixtures/'badparent/value').write_text(A);(fixtures/'badparent/value').chmod(0o400)
|
||||
if not (fixtures/'parentlink').exists():(fixtures/'parentlink').symlink_to('directory',target_is_directory=True)
|
||||
(w/'runtime').mkdir(exist_ok=True)
|
||||
dbfile=w/'runtime/test.db'
|
||||
if dbfile.exists():dbfile.unlink() # isolated synthetic test database only
|
||||
ini=w/'runtime/app.ini';ini.write_text('''APP_NAME = synthetic-private-input-test
|
||||
RUN_USER = worsch
|
||||
WORK_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = SYNTHETIC-TEST-KEY
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
SLOW_QUERY_THRESHOLD = 1ns
|
||||
PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/test.db
|
||||
[server]
|
||||
APP_DATA_PATH = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/data
|
||||
OFFLINE_MODE = true
|
||||
[repository]
|
||||
ROOT = /tmp/custodian-forgejo-successor-attempt2/cli-work/runtime/repos
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = Error
|
||||
''')
|
||||
env={'PATH':'/usr/bin:/bin','HOME':str(w/'home'),'GIT_CONFIG_NOSYSTEM':'1','GIT_CONFIG_GLOBAL':str(w/'home/test.gitconfig'),'GIT_TERMINAL_PROMPT':'0','USER':'worsch','GITEA_WORK_DIR':str(w/'runtime')}
|
||||
base=['bwrap','--unshare-pid','--unshare-net','--ro-bind','/usr','/usr','--ro-bind','/lib','/lib','--ro-bind','/lib64','/lib64','--symlink','usr/bin','/bin','--proc','/proc','--dev','/dev','--tmpfs','/tmp','--bind',str(w),str(w),'--dir','/run','--ro-bind',str(fixtures),'/run/forgejo-oidc','--ro-bind',str(fixtures/'good'),'/run/forgejo-oidc/CLIENT_SECRET','--bind',str(fixtures),'/run/writable-input','--chdir',str(w/'runtime'),'--','env','-i']+[k+'='+v for k,v in env.items()]+[str(w/'forgejo'),'--config',str(ini)]
|
||||
results=[]
|
||||
def run(args,ok=True,reason=None):
|
||||
p=subprocess.run(base+args,capture_output=True,text=True,timeout=60); output=p.stdout+p.stderr
|
||||
assert A not in output and B not in output, 'synthetic input leaked in output'
|
||||
assert all(A not in arg and B not in arg for arg in base+args), 'synthetic input in argv'
|
||||
assert all(A not in v and B not in v for v in env.values()), 'synthetic input in env'
|
||||
results.append({'operation':args,'returncode':p.returncode,'stdout_stderr_secret_absent':True})
|
||||
(w/'cli-attempts.json').write_text(json.dumps(results,indent=2))
|
||||
if (p.returncode==0)!=ok:raise AssertionError('CLI unexpected status: '+repr(args)+'\n'+output[-5000:])
|
||||
if reason:assert reason in output,output[-2000:]
|
||||
return p
|
||||
run(['migrate'])
|
||||
clean_ini=ini.read_text()
|
||||
ini.write_text(clean_ini.replace('DB_TYPE = sqlite3', 'DB_TYPE = sqlite3\nLOG_SQL = true'))
|
||||
run(['admin','auth','add-oauth','--name','refused-debug-logging','--provider','github','--secret-file','/run/forgejo-oidc/CLIENT_SECRET'],False,'OAuth configuration requires SQL logging disabled')
|
||||
ini.write_text(clean_ini)
|
||||
def rows():
|
||||
with sqlite3.connect(w/'runtime/test.db') as db:return db.execute('select id,name,cfg from login_source order by id').fetchall()
|
||||
def cfg():return json.loads(rows()[0][2])
|
||||
run(['admin','auth','add-oauth','--name','synthetic-oauth','--provider','github','--key','synthetic-public-id','--secret-file','/run/forgejo-oidc/CLIENT_SECRET','--group-claim-name','groups','--admin-group','unchanged-admin','--scopes','read:user,user:email'])
|
||||
assert len(rows())==1 and cfg()['ClientSecret']==A
|
||||
id=str(rows()[0][0]); before=cfg()
|
||||
run(['admin','auth','update-oauth','--id',id,'--icon-url','https://synthetic.invalid/icon'])
|
||||
assert cfg()['ClientSecret']==A
|
||||
preserved=cfg()
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'])
|
||||
after=cfg();assert after['ClientSecret']==B
|
||||
assert {k:v for k,v in after.items() if k!='ClientSecret'}=={k:v for k,v in preserved.items() if k!='ClientSecret'},'secret replacement changed unrelated config'
|
||||
# Guarded inactive source exercises actual update omission and strict list semantics
|
||||
# without discovery/network or claiming a browser login.
|
||||
with sqlite3.connect(w/'runtime/test.db') as db:
|
||||
guarded=after|{'Provider':'openidConnect','OpenIDConnectAutoDiscoveryURL':'https://synthetic.invalid/.well-known/openid-configuration','RequiredClaimName':'groups','RequiredClaimValues':['forge-admins','forge-coulomb-owners','forge-coulomb-readers']}
|
||||
db.execute('update login_source set is_active=0,cfg=? where id=?',(json.dumps(guarded),id))
|
||||
run(['admin','auth','update-oauth','--id',id,'--name','synthetic-guarded'])
|
||||
assert cfg()['RequiredClaimValues']==guarded['RequiredClaimValues'] and cfg()['ClientSecret']==B
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/good'])
|
||||
assert cfg()['ClientSecret']==A and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
|
||||
for path in ['/run/forgejo-oidc/'+n for n in ['newline','short','long','badbyte','world','writable','executable','empty','symlink','fifo','directory','missing','badparent/value','parentlink/value']]+['relative','','/run/forgejo-oidc/../forgejo-oidc/good','/run/writable-input/good']:
|
||||
before=rows()
|
||||
for cmd in [['admin','auth','add-oauth','--name','refused','--provider','github'],['admin','auth','update-oauth','--id',id,'--name','refused']]:
|
||||
run(cmd+['--secret-file',path],False,'secret-file input refused')
|
||||
assert rows()==before,'failure changed DB'
|
||||
for action in [['add-oauth','--name','refused'],['update-oauth','--id',id]]:
|
||||
before=rows();run(['admin','auth']+action+['--secret','','--secret-file','/run/forgejo-oidc/good'],False,'mutually exclusive');assert rows()==before
|
||||
# Failure to clear/ambiguously replace gate keeps stored application bytes unchanged.
|
||||
before=rows();run(['admin','auth','update-oauth','--id',id,'--required-claim-values',''],False);assert rows()==before
|
||||
# Actual XORM error hook under a failing SQLite trigger must not disclose cfg.
|
||||
before=rows()
|
||||
with sqlite3.connect(w/'runtime/test.db') as database:
|
||||
database.execute("create trigger synthetic_private_error before update on login_source begin select raise(abort, 'synthetic-db-failure'); end")
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret-file','/run/forgejo-oidc/replacement'],False,'OAuth source update failed')
|
||||
assert rows()==before
|
||||
with sqlite3.connect(w/'runtime/test.db') as database:database.execute('drop trigger synthetic_private_error')
|
||||
# Legacy explicit secret remains supported; synthetic legacy string is not private input.
|
||||
run(['admin','auth','update-oauth','--id',id,'--secret','synthetic-legacy-literal'])
|
||||
assert cfg()['ClientSecret']=='synthetic-legacy-literal' and cfg()['RequiredClaimValues']==guarded['RequiredClaimValues']
|
||||
(w/'cli-results.json').write_text(json.dumps({'passed':True,'subprocesses':len(results),'real_backend_cli_and_sqlite':True,'network':'disabled by bwrap','secret_inputs':'synthetic only','values_never_in_argv_env_output':True,'actual_browser_login':False,'actual_readonly_file_bind':True,'replacement_preserves_other_config':True,'unsafe_SQL_logging_refused':True,'actual_error_and_slow_hooks_no_leak':True},indent=2));print('CLI integrations passed:',len(results))
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue