Bind real Forgejo callback to current issuer grants and retain ongoing profile gates
This commit is contained in:
parent
7c682d9c66
commit
27737fcce0
110 changed files with 32617 additions and 0 deletions
|
|
@ -0,0 +1,273 @@
|
|||
{
|
||||
"objective": "Finish what you found missing and finish CUST-WP-0080",
|
||||
"status": "active",
|
||||
"observed_at": "2026-10-18T10:25:27.278660+00:00",
|
||||
"full_scope_baseline": {
|
||||
"path": "docs/changes/netkingdom-target-2026-10-18/cust80-completion-r1/completion-audit.json",
|
||||
"sha256": "sha256:9bdee9a790db598956fe35d5954137bb4582c9f3810d2392f7ef3236c369b065",
|
||||
"all_requirements_retained": true
|
||||
},
|
||||
"normative_architecture": {
|
||||
"path": "docs/netkingdom-target-security-architecture.md",
|
||||
"sha256": "sha256:701c1e276df3d21925f6939821fb3953babfcb5bce8ff10fed582642f75ff484"
|
||||
},
|
||||
"acceptance_gates": [
|
||||
{
|
||||
"id": "TSA-01",
|
||||
"required_outcome": "Every inventoried privileged PEP authenticates its decision",
|
||||
"required_evidence": "PEP denominator; admitted signer/channel; forged, altered, wrong-key/pin, unsigned/downgrade, expired and rotation refusals. Start SECRETS-WP-0014.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-02",
|
||||
"required_outcome": "Exact approval produces one bounded protected operation",
|
||||
"required_evidence": "SECRETS-WP-0013/RPF-WP-0035-T12 native effect; Approval T01/T05 issuer/tenant provenance; wrong actor/tenant/body, expiry, replay and unavailable refusal.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-03",
|
||||
"required_outcome": "Crashes preserve uncertainty and cleanup",
|
||||
"required_evidence": "Actual ledger restart/concurrency, consumed-before-effect failure, target reconciliation, scoped cleanup and revocation evidence; no blind replay.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-04",
|
||||
"required_outcome": "First reusable browser case is accepted",
|
||||
"required_evidence": "NK-WP-0048 and named Mason/Warden/KeyCape adapters; independent native structure, sign-in/denial, delivery, revocation and rollback.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-05",
|
||||
"required_outcome": "Human and browser consumers can use the delivered identity",
|
||||
"required_evidence": "KEY-WP-0040/U08 real-factor recovery; User Engine journey; NK-WP-0050 and CUST-WP-0080-T05 human/agent Playwright acceptance.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-06",
|
||||
"required_outcome": "Actual agent and runner authority is bounded",
|
||||
"required_evidence": "CUST-WP-0073/GLAS-WP-0012 admitted runtime read-denial/tool-effect proof; Forge/Platform per-job credential and repository-workflow abuse tests.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-07",
|
||||
"required_outcome": "Credential lifecycle is inventoried and simplified",
|
||||
"required_evidence": "Current owner/consumer denominator, unknown/orphan findings, native-identity elimination candidates; sampled replacement/adoption/old-refusal and deletion semantics. G04/G09/G12.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-08",
|
||||
"required_outcome": "Recovery is independent and measured",
|
||||
"required_evidence": "NK-WP-0049 plus platform/root owners; isolated recovery with ordinary issuer unavailable, restore/reconnect/audit continuity and measured RTO/RPO. G08/G11.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-09",
|
||||
"required_outcome": "Security effects have independently reconciled evidence",
|
||||
"required_evidence": "Admitted source/class inventory, atomic outbox, receiver readback, lag/duplicate/boundary accounting and missing-class findings. Approval/Audit/Tenant/Secrets owners.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-10",
|
||||
"required_outcome": "Source and delivery controls cover declared entry paths",
|
||||
"required_evidence": "FORGE-WP-0006/RMGR-WP-0021: denominator, SSH/HTTP/UI/new repo/import/mirror coverage, required checks/provenance, private synthetic finding through resolution.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-11",
|
||||
"required_outcome": "Discovery remains accurate",
|
||||
"required_evidence": "All reviewed IDs exactly once, owner/index parity, usable source links, current limits/version aliases, drift checks and package-family disposition. Reuse/Repo Manager/provider owners.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-12",
|
||||
"required_outcome": "Improvement operates without repeated manual polling",
|
||||
"required_evidence": "CUST-WP-0079/Activity accepted schedule, unassisted firing, checkpoint and changed-state notifications; adopted cadence runtime/receiver receipts; owner change and native remeasurement.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-13",
|
||||
"required_outcome": "Shared adapters reduce per-consumer implementation",
|
||||
"required_evidence": "Producer-format cross-language fixtures, two real consumers, version compatibility, denied-path parity, upgrades and rollback; no second PDP.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-14",
|
||||
"required_outcome": "Minimal profiles are independently reusable",
|
||||
"required_evidence": "Version/provider lock, dependency and optional-provider declaration, provider adapters, second clean installation, complete positive/negative journeys and upgrade/recovery instructions.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-15",
|
||||
"required_outcome": "Optimization improves measured operations",
|
||||
"required_evidence": "Baseline and repeat measurements of onboarding time/touches, p50/p95 decision/effect latency, scanner throughput/failure, resources/headroom and owner effort with unchanged control coverage.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
},
|
||||
{
|
||||
"id": "TSA-16",
|
||||
"required_outcome": "Governance and exceptions remain truthful",
|
||||
"required_evidence": "Existing SEC-DSM ledger and close-down controls reconciled to exact evidence; real authority for changes, explicit residual promotions, preserved deferrals and no invented owner assent.",
|
||||
"status": "incomplete",
|
||||
"completion_evidence": []
|
||||
}
|
||||
],
|
||||
"sec_dsm_requirements": {
|
||||
"path": "docs/evidence/2026-10-12-netkingdom-security/sec-dsm-requirement-ledger.json",
|
||||
"sha256": "sha256:ad81f7186ada04aa1cc410932fc8669d69dc2232f3c0be00657b17f3aa3a8184",
|
||||
"count": 116,
|
||||
"status_counts": {
|
||||
"unverified": 84,
|
||||
"incomplete": 12,
|
||||
"reference": 20
|
||||
},
|
||||
"closure_status_unchanged": true
|
||||
},
|
||||
"gap_ids": [
|
||||
"G01",
|
||||
"G02",
|
||||
"G03",
|
||||
"G04",
|
||||
"G05",
|
||||
"G06",
|
||||
"G07",
|
||||
"G08",
|
||||
"G09",
|
||||
"G10",
|
||||
"G11",
|
||||
"G12",
|
||||
"G13"
|
||||
],
|
||||
"separate_required_deliverables": [
|
||||
"G01-G13 current closure evidence or already applicable approved exceptions",
|
||||
"SEC-DSM requirement ledger reconciled with exact evidence",
|
||||
"T05 real human and agent Playwright acceptance and supported discovery",
|
||||
"Second real consumer and clean installation proves reuse",
|
||||
"Residual handoffs before workplan finish; no required work left only in prose"
|
||||
],
|
||||
"source_progress": {
|
||||
"packet": "docs/changes/netkingdom-target-2026-10-18/forgejo-postgres-source-r1",
|
||||
"producer_source_commit": "git:ca0cb75729a63c9288b7333382d0fa3eb1e8c9a3",
|
||||
"consumer_owner_commit": "git:308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3",
|
||||
"consumer_paths": 40,
|
||||
"patch_sha256": "sha256:feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c",
|
||||
"postgreSQL_test_subtest_passes": 37,
|
||||
"database_schema_profiles": [
|
||||
"default",
|
||||
"explicit-public",
|
||||
"named"
|
||||
],
|
||||
"separate_client_crash_recovery": true,
|
||||
"actual_database_server_restart_recovery": true,
|
||||
"controlled_missing_lock_and_private_marker_counterexamples": true,
|
||||
"following_query_hook_context_panic_corrected": true,
|
||||
"selected_composed_passes_including_subtests": 94,
|
||||
"source_skips": 0,
|
||||
"release": {
|
||||
"frontend_and_static_backend": "passed",
|
||||
"release_version": "11.0.3+cust80.feb10793",
|
||||
"binary_sha256": "sha256:92efb26614d3fca22c400920d92c4cd022e32ab86c1f562f60699ab5f750cd28",
|
||||
"embedded_assets_exact": 2234,
|
||||
"actual_release_CLI_checks": 48,
|
||||
"exact_local_OCI_exports": 2,
|
||||
"manifest": "sha256:9d127b0a75d3d53b1598e7f380dce29f23f42c2e69056fdfc9a42122d98c41c7",
|
||||
"native_admission": false
|
||||
},
|
||||
"independent_owner_assent": false,
|
||||
"production_database": false,
|
||||
"native_gates_closed": 0
|
||||
},
|
||||
"current_work": "Implement the selected current KeyCape checks across actual Forgejo ongoing HTTP/API/Git/SSH paths, including authoritative account/source/subject/application-tenant binding, current groups and exact resource/action policy. Finish actual current protected operation/inverse and effect/approval integration; private journal custody/retention; every-release-writer exclusion; complete release rebuild and independent/native acceptance. All sixteen gates, G01-G13 and 116 ledger entries remain in scope.",
|
||||
"workplan_finished": false,
|
||||
"goal_complete": false,
|
||||
"preceding_audit": {
|
||||
"path": "docs/changes/netkingdom-target-2026-10-18/cust80-completion-r5/completion-audit.json",
|
||||
"sha256": "sha256:db0c077a8f6cc687579f6f53af48ebaa502ebccdda2533b186e71d942896d66e"
|
||||
},
|
||||
"issuer_source_progress": {
|
||||
"packet": "docs/changes/netkingdom-target-2026-10-18/keycape-token-status-r1",
|
||||
"base_revision": "git:d803e9282a47b49daf930fb905312fa5104c0ace",
|
||||
"scope": "proposed opt-in extension in the existing issuer",
|
||||
"source_delta_paths": 18,
|
||||
"complete_race_passes_including_subtests": 659,
|
||||
"existing_native_external_skips": 6,
|
||||
"new_token_status_passes_including_subtests": 43,
|
||||
"actual_built_server_process_checks": 26,
|
||||
"actual_process_restart": true,
|
||||
"actual_handler_HTTPS_PKCE_status_revocation": true,
|
||||
"controlled_revocation_guard_omission_failed_checks": 7,
|
||||
"independent_owner_assent": false,
|
||||
"native_admission": false,
|
||||
"gates_closed": 0,
|
||||
"owner_identifier_reconciliation": "existing exact historical reference restored using official consistency injection helper; new canonical records assigned by normal Repo Manager; no replacement existing UUID",
|
||||
"consumer_owner_commit": "git:67a0869c1ee40c9e00a60cb7c1d828734d5d3d32",
|
||||
"owner_primary_projection_verified": true,
|
||||
"owner_workplan": "KEY-WP-0042",
|
||||
"owner_workplan_status": "proposed",
|
||||
"owner_tasks": {
|
||||
"T01": "progress",
|
||||
"T02": "done",
|
||||
"T03": "todo"
|
||||
},
|
||||
"source_task_complete_only": true
|
||||
},
|
||||
"shared_client_source_progress": {
|
||||
"packet": "docs/changes/netkingdom-target-2026-10-18/keycape-current-client-r1",
|
||||
"issuer_commit": "git:08c083bae9ff35fbaf028c36d72a95b57d2541b7",
|
||||
"warden_commit": "git:08c098e64a9afd89e43f65436782ed2bb69cdfbd",
|
||||
"complete_issuer_race_passes_including_subtests": 724,
|
||||
"existing_native_external_skips": 6,
|
||||
"warden_canonical_unit_passes": 796,
|
||||
"warden_deselected": 4,
|
||||
"built_cross_owner_fixture_assertions": 45,
|
||||
"owner_processes": 2,
|
||||
"generated_go_consumer_processes": 4,
|
||||
"exact_source_delta_replay_paths": 23,
|
||||
"single_generated_producer_export_and_drift_refusal": true,
|
||||
"independent_owner_assent": false,
|
||||
"native_admission": false,
|
||||
"second_real_consumer": false,
|
||||
"native_gates_closed": 0,
|
||||
"both_owner_primary_projections_verified": true
|
||||
},
|
||||
"current_callback_source_progress": {
|
||||
"packet": "docs/changes/netkingdom-target-2026-10-18/forgejo-current-token-r1",
|
||||
"issuer_revision": "git:e2c5a947829568baf837543a6708fd0c3f6a863b",
|
||||
"apps_revision": "git:ab3cf5cdc56d085d040256e2e84633b0289ab0af",
|
||||
"consumer_source_postimages_exact": 49,
|
||||
"issuer_source_postimages_exact": 6,
|
||||
"latest_focused_results": 91,
|
||||
"final_composed_results": 116,
|
||||
"composed_skips": 0,
|
||||
"complete_issuer_race_results": 726,
|
||||
"existing_native_external_skips": 6,
|
||||
"actual_issuer_consumer_processes": 5,
|
||||
"original_purpose_and_issuer_failures_retained": true,
|
||||
"synthetic_MFA_and_PDP_admission": true,
|
||||
"independent_owner_assent": false,
|
||||
"complete_release_rebuild": false,
|
||||
"native_admission": false,
|
||||
"native_gates_closed": 0,
|
||||
"both_owner_primary_projections_verified": true
|
||||
},
|
||||
"ongoing_access_profile": {
|
||||
"selection": "Require current KeyCape checks on every ongoing API/Git/SSH operation for linked Forgejo accounts; retain separate local break-glass path",
|
||||
"selected_by": "user conversation response",
|
||||
"decision": "b96ef564-c713-4a48-9ab2-849958cec681",
|
||||
"implementation_status": "incomplete",
|
||||
"native_rollout_authority": false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
# Actual Forgejo callback and issuer current-grant binding
|
||||
|
||||
This source checkpoint consumes the existing producer-owned KeyCape client
|
||||
inside actual Forgejo source. It does not introduce a token registry, PDP or
|
||||
independently maintained client. The generated five-file adapter is exact from
|
||||
KeyCape `08c083ba`; issuer source `e2c5a947` adds the online human ID token's
|
||||
exact RS256 `at_hash` binding. Legacy disabled-profile issuance is unchanged.
|
||||
The [OIDC definition](https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken)
|
||||
supplies the hash rule; requiring it is the explicit proposed online-consumer
|
||||
profile, not a claim that the code flow universally requires this optional field.
|
||||
|
||||
The callback requires independently admitted application tenant and current-token
|
||||
configuration. It separates ID-token purpose from access-grant `jti`/`scope`,
|
||||
checks the signed exact token-pair hash before private caller access, and joins
|
||||
issuer, owning client, subject, tenant, principal type, issuance, assurance,
|
||||
original authentication time and tenant provenance. Fresh authenticated status
|
||||
checks precede the PDP and follow its response. No stale positive is cached;
|
||||
missing, revoked, substituted, suspended, unavailable or withdrawn-trust inputs
|
||||
refuse, without a group-only fallback or disclosed bearer/credential value.
|
||||
|
||||
## Verification
|
||||
|
||||
- Two new purpose counterexamples fail against the original ID verifier (three
|
||||
failure events including the parent test); the corrected latest focused suite
|
||||
passes 91 test/subtest results without skips.
|
||||
- The complete actual issuer race suite passes 726 test/subtest results with
|
||||
the same six preexisting native/external skips, listed in `issuer-full-race.json`.
|
||||
New checks verify exact signed pair correspondence, distinct grants/resource
|
||||
audience separation and unchanged disabled-profile output.
|
||||
- A compiled actual Forgejo test binary executes in five separate consumer
|
||||
processes against real fixture HTTPS token/discovery/UserInfo/status/revocation
|
||||
handlers. Two current grants pass; a mixed pair, revoked original and suspended
|
||||
human refuse. The provider FetchUser, guarded callback and generated current
|
||||
client execute. The identical consumer fails against the preceding issuer.
|
||||
- All 49 consumer source postimages and six issuer source delta postimages replay
|
||||
exactly from their pinned source archives with fuzz zero. Compiled runtime and
|
||||
generated client bytes match. The sole later source change adds a tested
|
||||
owner-tenant negative vector.
|
||||
|
||||
Positive integration phases execute actual discovery and FetchUser. Negative
|
||||
phases supply a completed-authentication snapshot to exercise the callback
|
||||
current-status boundary directly. MFA authentication sessions and PDP
|
||||
admission/signing are synthetic. Tokens and
|
||||
private signing/TLS/client material remain in fixture memory/tmpfs, never public
|
||||
receipts. The actual paired source fixture is not a native browser, deployed
|
||||
Forgejo, production issuer/custody or second real consumer acceptance. A complete
|
||||
frontend/static release and OCI rebuild for the new patch remain outstanding.
|
||||
Earlier release artifacts apply to their original patch only.
|
||||
|
||||
The first composed run passed 114 results and skipped the existing private
|
||||
file-replacement check because its reused fixture directory was exhausted. The
|
||||
runner now creates new read-only/private fixture aliases per trial. The original
|
||||
skip and its cause remain in the receipts; they are not PostgreSQL/native proof.
|
||||
The final composed rerun passes 116 test/subtest results without skips, as
|
||||
recorded in `validation-summary.json`.
|
||||
|
||||
## Selected ongoing profile and remaining work
|
||||
|
||||
The user selected **current KeyCape checks on every API/Git/SSH operation** for
|
||||
NetKingdom-linked accounts, retaining the separate local break-glass path. State
|
||||
Hub decision `b96ef564-c713-4a48-9ab2-849958cec681` records that conversation
|
||||
choice and grants no native mutation. Actual ongoing route/resource/action/group
|
||||
enforcement is still implementation work under RAPPS16-T09/NK48-T43. This callback
|
||||
checkpoint cannot satisfy it.
|
||||
|
||||
Current protected operation/inverse identities, approval/effect correspondence,
|
||||
private journal custody/retention, every release writer, independent owner review,
|
||||
complete package/type/source admission and all native acceptance remain open.
|
||||
The full CUST-WP-0080 scope stays sixteen gates, G01–G13 and 116 ledger entries;
|
||||
no native gate closes here. Existing owner task UUIDs/statuses are preserved.
|
||||
|
||||
Apps publication retains the initial four-file source commit and a separate
|
||||
two-file validation-document correction. `apps-owner-source` preserves initial
|
||||
postimages; `apps-owner-source-correction` preserves final corrections. The
|
||||
original executed correction manifest is byte-exact alongside its capsule-path
|
||||
projection. Runtime and patch bytes do not change in that refinement.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"actual_discovery_and_userinfo": true,
|
||||
"actual_forgejo_callback_and_generated_client": true,
|
||||
"actual_https_human_PKCE": true,
|
||||
"actual_introspection_and_revocation": true,
|
||||
"consumer_processes": 5,
|
||||
"native_acceptance": false,
|
||||
"phases": [
|
||||
"active",
|
||||
"mixed-pair",
|
||||
"revoked-after-userinfo",
|
||||
"active",
|
||||
"suspended-after-userinfo"
|
||||
],
|
||||
"private_inputs": "fixture memory/tmpfs only",
|
||||
"status": "passed",
|
||||
"status_calls": 7,
|
||||
"synthetic_MFA_session_seed_and_PDP_admission": true,
|
||||
"userinfo_calls": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
"""Apply only frozen owner source; preserve unrelated worktree and index state."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
HERE=Path(__file__).resolve().parent
|
||||
parser=argparse.ArgumentParser();parser.add_argument('owner',choices=['key-cape','railiance-apps']);args=parser.parse_args()
|
||||
cfg=json.loads((HERE/(args.owner+'-application-manifest.json')).read_text());root=Path('/home/worsch')/args.owner
|
||||
sha=lambda raw:'sha256:'+hashlib.sha256(raw).hexdigest()
|
||||
def git(*a):return subprocess.check_output(['git','-C',str(root),*a],text=True,timeout=40).strip()
|
||||
base=cfg['base_revision'].removeprefix('git:')
|
||||
assert git('rev-parse','HEAD')==base==git('ls-remote','origin','refs/heads/main').split()[0]
|
||||
assert set(git('diff','--name-only','HEAD').splitlines())<={'.custodian-brief.md','WORK-RECORDS.md'}
|
||||
paths={r['path'] for r in cfg['files']}
|
||||
protected={name:{'sha256':sha((root/name).read_bytes()),'index':git('ls-files','--stage','--',name)} for name in git('ls-files').splitlines() if name not in paths and (root/name).is_file()}
|
||||
before=re.findall(r'```task\n.*?```',(root/cfg['workplan']).read_text(),re.S)
|
||||
for r in cfg['files']:
|
||||
p=root/r['path'];assert (not p.exists()) if r['before_sha256'] is None else sha(p.read_bytes())==r['before_sha256'],r['path']
|
||||
assert sha(Path(r['source']).read_bytes())==r['sha256'],r['path']
|
||||
for r in cfg['files']:
|
||||
p=root/r['path'];p.parent.mkdir(parents=True,exist_ok=True);p.write_bytes(Path(r['source']).read_bytes())
|
||||
assert re.findall(r'```task\n.*?```',(root/cfg['workplan']).read_text(),re.S)==before
|
||||
for name,r in protected.items():assert sha((root/name).read_bytes())==r['sha256'] and git('ls-files','--stage','--',name)==r['index'],name
|
||||
out=HERE/(args.owner+'-publication');out.mkdir(exist_ok=False)
|
||||
(out/'protected.json').write_text(json.dumps(protected,indent=2)+'\n')
|
||||
(out/'application.json').write_text(json.dumps({'owner':args.owner,'files':len(paths),'existing_task_blocks_preserved':len(before),'unrelated_worktree_and_index_preserved':True,'native_admission':False},indent=2)+'\n')
|
||||
publication={'owner':args.owner,'base_revision':base,'message':cfg['message'],'files':[{'path':r['path'],'sha256':r['sha256'].removeprefix('sha256:')} for r in cfg['files']]}
|
||||
(out/'publication-inputs.json').write_text(json.dumps(publication,indent=2)+'\n');print(json.dumps({'owner':args.owner,'files':len(paths),'source_applied':True}))
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# Forgejo release preparation (build only)
|
||||
|
||||
The Apps Makefile targets package the digest-bound 49-path Forgejo 11.0.3
|
||||
source candidate in `forgejo-security-extension.patch`. It combines the original
|
||||
three-group any-of gate, private secret-file input, private source-operation DB
|
||||
primitives and the producer-owned Access Engine client in the OIDC callback.
|
||||
Coordinator source tests and complete release checks are recorded separately;
|
||||
independent owner review and operational admission of this revision remain open. They never deploy, push, change default values or choose
|
||||
an operational image. Third-party application source is expanded only into a new
|
||||
`/tmp` workspace; this repository retains a release input lock and exact reviewed
|
||||
patch, not a second source-code tree. Upstream source ownership and publication
|
||||
remain separate owner work.
|
||||
|
||||
Supply explicit public input archives/cache paths to `make forgejo-release-prepare`:
|
||||
`FORGEJO_SOURCE_ARCHIVE`, `FORGEJO_GO_ARCHIVE`, `FORGEJO_NODE_ARCHIVE`,
|
||||
`FORGEJO_BUILD_GOMODCACHE`, optionally `FORGEJO_BUILD_GOCACHE_SEED` and
|
||||
`FORGEJO_BUILD_NPM_CACHE_SEED`. Every archive, patch, source output, dependency
|
||||
lock and selected host compiler tool is digest-bound by `inputs.lock.json`.
|
||||
Go's official checksums and Node's official SHASUMS must be independently checked
|
||||
when acquiring those public archives. The source archive is official Forgejo
|
||||
v11.0.3; archive PAX commit b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237.
|
||||
|
||||
The workspace must not exist. Existing trials are preserved, never restarted
|
||||
because an observer timed out. The recipe's `process.json` records the exact
|
||||
active child PID, phase and command, then completion; logs and receipts stay in
|
||||
the workspace. Do not run two phases against the same workspace concurrently.
|
||||
|
||||
`make forgejo-release-acquire-npm` explicitly acquires only public registry
|
||||
packages, with fresh HOME/config paths, integrity-bound lockfile and lifecycle
|
||||
scripts disabled. With the cache ready, `make forgejo-release-build` reruns
|
||||
`npm ci --offline --ignore-scripts`, Go module verification, upstream frontend
|
||||
compilation and upstream generation/static backend targets inside a network
|
||||
namespace without connectivity. It uses the exact Go/Node distributions, local
|
||||
compiler inputs, `bindata timetzdata sqlite sqlite_unlock_notify` plus upstream
|
||||
static target's `netgo osusergo`, explicit Make GOFLAGS for trimpath/buildvcs and
|
||||
fixed file/directory timestamps. Build tags retain the upstream rootless build
|
||||
profile. `make forgejo-release-verify` compares every embedded public/options/
|
||||
template file to actual generated source bytes, checks the extension CLI and
|
||||
checks the ELF has no dynamic interpreter. These are local artifact checks,
|
||||
not application login, DB, native rollout or architecture acceptance.
|
||||
|
||||
For a **local container build** only, first acquire the locked public amd64
|
||||
base with an empty Docker client config and the explicit local socket. Then
|
||||
`make forgejo-release-container` uses that exact digest as a build input and a
|
||||
minimal context containing only the verified executable and generated Dockerfile.
|
||||
It directly exports a local OCI archive with `rewrite-timestamp=true`, no-cache,
|
||||
no image load or push. SOURCE_DATE_EPOCH alone does not fix COPY parent-directory
|
||||
timestamps. The image epoch is max(source epoch,ceil immutable base created
|
||||
instant), so the original base layers remain exact while new metadata is stable.
|
||||
The recipe verifies every OCI blob, original base layer prefix, rootless runtime
|
||||
configuration, provenance labels and exact final executable. Two no-cache exports
|
||||
must reproduce the frozen output image and archive digests before claiming
|
||||
reproducibility. The actual local proof receipts are in the accompanying source
|
||||
proposal packet, not an admission of the resulting artifact.
|
||||
|
||||
There is no deployment tag, registry publication, RUN directive, privileged
|
||||
container or Docker host/secret mount. Public base acquisition and a standard
|
||||
local Docker/buildx daemon supporting OCI export/layer timestamp rewriting are
|
||||
explicit prerequisites. An unavailable feature is a retained blocker; do not
|
||||
switch to a privileged builder or claim a successful image from a backend-only
|
||||
binary. Existing contexts/output archives refuse overwrite. Reconcile an actual
|
||||
failed export explicitly, retaining original metadata/logs/artifacts; observation
|
||||
timeouts never justify another build. Older prototype load/save behavior and
|
||||
its config-versus-manifest-ID issue are retained in the source trial history,
|
||||
not shipped as a production continuation.
|
||||
|
||||
The static host toolchain is pinned for this Linux amd64 review estate. Broader
|
||||
cross-host reproducibility or a different architecture/compiler/libc is a new
|
||||
input revision, not an implicit fallback. The glibc static-linker's optional
|
||||
SQLite `dlopen` diagnostic is retained; no SQLite-extension runtime compatibility
|
||||
or production platform admission is inferred from ELF inspection or asset checks.
|
||||
An accepted owner image/source/backend, authenticated access-engine decision,
|
||||
private secret-input boundary, type/case/descriptor re-freeze, current operation/
|
||||
inverse sessions, durable journal and application/Helm writer exclusion are
|
||||
separate gates. Helm omission/rollback does not remove persisted OAuth source.
|
||||
The existing case gate is compatibility code, not a single-PDP exception.
|
||||
|
||||
## KeyCape identity and callback admission
|
||||
|
||||
The callback independently verifies the ID token against owner-admitted public
|
||||
keys, algorithms, issuer, audience and token type. Public trust is loaded from
|
||||
`FORGEJO_ACCESS_ENGINE_ADMISSION_FILE`; only an absolute private file reference
|
||||
is used for the PDP caller token. No admission file or operational keys are
|
||||
supplied by this build. New fields `tenant_source_claim` and
|
||||
`max_authentication_age` are mandatory. The KeyCape v0.3 profile uses
|
||||
`assurance_claim: assurance` and `tenant_source_claim: tenant_source`. Maximum
|
||||
identity age and maximum authentication age are separate positive durations
|
||||
chosen by the owner, not implicit defaults.
|
||||
|
||||
The signed assurance object preserves `level`, `methods`, boolean `mfa`,
|
||||
`source: key-cape` and integer `at`. Its authentication time must not follow
|
||||
issuance or the current time, and must satisfy the admitted authentication-age
|
||||
bound. Identity validity ends at the earliest token expiration, issued-at age
|
||||
bound or authentication-age bound. Directory, registration and default tenant
|
||||
provenance remain distinct in the request. Mutable UserInfo cannot replace
|
||||
these authenticated facts. Current UserInfo subject and the Goth subject must
|
||||
match the independently verified token; the three required group alternatives
|
||||
remain policy input.
|
||||
|
||||
The parser also represents valid lower-assurance facts. Application policy
|
||||
continues to decide authorization. The Check client and unchanged canonical
|
||||
signature bytes are a generated source slice of Access Engine ca0cb757; Forgejo
|
||||
adds no policy evaluator. Exact caller/policy/registry/signer/request/lifetime
|
||||
pins, an authenticated HTTPS connection and a current ALLOW are required.
|
||||
Synthetic callback fixtures test ALLOW, DENY and adverse cases, including an ID
|
||||
token serialized by the actual pinned KeyCape handler. Those fixtures do not
|
||||
prove an actual browser, factor, custody or native rollout.
|
||||
|
||||
The current integration covers this guarded OIDC callback. Ongoing application
|
||||
HTTP/API/Git/SSH paths, current operation/inverse sessions, durable private
|
||||
evidence and retention, deployed PostgreSQL/custody acceptance and exclusion of all release
|
||||
writers remain open in RAPPS-WP-0016-T08/T09 and NK-WP-0048-T43. An old image
|
||||
approval or source test result cannot admit this candidate.
|
||||
|
||||
## PostgreSQL operation-source verification
|
||||
|
||||
The source primitive has now run on a disposable PostgreSQL 16.14 instance using
|
||||
the actual Forgejo engine, model schemas and OAuth configuration types. Tests
|
||||
cover default, explicit-public and named schemas, exact source restoration, drift
|
||||
and linked-account refusals, trigger rollback, uncooperative SQL writer exclusion
|
||||
and concurrent preparation. Independent client processes and an actual server
|
||||
restart verify prepared/applied/inverse journal recovery. The test-only
|
||||
`source_operation_postgres` tag selects the integration tests; no runtime route,
|
||||
credential, session or authority callback is added. The companion Custodian
|
||||
`forgejo-postgres-source-r1` packet holds the isolated fixture, commands, source
|
||||
pins, counterexamples and original failed attempts.
|
||||
|
||||
Actual PostgreSQL diagnostics exposed a tracing-hook composition crash. Tracing
|
||||
now publishes its task context to the shared XORM hook context so later hooks
|
||||
retain it. Ordinary and private SQLite traces and actual PostgreSQL failure
|
||||
queries exercise this path. Controlled copies without the table lock or private
|
||||
query marker fail their respective regressions. These are source/database
|
||||
mechanics checks; owner admission, current authenticated operation/inverse
|
||||
sessions, runtime/backup custody, retention, every release writer and native
|
||||
deployed-database acceptance remain separate obligations. The previous frozen
|
||||
release is historical and does not contain this context correction.
|
||||
|
||||
|
||||
## Current-token callback source continuation
|
||||
|
||||
The 49-path candidate additionally vendors the unchanged, producer-generated
|
||||
KeyCape current-token adapter from `08c083ba`; no consumer token registry or
|
||||
handwritten copy is introduced. `current_token` (explicit HTTPS issuer, resource
|
||||
audience, owning client, CA/JWKS/private-client-file references and maximum token
|
||||
age) and `application_tenant` are mandatory in the independently admitted
|
||||
`FORGEJO_ACCESS_ENGINE_ADMISSION_FILE` for this guarded callback profile. A signed
|
||||
caller cannot select the application's tenant. No operational configuration or
|
||||
credential is supplied by the build.
|
||||
|
||||
The callback requires the online KeyCape human ID token's exact RS256 `at_hash`
|
||||
access-grant binding before private caller use. Access-only `jti`/`scope` cannot
|
||||
substitute for an ID token. It joins independently verified subject/tenant/type,
|
||||
issuance, assurance/authentication time and tenant provenance to a fresh issuer
|
||||
observation before the PDP, then checks current status again after the decision.
|
||||
Source tests refuse mixed, revoked, suspended, stale, malformed and unavailable
|
||||
inputs; actual HTTPS issuer PKCE/discovery/UserInfo/status/revocation and real
|
||||
Forgejo provider/callback run in five isolated consumer processes. MFA session
|
||||
seeds and PDP admissions are synthetic. Complete release build/asset/OCI evidence
|
||||
for the preceding patch remains historical; this revision requires its own build
|
||||
and independent owner/package/type/custody admission before operational use.
|
||||
|
||||
The user selected current KeyCape checks on every ongoing API/Git/SSH operation
|
||||
for NetKingdom-linked accounts, while retaining the separate local break-glass
|
||||
path. That is an implementation requirement under RAPPS16-T09/NK48-T43, not a
|
||||
claim that the callback implements those routes or a native deployment grant.
|
||||
Ongoing resource/action/current-group decisions, protected operation/inverse and
|
||||
all release writers/journal custody remain acceptance work.
|
||||
|
||||
Final composed source validation passes 116 test/subtest results without skips.
|
||||
The initial 114-pass run skipped its private file-replacement check because a
|
||||
reused fixture was exhausted; the runner now creates fresh private/read-only
|
||||
aliases. That initial skip is retained and is not PostgreSQL acceptance.
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
---
|
||||
id: RAPPS-WP-0016
|
||||
type: workplan
|
||||
title: "Forgejo sign-in through NetKingdom, before the forge closes"
|
||||
domain: infotech
|
||||
repo: railiance-apps
|
||||
status: active
|
||||
flavor: implementation
|
||||
owner: claude-code
|
||||
topic_slug: railiance
|
||||
created: "2026-10-08"
|
||||
updated: "2026-10-18"
|
||||
depends_on:
|
||||
- KEY-WP-0038
|
||||
related_repos:
|
||||
- prj-forgejo-org-refactor
|
||||
- key-cape
|
||||
- railiance-platform
|
||||
state_hub_workstream_id: "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b"
|
||||
---
|
||||
|
||||
# RAPPS-WP-0016 - Forgejo sign-in through NetKingdom, before the forge closes
|
||||
|
||||
## Why
|
||||
|
||||
`forgejo.coulomb.social` is closing: private by default, no anonymous access
|
||||
(prj-forgejo-org-refactor ORGREF-WP-0002, decision
|
||||
`decisions/forge-visibility-policy.md`). The founder's condition is that sign-in
|
||||
runs through NetKingdom first. railiance-apps owns the Forgejo Helm release
|
||||
(`docs/forgejo-on-railiance01.md`: S5 Helm release, ingress, operator Makefile),
|
||||
so the Forgejo side lands here. This plan is ORGREF-WP-0002-T05. The NetKingdom
|
||||
side is key-cape `KEY-WP-0038`.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- **Founder guardrail:** tighten gradually; never lock anyone out; never risk
|
||||
assets; never reach a state that is hard to repair. Every step keeps the
|
||||
current sign-in path working until the new one is proven.
|
||||
- **No visibility change** in this plan. Repositories, packages and
|
||||
`REQUIRE_SIGNIN_VIEW` are ORGREF-WP-0002-T08.
|
||||
- **Configuration in git.** The authentication source is declared in
|
||||
`helm/forgejo-values.yaml` (`gitea.oauth`), with its secret drawn from an
|
||||
ExternalSecret over OpenBao. No click-ops, and no secret value in git.
|
||||
- Open registration is already disabled (`service.DISABLE_REGISTRATION: true`).
|
||||
This plan keeps it that way.
|
||||
|
||||
## Tasks
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "98004ed0-83e6-5f6e-9b18-15b8b2aec2e7"
|
||||
```
|
||||
|
||||
### T01 — Make the local administrator a sealed break-glass
|
||||
|
||||
Before anything about sign-in changes: confirm the local administrator account
|
||||
works, keep its credential only in OpenBao, and document recovery (who, how,
|
||||
where it is logged) in `docs/forgejo-on-railiance01.md`. Verify one break-glass
|
||||
sign-in and record it without the credential.
|
||||
|
||||
Acceptance: a recorded break-glass sign-in, with the recovery path written down.
|
||||
This is the "never lock ourselves out" floor for every later step.
|
||||
|
||||
Done 2026-10-17: founder sealed `forgejo_admin` into OpenBao
|
||||
`platform/workloads/forgejo/break-glass-admin` (version 1) and verified a local
|
||||
sign-in (HTTP 200, site admin): `docs/evidence/2026-10-17-forgejo-breakglass-signin.json`.
|
||||
Recovery path: `docs/forgejo-on-railiance01.md` § Break-glass administrator.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T02
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T01, KEY-WP-0038-T02]
|
||||
blocking_reason: "Break-glass done (T01); needs the NetKingdom client registered with its secret in custody (KEY-WP-0038-T02)."
|
||||
priority: high
|
||||
state_hub_task_id: "13cd3aec-c87d-5d3f-a421-39d8ff131edd"
|
||||
```
|
||||
|
||||
### T02 — Add the NetKingdom authentication source
|
||||
|
||||
- an ExternalSecret delivering the `forgejo` client secret from OpenBao to a
|
||||
Secret in the `forgejo` namespace. Custody path is
|
||||
`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, read by ESO through
|
||||
Kubernetes-auth role `external-secrets-keycape-forgejo-forgejo`, which carries
|
||||
only policy `workload-kv-read-keycape-forgejo-forgejo` (net-kingdom 074b3a0;
|
||||
replaces the earlier `platform/workloads/forgejo/netkingdom-oidc-client` proposal). The
|
||||
SA/SecretStore/ExternalSecret shape follows the NK-WP-0048
|
||||
`deliver-consumer-secret` receipt contract
|
||||
(`docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json`)
|
||||
- `gitea.oauth`: name `netkingdom` (must match the redirect URI registered in
|
||||
key-cape), provider `openidConnect`, auto-discovery from
|
||||
`https://kc.coulomb.social/.well-known/openid-configuration`, scopes
|
||||
`openid profile email groups`, group-claim name `groups`
|
||||
- `oauth2_client` settings: no automatic account creation for unknown subjects;
|
||||
account linking to existing local accounts by username after a successful
|
||||
local sign-in (so the founder and agent accounts keep their history)
|
||||
- Helm upgrade with the previous revision noted for rollback
|
||||
|
||||
Local password sign-in stays enabled.
|
||||
|
||||
Acceptance: the "Sign in with netkingdom" option appears. Local sign-in still
|
||||
works. Rollback is one `helm rollback` to the noted revision.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T03
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T02]
|
||||
blocking_reason: "Linking and team mapping follow a working authentication source."
|
||||
priority: high
|
||||
state_hub_task_id: "81f072aa-c316-511e-a2cd-21dd1b1d0a00"
|
||||
```
|
||||
|
||||
### T03 — Link accounts and map groups to teams
|
||||
|
||||
- The founder links their account through NetKingdom (MFA), then signs in again
|
||||
through NetKingdom only and sees the same repositories, organisations and
|
||||
admin rights.
|
||||
- Map NetKingdom groups (names agreed in KEY-WP-0038-T01) to organisation teams:
|
||||
owners and readers at least. Create the read-only teams that
|
||||
ORGREF-WP-0002-T06 consumers will use.
|
||||
- Apply KEY-WP-0038-T03's decision on agents and services: NetKingdom sign-in, or
|
||||
scoped tokens bound to service identities.
|
||||
|
||||
Acceptance: founder and one agent work through NetKingdom; group-to-team mapping
|
||||
verified by adding and removing one test membership; break-glass still works.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T04
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T03]
|
||||
needs_human: true
|
||||
blocking_reason: "Restricting local password sign-in is a founder decision once NetKingdom sign-in is proven."
|
||||
priority: medium
|
||||
state_hub_task_id: "32c54737-c40f-5fde-9b91-163f8dcb0068"
|
||||
```
|
||||
|
||||
### T04 — Decide on local password sign-in
|
||||
|
||||
Once NetKingdom sign-in has been used for a while without problems, the founder
|
||||
decides whether local password sign-in is turned off for everyone except
|
||||
break-glass. This is not required to close the forge; it is the next tightening
|
||||
step, taken separately.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T05
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T02]
|
||||
blocking_reason: "Waits for net-kingdom's answer to the Apps 1.3 needs-change record (the-custodian docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-apps.json: RAPPS-1.3-C1..C3) and the re-frozen 1.3 candidate."
|
||||
priority: medium
|
||||
state_hub_task_id: "728786c9-f1f9-5983-a6a4-dea21faf67f5"
|
||||
```
|
||||
|
||||
### T05 — Apps owner seams for NK-WP-0048 consumer steps
|
||||
|
||||
Implement the real Apps capabilities behind the `deliver-consumer-secret` and
|
||||
`hand-over` adapters, as owner-run paths (option C): `owner_apply` /
|
||||
`owner_remove` emitting `netkingdom.consumer-secret-delivery.v1` receipts from the
|
||||
T02 objects; a consumer verifier that observes Forgejo sign-in and the T03
|
||||
positive/negative group probes; and a committed-evidence hand-over receipt store.
|
||||
See the owner-review record for the contract.
|
||||
|
||||
Acceptance: receipts produced from the live T02/T03 run validate against the
|
||||
adapters' checks with no value-bearing keys.
|
||||
|
||||
## Also in scope: stale node credential
|
||||
|
||||
The k3s `registries.yaml` on railiance01 still holds a login for the frozen Gitea
|
||||
host `92.205.130.254` (ORGREF-WP-0002-T02). Coordinate its removal with the node
|
||||
owner when ORGREF-WP-0002-T06 adds the forge registry login, in the same change.
|
||||
|
||||
|
||||
## T06 — Package the reviewed consumer source through the Apps build path
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T06
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "08ab8921-7801-5309-b84b-bc2a3ceea1e4"
|
||||
```
|
||||
|
||||
Completed 2026-10-18, source build tooling only. The existing Apps Makefile now
|
||||
has explicit prepare, public-dependency acquisition, offline build, artifact
|
||||
verification and local OCI export targets for the pinned official Forgejo11.0.3
|
||||
source plus reviewed multi-value claim patch. Source/toolchains/locks/compiler
|
||||
inputs/base image are fixed. Third-party application source stays in a new /tmp
|
||||
workspace; Apps holds the release input lock, patch and build recipe.
|
||||
|
||||
The original build profile produced identical frontend assets, bindata sources
|
||||
and complete static executable in two workspaces, and identical local OCI
|
||||
archives in two no-cache exports. Independent review then found host process
|
||||
argv visibility through /proc (APPSBUILD-R1-F01). The revised recipe adds a
|
||||
private PID namespace; exact source and synthetic canary regression are separately
|
||||
reviewed. The original full builds are historical r1-profile evidence; no full
|
||||
r2 build is claimed. Original packet/review and failure history remain in
|
||||
Custodian apps-forgejo-release-build-r1 and apps-release-independent-review-r1;
|
||||
the correction is apps-forgejo-release-build-r2. This is coordinator source
|
||||
integration after independent technical review, not formal owner assent or image
|
||||
admission. Helm values, deployed image, local sign-in and visibility are unchanged.
|
||||
NK-WP-0048-T39 remains progress for the full release acceptance scope.
|
||||
|
||||
## T07 — Keep the client secret out of process arguments
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T07
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "786d6584-6db1-5a10-9324-d7b94db0df94"
|
||||
```
|
||||
|
||||
Implement and independently review a real Forgejo add/update --secret-file path
|
||||
and the actual chart helper/mount adaptation. Preserve the existing CLIENT_SECRET
|
||||
key and custody route; use no agent-value argument or environment fallback. Test
|
||||
bounded stable nofollow file reads, exact existing minted-secret encoding,
|
||||
mutual exclusion/update preservation, fixed errors and actual Helm rendering.
|
||||
Private mount identity/reader set and new package/type revisions need explicit
|
||||
owner/runtime admission. The in-progress isolated candidate is Custodian
|
||||
forgejo-private-input-r1; source completion is distinct from T02 native delivery.
|
||||
|
||||
## T08 — Provide a transactional application-source inverse and durable recovery
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T08
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "4ab8a5e7-ab32-5ee2-aaf2-6da246d1b0c8"
|
||||
```
|
||||
|
||||
Implement actual owner database primitives that record operation intent/outcome
|
||||
in the same durable database, compare complete raw persisted before/post/current
|
||||
source state privately, and serialize conflicting application writers at the DB
|
||||
boundary. Never emit stored credential/config bytes or delete/detach linked users
|
||||
or a preexisting/shared source. SQLite and PostgreSQL transaction semantics must
|
||||
be tested against the real app packages; unsupported backends refuse.
|
||||
|
||||
T05 and NK-WP-0048-T34 additionally require actual authenticated execution and
|
||||
fresh inverse sessions, admitted evidence/recovery and exclusion of every release
|
||||
writer, including Helm/runtime writers. The candidate DB primitive alone does
|
||||
not establish those facilities. Helm rollback/omission does not remove persisted
|
||||
OAuth sources: verify the exact app-state inverse before any image downgrade.
|
||||
If links prevent removal, retain the extended image and reconcile. The earlier
|
||||
T02 single-Helm-rollback statement is not sufficient recovery for this extension;
|
||||
its historical text is retained and corrected here.
|
||||
|
||||
2026-10-18 PostgreSQL/source-context continuation for T08: actual Forgejo model
|
||||
schemas, OAuth configuration types and production DB initialization run on a
|
||||
disposable PostgreSQL 16.14 instance. All 37 test/subtest results pass across
|
||||
default, explicit-public and named schemas. Separate client processes prove
|
||||
rollback after process death immediately following INSERT, committed apply
|
||||
survival through a real server restart, and subsequent reconciliation/inverse.
|
||||
Exact raw restoration, private drift, linked-user/external-link refusal, trigger
|
||||
rollback, table-writer exclusion and concurrent preparation are covered.
|
||||
Controlled copies with the table lock or private-query context removed fail the
|
||||
corresponding tests in all three schema profiles. All owned fixture containers
|
||||
and volumes were removed; no deployed database or real credential was used.
|
||||
|
||||
The diagnostics check exposed a real tracing-hook composition panic. The new
|
||||
40-path build candidate publishes the task context into XORM's shared query
|
||||
context so subsequent hooks preserve it. Actual private/ordinary SQLite traces
|
||||
and PostgreSQL driver-error hooks cover the correction. Six focused SQLite DB
|
||||
checks and all 94 selected composed package results pass without skips. The
|
||||
previous panic, fixture/compilation failures and refinement history remain in
|
||||
Custodian forgejo-postgres-source-r1. Complete release evidence uses the new
|
||||
feb10793 patch; the prior frozen artifact is historical.
|
||||
|
||||
This proves bounded source/database mechanics. T08 remains progress for current
|
||||
authenticated initiating/inverse sessions, release-wide writer exclusion, private
|
||||
evidence/custody/retention and native deployed-database acceptance. Independent
|
||||
owner review and exact package/type/runtime admission remain open. T07/T09 and
|
||||
all original task UUIDs and statuses are preserved.
|
||||
|
||||
## T09 — Enforce authenticated Access Engine decisions in the application path
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T09
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "d3804638-c73d-5b9f-a456-4262f62f9e6b"
|
||||
```
|
||||
|
||||
Use the producer-owned decision format/canonical bytes and independently admitted
|
||||
signer/policy/request pins. Forgejo must verify the caller's issuer/tenant and
|
||||
assurance facts, obtain and authenticate the exact Access Engine resource/action
|
||||
decision, and refuse missing, stale, wrong-pin, unavailable or malformed replies.
|
||||
Keep the original three-group any-of access set as policy input; its local
|
||||
compatibility gate is not a substitute PDP or an architecture exception. An
|
||||
issuer allowed/denied claim cannot manufacture authorization. Source integration
|
||||
needs real provider/callback/session wiring and meaningful producer-consumer
|
||||
positive/adverse tests before re-freezing the case and package. Existing break-
|
||||
glass/local account and linking behavior remain within the declared owner scope.
|
||||
|
||||
2026-10-18 source continuation for T07/T08/T09: the digest-bound build
|
||||
candidate now composes all 37 paths, including private secret-file input, actual
|
||||
source-state transactions/private SQL hooks, the producer-owned Access Engine
|
||||
Check client from ca0cb757 and guarded OIDC callback enforcement. The parser uses
|
||||
the actual KeyCape typed assurance/authentication-time/provenance contract; its
|
||||
actual issuer fixture/callback suite passes 71 test/subtest results, and selected
|
||||
composed packages pass 93 with no skips. The previous incompatible-string-claim
|
||||
rejection is retained. All patch postimages independently replay from the pinned
|
||||
official archive. Complete release build/verification is tracked in Custodian
|
||||
forgejo-keycape-claims-r1; earlier release outputs do not prove this revision.
|
||||
|
||||
This is coordinator source integration of a tested build candidate; independent
|
||||
owner review and formal operational admission remain open. T07's chart/private
|
||||
mount admission, T08's fresh authenticated operation/inverse and release-wide
|
||||
writer exclusion, T09's ongoing HTTP/API/Git/SSH paths, private evidence/retention,
|
||||
PostgreSQL/native proof and actual issuer/factor/custody acceptance remain open.
|
||||
No trust, deployed image, Helm values, source DB, user/link state or local sign-in
|
||||
is changed by this build-source update. The preserved legacy patch is historical;
|
||||
the input lock now explicitly selects forgejo-security-extension.patch.
|
||||
|
||||
|
||||
2026-10-18 current-token callback continuation: the new 49-path source candidate
|
||||
uses the exact generated KeyCape online client, without a consumer token registry.
|
||||
Mandatory owner application-tenant/current-token admission, ID-token purpose,
|
||||
exact signed RS256 at_hash, current status before/after the PDP, and signed
|
||||
identity/assurance/authentication-time/tenant-provenance joins refuse substitution
|
||||
and stale authority. The latest focused callback/identity suite passes 91 results
|
||||
without skips; the final selected four composed packages pass 116 results without
|
||||
skips. The earlier 114-pass run skipped the private file-replacement check because
|
||||
its reused fixture directory was exhausted; a fresh private/read-only fixture
|
||||
restores that check. The original skip is retained, not PostgreSQL evidence.
|
||||
The actual HTTP PKCE/discovery/UserInfo/status/revocation and Forgejo provider /
|
||||
callback pass five fresh consumer processes: two current grants accepted, mixed
|
||||
pair/revoked original/suspended human refused. The original verifier and issuer
|
||||
fail their new controls. MFA session seeds and PDP admission are synthetic.
|
||||
|
||||
The user explicitly selected current KeyCape checks on every API/Git/SSH
|
||||
operation for linked accounts, keeping the separate local break-glass path.
|
||||
That full ongoing route/resource/action/group profile is still implementation
|
||||
work here; callback proof does not close it. Input lock now selects patch
|
||||
9fe3d7ba; complete release/asset/OCI checks, independent owner assent,
|
||||
software/type/package/custody admission, real factors/current sessions and native
|
||||
acceptance remain open. T07/T08 and every existing task UUID/status are preserved.
|
||||
Exact source/evidence: Custodian forgejo-current-token-r1.
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
# Forgejo release preparation (build only)
|
||||
|
||||
The Apps Makefile targets package the digest-bound 49-path Forgejo 11.0.3
|
||||
source candidate in `forgejo-security-extension.patch`. It combines the original
|
||||
three-group any-of gate, private secret-file input, private source-operation DB
|
||||
primitives and the producer-owned Access Engine client in the OIDC callback.
|
||||
Coordinator source tests and complete release checks are recorded separately;
|
||||
independent owner review and operational admission of this revision remain open. They never deploy, push, change default values or choose
|
||||
an operational image. Third-party application source is expanded only into a new
|
||||
`/tmp` workspace; this repository retains a release input lock and exact reviewed
|
||||
patch, not a second source-code tree. Upstream source ownership and publication
|
||||
remain separate owner work.
|
||||
|
||||
Supply explicit public input archives/cache paths to `make forgejo-release-prepare`:
|
||||
`FORGEJO_SOURCE_ARCHIVE`, `FORGEJO_GO_ARCHIVE`, `FORGEJO_NODE_ARCHIVE`,
|
||||
`FORGEJO_BUILD_GOMODCACHE`, optionally `FORGEJO_BUILD_GOCACHE_SEED` and
|
||||
`FORGEJO_BUILD_NPM_CACHE_SEED`. Every archive, patch, source output, dependency
|
||||
lock and selected host compiler tool is digest-bound by `inputs.lock.json`.
|
||||
Go's official checksums and Node's official SHASUMS must be independently checked
|
||||
when acquiring those public archives. The source archive is official Forgejo
|
||||
v11.0.3; archive PAX commit b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237.
|
||||
|
||||
The workspace must not exist. Existing trials are preserved, never restarted
|
||||
because an observer timed out. The recipe's `process.json` records the exact
|
||||
active child PID, phase and command, then completion; logs and receipts stay in
|
||||
the workspace. Do not run two phases against the same workspace concurrently.
|
||||
|
||||
`make forgejo-release-acquire-npm` explicitly acquires only public registry
|
||||
packages, with fresh HOME/config paths, integrity-bound lockfile and lifecycle
|
||||
scripts disabled. With the cache ready, `make forgejo-release-build` reruns
|
||||
`npm ci --offline --ignore-scripts`, Go module verification, upstream frontend
|
||||
compilation and upstream generation/static backend targets inside a network
|
||||
namespace without connectivity. It uses the exact Go/Node distributions, local
|
||||
compiler inputs, `bindata timetzdata sqlite sqlite_unlock_notify` plus upstream
|
||||
static target's `netgo osusergo`, explicit Make GOFLAGS for trimpath/buildvcs and
|
||||
fixed file/directory timestamps. Build tags retain the upstream rootless build
|
||||
profile. `make forgejo-release-verify` compares every embedded public/options/
|
||||
template file to actual generated source bytes, checks the extension CLI and
|
||||
checks the ELF has no dynamic interpreter. These are local artifact checks,
|
||||
not application login, DB, native rollout or architecture acceptance.
|
||||
|
||||
For a **local container build** only, first acquire the locked public amd64
|
||||
base with an empty Docker client config and the explicit local socket. Then
|
||||
`make forgejo-release-container` uses that exact digest as a build input and a
|
||||
minimal context containing only the verified executable and generated Dockerfile.
|
||||
It directly exports a local OCI archive with `rewrite-timestamp=true`, no-cache,
|
||||
no image load or push. SOURCE_DATE_EPOCH alone does not fix COPY parent-directory
|
||||
timestamps. The image epoch is max(source epoch,ceil immutable base created
|
||||
instant), so the original base layers remain exact while new metadata is stable.
|
||||
The recipe verifies every OCI blob, original base layer prefix, rootless runtime
|
||||
configuration, provenance labels and exact final executable. Two no-cache exports
|
||||
must reproduce the frozen output image and archive digests before claiming
|
||||
reproducibility. The actual local proof receipts are in the accompanying source
|
||||
proposal packet, not an admission of the resulting artifact.
|
||||
|
||||
There is no deployment tag, registry publication, RUN directive, privileged
|
||||
container or Docker host/secret mount. Public base acquisition and a standard
|
||||
local Docker/buildx daemon supporting OCI export/layer timestamp rewriting are
|
||||
explicit prerequisites. An unavailable feature is a retained blocker; do not
|
||||
switch to a privileged builder or claim a successful image from a backend-only
|
||||
binary. Existing contexts/output archives refuse overwrite. Reconcile an actual
|
||||
failed export explicitly, retaining original metadata/logs/artifacts; observation
|
||||
timeouts never justify another build. Older prototype load/save behavior and
|
||||
its config-versus-manifest-ID issue are retained in the source trial history,
|
||||
not shipped as a production continuation.
|
||||
|
||||
The static host toolchain is pinned for this Linux amd64 review estate. Broader
|
||||
cross-host reproducibility or a different architecture/compiler/libc is a new
|
||||
input revision, not an implicit fallback. The glibc static-linker's optional
|
||||
SQLite `dlopen` diagnostic is retained; no SQLite-extension runtime compatibility
|
||||
or production platform admission is inferred from ELF inspection or asset checks.
|
||||
An accepted owner image/source/backend, authenticated access-engine decision,
|
||||
private secret-input boundary, type/case/descriptor re-freeze, current operation/
|
||||
inverse sessions, durable journal and application/Helm writer exclusion are
|
||||
separate gates. Helm omission/rollback does not remove persisted OAuth source.
|
||||
The existing case gate is compatibility code, not a single-PDP exception.
|
||||
|
||||
## KeyCape identity and callback admission
|
||||
|
||||
The callback independently verifies the ID token against owner-admitted public
|
||||
keys, algorithms, issuer, audience and token type. Public trust is loaded from
|
||||
`FORGEJO_ACCESS_ENGINE_ADMISSION_FILE`; only an absolute private file reference
|
||||
is used for the PDP caller token. No admission file or operational keys are
|
||||
supplied by this build. New fields `tenant_source_claim` and
|
||||
`max_authentication_age` are mandatory. The KeyCape v0.3 profile uses
|
||||
`assurance_claim: assurance` and `tenant_source_claim: tenant_source`. Maximum
|
||||
identity age and maximum authentication age are separate positive durations
|
||||
chosen by the owner, not implicit defaults.
|
||||
|
||||
The signed assurance object preserves `level`, `methods`, boolean `mfa`,
|
||||
`source: key-cape` and integer `at`. Its authentication time must not follow
|
||||
issuance or the current time, and must satisfy the admitted authentication-age
|
||||
bound. Identity validity ends at the earliest token expiration, issued-at age
|
||||
bound or authentication-age bound. Directory, registration and default tenant
|
||||
provenance remain distinct in the request. Mutable UserInfo cannot replace
|
||||
these authenticated facts. Current UserInfo subject and the Goth subject must
|
||||
match the independently verified token; the three required group alternatives
|
||||
remain policy input.
|
||||
|
||||
The parser also represents valid lower-assurance facts. Application policy
|
||||
continues to decide authorization. The Check client and unchanged canonical
|
||||
signature bytes are a generated source slice of Access Engine ca0cb757; Forgejo
|
||||
adds no policy evaluator. Exact caller/policy/registry/signer/request/lifetime
|
||||
pins, an authenticated HTTPS connection and a current ALLOW are required.
|
||||
Synthetic callback fixtures test ALLOW, DENY and adverse cases, including an ID
|
||||
token serialized by the actual pinned KeyCape handler. Those fixtures do not
|
||||
prove an actual browser, factor, custody or native rollout.
|
||||
|
||||
The current integration covers this guarded OIDC callback. Ongoing application
|
||||
HTTP/API/Git/SSH paths, current operation/inverse sessions, durable private
|
||||
evidence and retention, deployed PostgreSQL/custody acceptance and exclusion of all release
|
||||
writers remain open in RAPPS-WP-0016-T08/T09 and NK-WP-0048-T43. An old image
|
||||
approval or source test result cannot admit this candidate.
|
||||
|
||||
## PostgreSQL operation-source verification
|
||||
|
||||
The source primitive has now run on a disposable PostgreSQL 16.14 instance using
|
||||
the actual Forgejo engine, model schemas and OAuth configuration types. Tests
|
||||
cover default, explicit-public and named schemas, exact source restoration, drift
|
||||
and linked-account refusals, trigger rollback, uncooperative SQL writer exclusion
|
||||
and concurrent preparation. Independent client processes and an actual server
|
||||
restart verify prepared/applied/inverse journal recovery. The test-only
|
||||
`source_operation_postgres` tag selects the integration tests; no runtime route,
|
||||
credential, session or authority callback is added. The companion Custodian
|
||||
`forgejo-postgres-source-r1` packet holds the isolated fixture, commands, source
|
||||
pins, counterexamples and original failed attempts.
|
||||
|
||||
Actual PostgreSQL diagnostics exposed a tracing-hook composition crash. Tracing
|
||||
now publishes its task context to the shared XORM hook context so later hooks
|
||||
retain it. Ordinary and private SQLite traces and actual PostgreSQL failure
|
||||
queries exercise this path. Controlled copies without the table lock or private
|
||||
query marker fail their respective regressions. These are source/database
|
||||
mechanics checks; owner admission, current authenticated operation/inverse
|
||||
sessions, runtime/backup custody, retention, every release writer and native
|
||||
deployed-database acceptance remain separate obligations. The previous frozen
|
||||
release is historical and does not contain this context correction.
|
||||
|
||||
|
||||
## Current-token callback source continuation
|
||||
|
||||
The 49-path candidate additionally vendors the unchanged, producer-generated
|
||||
KeyCape current-token adapter from `08c083ba`; no consumer token registry or
|
||||
handwritten copy is introduced. `current_token` (explicit HTTPS issuer, resource
|
||||
audience, owning client, CA/JWKS/private-client-file references and maximum token
|
||||
age) and `application_tenant` are mandatory in the independently admitted
|
||||
`FORGEJO_ACCESS_ENGINE_ADMISSION_FILE` for this guarded callback profile. A signed
|
||||
caller cannot select the application's tenant. No operational configuration or
|
||||
credential is supplied by the build.
|
||||
|
||||
The callback requires the online KeyCape human ID token's exact RS256 `at_hash`
|
||||
access-grant binding before private caller use. Access-only `jti`/`scope` cannot
|
||||
substitute for an ID token. It joins independently verified subject/tenant/type,
|
||||
issuance, assurance/authentication time and tenant provenance to a fresh issuer
|
||||
observation before the PDP, then checks current status again after the decision.
|
||||
Source tests refuse mixed, revoked, suspended, stale, malformed and unavailable
|
||||
inputs; actual HTTPS issuer PKCE/discovery/UserInfo/status/revocation and real
|
||||
Forgejo provider/callback run in five isolated consumer processes. MFA session
|
||||
seeds and PDP admissions are synthetic. Complete release build/asset/OCI evidence
|
||||
for the preceding patch remains historical; this revision requires its own build
|
||||
and independent owner/package/type/custody admission before operational use.
|
||||
|
||||
The user selected current KeyCape checks on every ongoing API/Git/SSH operation
|
||||
for NetKingdom-linked accounts, while retaining the separate local break-glass
|
||||
path. That is an implementation requirement under RAPPS16-T09/NK48-T43, not a
|
||||
claim that the callback implements those routes or a native deployment grant.
|
||||
Ongoing resource/action/current-group decisions, protected operation/inverse and
|
||||
all release writers/journal custody remain acceptance work.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,244 @@
|
|||
{
|
||||
"schema": "railiance-apps.forgejo-release-inputs.v1",
|
||||
"forgejo_version": "11.0.3",
|
||||
"source_archive_sha256": "1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c",
|
||||
"source_commit": "b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237",
|
||||
"patch_sha256": "9fe3d7baa648c9347964a177befab5070e00151d1ec5ddff16c41890db84e62a",
|
||||
"go_archive_sha256": "3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8",
|
||||
"go_version": "1.24.3",
|
||||
"node_archive_sha256": "dafe2e8f82cb97de1bd10db9e2ec4c07bbf53389b0799b1e095a918951e78fd4",
|
||||
"node_version": "22.15.0",
|
||||
"source_date_epoch": 1752105237,
|
||||
"release_version": "11.0.3+cust80.9fe3d7ba",
|
||||
"tags": "bindata timetzdata sqlite sqlite_unlock_notify",
|
||||
"runtime_base": "code.forgejo.org/forgejo/forgejo@sha256:41649dff5311664097d29b4d80620403f0b27e7c0eee0b8af79b132a37f3807b",
|
||||
"runtime_index_digest": "sha256:691657c7e64af7730a2e885e6d022cf16ae60724931c5392bd61838afadcaeba",
|
||||
"platform": "linux/amd64",
|
||||
"reviewed_outputs": [
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth.go",
|
||||
"sha256": "d718d817c46f6393488a9c5a894b7535640cd0ec1a1ae3f72f8411a2bbec3b38"
|
||||
},
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth_secret_linux.go",
|
||||
"sha256": "6741961f1df67c1afbf34c796e549425f0e04a0d0678c6eedb05f66d4754c64b"
|
||||
},
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth_secret_linux_test.go",
|
||||
"sha256": "b28c2369dfab9bf2ca48e707e01b4394a36c8be5490cfe5bb658017bb626c4b2"
|
||||
},
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth_secret_other.go",
|
||||
"sha256": "54056c989a4757e73db0252130e7c16bf16f5ec8eaaca2468f019ba2ef5c532d"
|
||||
},
|
||||
{
|
||||
"path": "go.mod",
|
||||
"sha256": "11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation.go",
|
||||
"sha256": "ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_internal_test.go",
|
||||
"sha256": "af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_postgres_export_test.go",
|
||||
"sha256": "b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_postgres_test.go",
|
||||
"sha256": "4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_process_test.go",
|
||||
"sha256": "40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_test.go",
|
||||
"sha256": "77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428"
|
||||
},
|
||||
{
|
||||
"path": "models/db/context.go",
|
||||
"sha256": "d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7"
|
||||
},
|
||||
{
|
||||
"path": "models/db/engine.go",
|
||||
"sha256": "d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8"
|
||||
},
|
||||
{
|
||||
"path": "models/db/sensitive_sql.go",
|
||||
"sha256": "06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584"
|
||||
},
|
||||
{
|
||||
"path": "models/db/sensitive_sql_logging_test.go",
|
||||
"sha256": "d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7"
|
||||
},
|
||||
{
|
||||
"path": "models/db/sensitive_sql_test.go",
|
||||
"sha256": "de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b"
|
||||
},
|
||||
{
|
||||
"path": "models/db/tracing_context_test.go",
|
||||
"sha256": "612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070"
|
||||
},
|
||||
{
|
||||
"path": "routers/web/admin/auths.go",
|
||||
"sha256": "3498229e06fb9ff52396bcb5e68c0cc2d2ad24e6494533d8717d7c833f3e2923"
|
||||
},
|
||||
{
|
||||
"path": "routers/web/auth/oauth.go",
|
||||
"sha256": "58bff778e0dc17a521706289bf45c3e0d76a759a0874f76c379b3d1ddb419b17"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine.go",
|
||||
"sha256": "d16330043825d0284253e65d28bda2de4edbc633e4887d8c3618a106ee251daa"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_assurance.go",
|
||||
"sha256": "42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_assurance_test.go",
|
||||
"sha256": "44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_azp_test.go",
|
||||
"sha256": "5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_callback_test.go",
|
||||
"sha256": "1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_current_token.go",
|
||||
"sha256": "4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_current_token_test.go",
|
||||
"sha256": "58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_purpose_test.go",
|
||||
"sha256": "533dc3153fcf9bfcf7bfc08712c057092c7b3892882f217fb8f839fdbdd99939"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_test.go",
|
||||
"sha256": "9112a142983c0a58c5d40344b58a20fa2c1bdbc7a79b5c586796362839a43b48"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/current_issuer_fixture_test.go",
|
||||
"sha256": "24fb430e72848326c5d9ef42eab5028cf915dd601f43fb06010b32b568908781"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/providers_openid.go",
|
||||
"sha256": "5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/required_claim_values.go",
|
||||
"sha256": "29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/required_claim_values_test.go",
|
||||
"sha256": "54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/source.go",
|
||||
"sha256": "44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/source_callout.go",
|
||||
"sha256": "6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/LICENSE",
|
||||
"sha256": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/go.mod",
|
||||
"sha256": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/internal/sign/sign.go",
|
||||
"sha256": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/api/canonical.go",
|
||||
"sha256": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/api/caring.go",
|
||||
"sha256": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/api/resource_manifest.go",
|
||||
"sha256": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/decisionpep/client.go",
|
||||
"sha256": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/decisionpep/client_test.go",
|
||||
"sha256": "1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/producer-source.json",
|
||||
"sha256": "c1d83e3d79f9860ffaa61ec19a6b3415e914d3058b11b07cdd05a7d1182cdceb"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/testsupport/signfixture/sign.go",
|
||||
"sha256": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/LICENSE",
|
||||
"sha256": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/go.mod",
|
||||
"sha256": "e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/internal/jose/jose.go",
|
||||
"sha256": "a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/pkg/tokenstatus/client.go",
|
||||
"sha256": "f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/producer-source.json",
|
||||
"sha256": "3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
}
|
||||
],
|
||||
"lockfiles": {
|
||||
"go.mod": "11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"package.json": "ab5c54ec7077501b50acf3ab5f1fff4f63a51abe99e720b8ecdc6cc6c78f39fc",
|
||||
"package-lock.json": "fdd5c2f891311be7c01b1907b418c243ca1f0f951fd4b4ee5b5e931c43493657"
|
||||
},
|
||||
"host_tools": {
|
||||
"/usr/bin/make": "d78b8f1d099fbcfb6f2f49ab87223b9b68fb3956642f92d6ec6de812e8afa965",
|
||||
"/usr/bin/gcc": "1b99826121ae6682a634e5efe09bd3e3df58ce58e0b28f849114ab5b89139c26",
|
||||
"/usr/bin/ld": "e46a122cc3a29feba3b1d0fc2a274b6e46538be016ebe381370c0801bb98b0b8",
|
||||
"/usr/bin/ar": "534681ac11c18868cfc4fdf98770aa0ba8973eedc90c231e94e6ba96e1a04f27",
|
||||
"/usr/bin/bwrap": "e318903862396f96de3df57264e0158682b952fd3fb53ac23d876413e7b30f71",
|
||||
"/usr/lib/x86_64-linux-gnu/libc.a": "a40c7eae7581f977b65eddb81cbc799213a695749e039d966db55bdee4ac9621",
|
||||
"/usr/lib/x86_64-linux-gnu/libm.a": "420cd3c406f6f8c12deb3d1291146e9c091ee015a7c39eaf2237f01e80d6f0ae"
|
||||
},
|
||||
"runtime_base_config_sha256": "bc5a3df111729582faadebd01d126ba769e2b65d0c62f306f6bc79f4e562452b",
|
||||
"patch_file": "forgejo-security-extension.patch",
|
||||
"candidate_review_status": "current-token callback source tested; complete release rebuild and independent owner review pending",
|
||||
"producer_source_commit": "ca0cb75729a63c9288b7333382d0fa3eb1e8c9a3",
|
||||
"optional_source_tests": {
|
||||
"source_operation_postgres": {
|
||||
"database_major": 16,
|
||||
"synthetic_fixture_image": "postgres@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777",
|
||||
"source_only": true,
|
||||
"production_connection_or_credentials": false
|
||||
}
|
||||
},
|
||||
"identity_client_source_revision": "git:08c083bae9ff35fbaf028c36d72a95b57d2541b7",
|
||||
"identity_issuer_source_revision": "git:e2c5a947829568baf837543a6708fd0c3f6a863b"
|
||||
}
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
---
|
||||
id: RAPPS-WP-0016
|
||||
type: workplan
|
||||
title: "Forgejo sign-in through NetKingdom, before the forge closes"
|
||||
domain: infotech
|
||||
repo: railiance-apps
|
||||
status: active
|
||||
flavor: implementation
|
||||
owner: claude-code
|
||||
topic_slug: railiance
|
||||
created: "2026-10-08"
|
||||
updated: "2026-10-18"
|
||||
depends_on:
|
||||
- KEY-WP-0038
|
||||
related_repos:
|
||||
- prj-forgejo-org-refactor
|
||||
- key-cape
|
||||
- railiance-platform
|
||||
state_hub_workstream_id: "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b"
|
||||
---
|
||||
|
||||
# RAPPS-WP-0016 - Forgejo sign-in through NetKingdom, before the forge closes
|
||||
|
||||
## Why
|
||||
|
||||
`forgejo.coulomb.social` is closing: private by default, no anonymous access
|
||||
(prj-forgejo-org-refactor ORGREF-WP-0002, decision
|
||||
`decisions/forge-visibility-policy.md`). The founder's condition is that sign-in
|
||||
runs through NetKingdom first. railiance-apps owns the Forgejo Helm release
|
||||
(`docs/forgejo-on-railiance01.md`: S5 Helm release, ingress, operator Makefile),
|
||||
so the Forgejo side lands here. This plan is ORGREF-WP-0002-T05. The NetKingdom
|
||||
side is key-cape `KEY-WP-0038`.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- **Founder guardrail:** tighten gradually; never lock anyone out; never risk
|
||||
assets; never reach a state that is hard to repair. Every step keeps the
|
||||
current sign-in path working until the new one is proven.
|
||||
- **No visibility change** in this plan. Repositories, packages and
|
||||
`REQUIRE_SIGNIN_VIEW` are ORGREF-WP-0002-T08.
|
||||
- **Configuration in git.** The authentication source is declared in
|
||||
`helm/forgejo-values.yaml` (`gitea.oauth`), with its secret drawn from an
|
||||
ExternalSecret over OpenBao. No click-ops, and no secret value in git.
|
||||
- Open registration is already disabled (`service.DISABLE_REGISTRATION: true`).
|
||||
This plan keeps it that way.
|
||||
|
||||
## Tasks
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "98004ed0-83e6-5f6e-9b18-15b8b2aec2e7"
|
||||
```
|
||||
|
||||
### T01 — Make the local administrator a sealed break-glass
|
||||
|
||||
Before anything about sign-in changes: confirm the local administrator account
|
||||
works, keep its credential only in OpenBao, and document recovery (who, how,
|
||||
where it is logged) in `docs/forgejo-on-railiance01.md`. Verify one break-glass
|
||||
sign-in and record it without the credential.
|
||||
|
||||
Acceptance: a recorded break-glass sign-in, with the recovery path written down.
|
||||
This is the "never lock ourselves out" floor for every later step.
|
||||
|
||||
Done 2026-10-17: founder sealed `forgejo_admin` into OpenBao
|
||||
`platform/workloads/forgejo/break-glass-admin` (version 1) and verified a local
|
||||
sign-in (HTTP 200, site admin): `docs/evidence/2026-10-17-forgejo-breakglass-signin.json`.
|
||||
Recovery path: `docs/forgejo-on-railiance01.md` § Break-glass administrator.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T02
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T01, KEY-WP-0038-T02]
|
||||
blocking_reason: "Break-glass done (T01); needs the NetKingdom client registered with its secret in custody (KEY-WP-0038-T02)."
|
||||
priority: high
|
||||
state_hub_task_id: "13cd3aec-c87d-5d3f-a421-39d8ff131edd"
|
||||
```
|
||||
|
||||
### T02 — Add the NetKingdom authentication source
|
||||
|
||||
- an ExternalSecret delivering the `forgejo` client secret from OpenBao to a
|
||||
Secret in the `forgejo` namespace. Custody path is
|
||||
`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, read by ESO through
|
||||
Kubernetes-auth role `external-secrets-keycape-forgejo-forgejo`, which carries
|
||||
only policy `workload-kv-read-keycape-forgejo-forgejo` (net-kingdom 074b3a0;
|
||||
replaces the earlier `platform/workloads/forgejo/netkingdom-oidc-client` proposal). The
|
||||
SA/SecretStore/ExternalSecret shape follows the NK-WP-0048
|
||||
`deliver-consumer-secret` receipt contract
|
||||
(`docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json`)
|
||||
- `gitea.oauth`: name `netkingdom` (must match the redirect URI registered in
|
||||
key-cape), provider `openidConnect`, auto-discovery from
|
||||
`https://kc.coulomb.social/.well-known/openid-configuration`, scopes
|
||||
`openid profile email groups`, group-claim name `groups`
|
||||
- `oauth2_client` settings: no automatic account creation for unknown subjects;
|
||||
account linking to existing local accounts by username after a successful
|
||||
local sign-in (so the founder and agent accounts keep their history)
|
||||
- Helm upgrade with the previous revision noted for rollback
|
||||
|
||||
Local password sign-in stays enabled.
|
||||
|
||||
Acceptance: the "Sign in with netkingdom" option appears. Local sign-in still
|
||||
works. Rollback is one `helm rollback` to the noted revision.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T03
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T02]
|
||||
blocking_reason: "Linking and team mapping follow a working authentication source."
|
||||
priority: high
|
||||
state_hub_task_id: "81f072aa-c316-511e-a2cd-21dd1b1d0a00"
|
||||
```
|
||||
|
||||
### T03 — Link accounts and map groups to teams
|
||||
|
||||
- The founder links their account through NetKingdom (MFA), then signs in again
|
||||
through NetKingdom only and sees the same repositories, organisations and
|
||||
admin rights.
|
||||
- Map NetKingdom groups (names agreed in KEY-WP-0038-T01) to organisation teams:
|
||||
owners and readers at least. Create the read-only teams that
|
||||
ORGREF-WP-0002-T06 consumers will use.
|
||||
- Apply KEY-WP-0038-T03's decision on agents and services: NetKingdom sign-in, or
|
||||
scoped tokens bound to service identities.
|
||||
|
||||
Acceptance: founder and one agent work through NetKingdom; group-to-team mapping
|
||||
verified by adding and removing one test membership; break-glass still works.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T04
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T03]
|
||||
needs_human: true
|
||||
blocking_reason: "Restricting local password sign-in is a founder decision once NetKingdom sign-in is proven."
|
||||
priority: medium
|
||||
state_hub_task_id: "32c54737-c40f-5fde-9b91-163f8dcb0068"
|
||||
```
|
||||
|
||||
### T04 — Decide on local password sign-in
|
||||
|
||||
Once NetKingdom sign-in has been used for a while without problems, the founder
|
||||
decides whether local password sign-in is turned off for everyone except
|
||||
break-glass. This is not required to close the forge; it is the next tightening
|
||||
step, taken separately.
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T05
|
||||
status: wait
|
||||
depends_on: [RAPPS-WP-0016-T02]
|
||||
blocking_reason: "Waits for net-kingdom's answer to the Apps 1.3 needs-change record (the-custodian docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-apps.json: RAPPS-1.3-C1..C3) and the re-frozen 1.3 candidate."
|
||||
priority: medium
|
||||
state_hub_task_id: "728786c9-f1f9-5983-a6a4-dea21faf67f5"
|
||||
```
|
||||
|
||||
### T05 — Apps owner seams for NK-WP-0048 consumer steps
|
||||
|
||||
Implement the real Apps capabilities behind the `deliver-consumer-secret` and
|
||||
`hand-over` adapters, as owner-run paths (option C): `owner_apply` /
|
||||
`owner_remove` emitting `netkingdom.consumer-secret-delivery.v1` receipts from the
|
||||
T02 objects; a consumer verifier that observes Forgejo sign-in and the T03
|
||||
positive/negative group probes; and a committed-evidence hand-over receipt store.
|
||||
See the owner-review record for the contract.
|
||||
|
||||
Acceptance: receipts produced from the live T02/T03 run validate against the
|
||||
adapters' checks with no value-bearing keys.
|
||||
|
||||
## Also in scope: stale node credential
|
||||
|
||||
The k3s `registries.yaml` on railiance01 still holds a login for the frozen Gitea
|
||||
host `92.205.130.254` (ORGREF-WP-0002-T02). Coordinate its removal with the node
|
||||
owner when ORGREF-WP-0002-T06 adds the forge registry login, in the same change.
|
||||
|
||||
|
||||
## T06 — Package the reviewed consumer source through the Apps build path
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T06
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "08ab8921-7801-5309-b84b-bc2a3ceea1e4"
|
||||
```
|
||||
|
||||
Completed 2026-10-18, source build tooling only. The existing Apps Makefile now
|
||||
has explicit prepare, public-dependency acquisition, offline build, artifact
|
||||
verification and local OCI export targets for the pinned official Forgejo11.0.3
|
||||
source plus reviewed multi-value claim patch. Source/toolchains/locks/compiler
|
||||
inputs/base image are fixed. Third-party application source stays in a new /tmp
|
||||
workspace; Apps holds the release input lock, patch and build recipe.
|
||||
|
||||
The original build profile produced identical frontend assets, bindata sources
|
||||
and complete static executable in two workspaces, and identical local OCI
|
||||
archives in two no-cache exports. Independent review then found host process
|
||||
argv visibility through /proc (APPSBUILD-R1-F01). The revised recipe adds a
|
||||
private PID namespace; exact source and synthetic canary regression are separately
|
||||
reviewed. The original full builds are historical r1-profile evidence; no full
|
||||
r2 build is claimed. Original packet/review and failure history remain in
|
||||
Custodian apps-forgejo-release-build-r1 and apps-release-independent-review-r1;
|
||||
the correction is apps-forgejo-release-build-r2. This is coordinator source
|
||||
integration after independent technical review, not formal owner assent or image
|
||||
admission. Helm values, deployed image, local sign-in and visibility are unchanged.
|
||||
NK-WP-0048-T39 remains progress for the full release acceptance scope.
|
||||
|
||||
## T07 — Keep the client secret out of process arguments
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T07
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "786d6584-6db1-5a10-9324-d7b94db0df94"
|
||||
```
|
||||
|
||||
Implement and independently review a real Forgejo add/update --secret-file path
|
||||
and the actual chart helper/mount adaptation. Preserve the existing CLIENT_SECRET
|
||||
key and custody route; use no agent-value argument or environment fallback. Test
|
||||
bounded stable nofollow file reads, exact existing minted-secret encoding,
|
||||
mutual exclusion/update preservation, fixed errors and actual Helm rendering.
|
||||
Private mount identity/reader set and new package/type revisions need explicit
|
||||
owner/runtime admission. The in-progress isolated candidate is Custodian
|
||||
forgejo-private-input-r1; source completion is distinct from T02 native delivery.
|
||||
|
||||
## T08 — Provide a transactional application-source inverse and durable recovery
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T08
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "4ab8a5e7-ab32-5ee2-aaf2-6da246d1b0c8"
|
||||
```
|
||||
|
||||
Implement actual owner database primitives that record operation intent/outcome
|
||||
in the same durable database, compare complete raw persisted before/post/current
|
||||
source state privately, and serialize conflicting application writers at the DB
|
||||
boundary. Never emit stored credential/config bytes or delete/detach linked users
|
||||
or a preexisting/shared source. SQLite and PostgreSQL transaction semantics must
|
||||
be tested against the real app packages; unsupported backends refuse.
|
||||
|
||||
T05 and NK-WP-0048-T34 additionally require actual authenticated execution and
|
||||
fresh inverse sessions, admitted evidence/recovery and exclusion of every release
|
||||
writer, including Helm/runtime writers. The candidate DB primitive alone does
|
||||
not establish those facilities. Helm rollback/omission does not remove persisted
|
||||
OAuth sources: verify the exact app-state inverse before any image downgrade.
|
||||
If links prevent removal, retain the extended image and reconcile. The earlier
|
||||
T02 single-Helm-rollback statement is not sufficient recovery for this extension;
|
||||
its historical text is retained and corrected here.
|
||||
|
||||
2026-10-18 PostgreSQL/source-context continuation for T08: actual Forgejo model
|
||||
schemas, OAuth configuration types and production DB initialization run on a
|
||||
disposable PostgreSQL 16.14 instance. All 37 test/subtest results pass across
|
||||
default, explicit-public and named schemas. Separate client processes prove
|
||||
rollback after process death immediately following INSERT, committed apply
|
||||
survival through a real server restart, and subsequent reconciliation/inverse.
|
||||
Exact raw restoration, private drift, linked-user/external-link refusal, trigger
|
||||
rollback, table-writer exclusion and concurrent preparation are covered.
|
||||
Controlled copies with the table lock or private-query context removed fail the
|
||||
corresponding tests in all three schema profiles. All owned fixture containers
|
||||
and volumes were removed; no deployed database or real credential was used.
|
||||
|
||||
The diagnostics check exposed a real tracing-hook composition panic. The new
|
||||
40-path build candidate publishes the task context into XORM's shared query
|
||||
context so subsequent hooks preserve it. Actual private/ordinary SQLite traces
|
||||
and PostgreSQL driver-error hooks cover the correction. Six focused SQLite DB
|
||||
checks and all 94 selected composed package results pass without skips. The
|
||||
previous panic, fixture/compilation failures and refinement history remain in
|
||||
Custodian forgejo-postgres-source-r1. Complete release evidence uses the new
|
||||
feb10793 patch; the prior frozen artifact is historical.
|
||||
|
||||
This proves bounded source/database mechanics. T08 remains progress for current
|
||||
authenticated initiating/inverse sessions, release-wide writer exclusion, private
|
||||
evidence/custody/retention and native deployed-database acceptance. Independent
|
||||
owner review and exact package/type/runtime admission remain open. T07/T09 and
|
||||
all original task UUIDs and statuses are preserved.
|
||||
|
||||
## T09 — Enforce authenticated Access Engine decisions in the application path
|
||||
|
||||
```task
|
||||
id: RAPPS-WP-0016-T09
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "d3804638-c73d-5b9f-a456-4262f62f9e6b"
|
||||
```
|
||||
|
||||
Use the producer-owned decision format/canonical bytes and independently admitted
|
||||
signer/policy/request pins. Forgejo must verify the caller's issuer/tenant and
|
||||
assurance facts, obtain and authenticate the exact Access Engine resource/action
|
||||
decision, and refuse missing, stale, wrong-pin, unavailable or malformed replies.
|
||||
Keep the original three-group any-of access set as policy input; its local
|
||||
compatibility gate is not a substitute PDP or an architecture exception. An
|
||||
issuer allowed/denied claim cannot manufacture authorization. Source integration
|
||||
needs real provider/callback/session wiring and meaningful producer-consumer
|
||||
positive/adverse tests before re-freezing the case and package. Existing break-
|
||||
glass/local account and linking behavior remain within the declared owner scope.
|
||||
|
||||
2026-10-18 source continuation for T07/T08/T09: the digest-bound build
|
||||
candidate now composes all 37 paths, including private secret-file input, actual
|
||||
source-state transactions/private SQL hooks, the producer-owned Access Engine
|
||||
Check client from ca0cb757 and guarded OIDC callback enforcement. The parser uses
|
||||
the actual KeyCape typed assurance/authentication-time/provenance contract; its
|
||||
actual issuer fixture/callback suite passes 71 test/subtest results, and selected
|
||||
composed packages pass 93 with no skips. The previous incompatible-string-claim
|
||||
rejection is retained. All patch postimages independently replay from the pinned
|
||||
official archive. Complete release build/verification is tracked in Custodian
|
||||
forgejo-keycape-claims-r1; earlier release outputs do not prove this revision.
|
||||
|
||||
This is coordinator source integration of a tested build candidate; independent
|
||||
owner review and formal operational admission remain open. T07's chart/private
|
||||
mount admission, T08's fresh authenticated operation/inverse and release-wide
|
||||
writer exclusion, T09's ongoing HTTP/API/Git/SSH paths, private evidence/retention,
|
||||
PostgreSQL/native proof and actual issuer/factor/custody acceptance remain open.
|
||||
No trust, deployed image, Helm values, source DB, user/link state or local sign-in
|
||||
is changed by this build-source update. The preserved legacy patch is historical;
|
||||
the input lock now explicitly selects forgejo-security-extension.patch.
|
||||
|
||||
|
||||
2026-10-18 current-token callback continuation: the new 49-path source candidate
|
||||
uses the exact generated KeyCape online client, without a consumer token registry.
|
||||
Mandatory owner application-tenant/current-token admission, ID-token purpose,
|
||||
exact signed RS256 at_hash, current status before/after the PDP, and signed
|
||||
identity/assurance/authentication-time/tenant-provenance joins refuse substitution
|
||||
and stale authority. The latest focused callback/identity suite passes 91 results
|
||||
without skips; the selected four composed packages passed 114 results with one
|
||||
optional PostgreSQL test skipped before the additional owner-tenant regression.
|
||||
The actual HTTP PKCE/discovery/UserInfo/status/revocation and Forgejo provider /
|
||||
callback pass five fresh consumer processes: two current grants accepted, mixed
|
||||
pair/revoked original/suspended human refused. The original verifier and issuer
|
||||
fail their new controls. MFA session seeds and PDP admission are synthetic.
|
||||
|
||||
The user explicitly selected current KeyCape checks on every API/Git/SSH
|
||||
operation for linked accounts, keeping the separate local break-glass path.
|
||||
That full ongoing route/resource/action/group profile is still implementation
|
||||
work here; callback proof does not close it. Input lock now selects patch
|
||||
9fe3d7ba; complete release/asset/OCI checks, independent owner assent,
|
||||
software/type/package/custody admission, real factors/current sessions and native
|
||||
acceptance remain open. T07/T08 and every existing task UUID/status are preserved.
|
||||
Exact source/evidence: Custodian forgejo-current-token-r1.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"workplan_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b",
|
||||
"id": "1f779c83-05d1-465a-a0e0-e4741fd6110a",
|
||||
"topic_id": null,
|
||||
"task_id": "d3804638-c73d-5b9f-a456-4262f62f9e6b",
|
||||
"decision_id": null,
|
||||
"event_type": "note",
|
||||
"summary": "Published Apps ab3cf5cd, retaining source a927ec8c and explicit validation-document refinement. New 49-path Forgejo source candidate consumes unchanged generated KeyCape adapter and requires owner application tenant/current-token admission, ID purpose, exact at_hash, signed fact joins and current checks before/after PDP. Latest focused 91 passes, final selected four-package suite 116 passes with zero skips. Initial file-replacement fixture skip/cause retained; fresh private/read-only fixtures restore check. Five actual issuer/provider/callback consumer processes pass with synthetic MFA/PDP admission; original issuer and purpose checks fail expected controls. Exact source replay and primary/remote verified; all nine task UUID/status bindings and preexisting staged brief preserved. User selected current checks on every API/Git/SSH operation, separate local break-glass retained; ongoing resource/group/action implementation, release rebuild, independent/native admission remain open in T09/NK48-T43.",
|
||||
"detail": null,
|
||||
"author": "codex",
|
||||
"session_id": null,
|
||||
"created_at": "2026-10-18T10:27:23.748772Z",
|
||||
"workstream_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"summary": "Published Apps ab3cf5cd, retaining source a927ec8c and explicit validation-document refinement. New 49-path Forgejo source candidate consumes unchanged generated KeyCape adapter and requires owner application tenant/current-token admission, ID purpose, exact at_hash, signed fact joins and current checks before/after PDP. Latest focused 91 passes, final selected four-package suite 116 passes with zero skips. Initial file-replacement fixture skip/cause retained; fresh private/read-only fixtures restore check. Five actual issuer/provider/callback consumer processes pass with synthetic MFA/PDP admission; original issuer and purpose checks fail expected controls. Exact source replay and primary/remote verified; all nine task UUID/status bindings and preexisting staged brief preserved. User selected current checks on every API/Git/SSH operation, separate local break-glass retained; ongoing resource/group/action implementation, release rebuild, independent/native admission remain open in T09/NK48-T43.",
|
||||
"event_type": "note",
|
||||
"author": "codex",
|
||||
"workplan_id": "f6b1d6f6-0abb-5774-87b9-bf3cfb640a3b",
|
||||
"task_id": "d3804638-c73d-5b9f-a456-4262f62f9e6b"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"initial_source_revision": "git:a927ec8cc821a93688ca939e5a06eb0379f8d9c3",
|
||||
"final_revision": "git:ab3cf5cdc56d085d040256e2e84633b0289ab0af",
|
||||
"initial_postimages_exact": 4,
|
||||
"correction_postimages_exact": 2,
|
||||
"executed_correction_manifest_sha256": "sha256:b4fb2e4ed9fe46c2fa6947384a23815dcbd2e4606555bdd3dcde4093382d2e92",
|
||||
"original_command_manifest_retained_byte_exact": true,
|
||||
"published_correction_source_paths": "separate frozen correction capsule; same source bytes/digests",
|
||||
"runtime_or_patch_changed_by_correction": false
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"owner": "railiance-apps",
|
||||
"base_revision": "git:a927ec8cc821a93688ca939e5a06eb0379f8d9c3",
|
||||
"remote_base_revision": "git:308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3",
|
||||
"files": [
|
||||
{
|
||||
"path": "build/forgejo/README.md",
|
||||
"before_sha256": "sha256:3cd9ea9e0078ebe76b49337df92b00b48600524e664afe419306fe931cdb651c",
|
||||
"sha256": "sha256:bce802e2d57f699da4911a53b8a1e424bbc1bbd995eff4f1fd1011d1c6527f70",
|
||||
"source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-current-token-r1/apps-owner-source/build/forgejo/README.md"
|
||||
},
|
||||
{
|
||||
"path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md",
|
||||
"before_sha256": "sha256:48eae582342a4c62ba38140ed2f4326102c5ca627636afe36432eb2fc692f74a",
|
||||
"sha256": "sha256:3341dedc6b01d6b6755c55bafdd81a316d02369b209f7d760434d3bb15f70a4a",
|
||||
"source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-current-token-r1/apps-owner-source/workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md"
|
||||
}
|
||||
],
|
||||
"reason": "correct initial skip classification and record final 116-pass zero-skip rerun; original source commit retained",
|
||||
"runtime_source_changed": false
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"owner": "railiance-apps",
|
||||
"base_revision": "git:a927ec8cc821a93688ca939e5a06eb0379f8d9c3",
|
||||
"remote_base_revision": "git:308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3",
|
||||
"files": [
|
||||
{
|
||||
"path": "build/forgejo/README.md",
|
||||
"before_sha256": "sha256:3cd9ea9e0078ebe76b49337df92b00b48600524e664afe419306fe931cdb651c",
|
||||
"sha256": "sha256:bce802e2d57f699da4911a53b8a1e424bbc1bbd995eff4f1fd1011d1c6527f70",
|
||||
"source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-current-token-r1/apps-owner-source-correction/build/forgejo/README.md"
|
||||
},
|
||||
{
|
||||
"path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md",
|
||||
"before_sha256": "sha256:48eae582342a4c62ba38140ed2f4326102c5ca627636afe36432eb2fc692f74a",
|
||||
"sha256": "sha256:3341dedc6b01d6b6755c55bafdd81a316d02369b209f7d760434d3bb15f70a4a",
|
||||
"source": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-18/forgejo-current-token-r1/apps-owner-source-correction/workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md"
|
||||
}
|
||||
],
|
||||
"reason": "correct initial skip classification and record final 116-pass zero-skip rerun; original source commit retained",
|
||||
"runtime_source_changed": false
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"workspace": "/tmp/custodian-forgejo-current-token-r1",
|
||||
"apps_base": "git:308e83fcfbf9ec98dbe6b0286cbe8ec7be8899b3",
|
||||
"official_source": "git:b81aa4d0807cc1e2d6a2fbafd0b75c46cc0ae237",
|
||||
"base_patch_sha256": "sha256:feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c",
|
||||
"all_base_source_postimages_exact": 40,
|
||||
"keycape_producer": "git:08c083bae9ff35fbaf028c36d72a95b57d2541b7",
|
||||
"generated_adapter_files_exact": 5,
|
||||
"real_credentials": false,
|
||||
"native_admission": false
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compiled_test_binary": "sha256:8052b5598371a595388c32c46ed9815349b3285f3ed864ec3dece75fafa8852e",
|
||||
"exact_pinned_runtime_and_export_files": 26,
|
||||
"latest_focused_results": 91,
|
||||
"newest_change": "one owner-tenant negative test; production and generated code unchanged",
|
||||
"complete_release_build": false,
|
||||
"native_admission": false
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,261 @@
|
|||
{
|
||||
"official_archive_sha256": "sha256:1ddd8c926ca07d40436897971458ace7dcf1d3496a0813ab9e0d869edef2365c",
|
||||
"base_patch_sha256": "sha256:feb1079399a43989742ae0a946c1ada50113d1821e6541f97f0b5592ee1fc20c",
|
||||
"patch_sha256": "sha256:9fe3d7baa648c9347964a177befab5070e00151d1ec5ddff16c41890db84e62a",
|
||||
"files": [
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth.go",
|
||||
"before_sha256": "sha256:d5faee30ffbb9a560256c92a0e05e652b79e95e016448cbac7dbb6ad0fdad937",
|
||||
"after_sha256": "sha256:d718d817c46f6393488a9c5a894b7535640cd0ec1a1ae3f72f8411a2bbec3b38"
|
||||
},
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth_secret_linux.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:6741961f1df67c1afbf34c796e549425f0e04a0d0678c6eedb05f66d4754c64b"
|
||||
},
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth_secret_linux_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:b28c2369dfab9bf2ca48e707e01b4394a36c8be5490cfe5bb658017bb626c4b2"
|
||||
},
|
||||
{
|
||||
"path": "cmd/admin_auth_oauth_secret_other.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:54056c989a4757e73db0252130e7c16bf16f5ec8eaaca2468f019ba2ef5c532d"
|
||||
},
|
||||
{
|
||||
"path": "go.mod",
|
||||
"before_sha256": "sha256:652162a4a95c6d1c349d2a8c0b514def5abd837f001137b77b7b3f4a0f162d00",
|
||||
"after_sha256": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:ef1b07ef6cf454015d00cde8588835f5118d829da91eb0303d37184859345a31"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_internal_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:af0f424d3a1fd2b29793b64baa1fa07284fee7ea0b7721c503db4efac0d871cb"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_postgres_export_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:b9d1553144766b15c408295b87506106935abf4ed61460598b58dc81825e808d"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_postgres_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:4a37504e5af00580d114ef1bed5cb32e09e4ec2de5fd3192adef043a4ea5f7fe"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_process_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:40d33b11a01cb659f0bf6f8b545c4d79910e8c082a789060bcbc9cadeee965bd"
|
||||
},
|
||||
{
|
||||
"path": "models/auth/source_operation_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:77d311bca42f48faf3fbdc15c53582e3d8a6d9108a72ed7bb6172e5b88951428"
|
||||
},
|
||||
{
|
||||
"path": "models/db/context.go",
|
||||
"before_sha256": "sha256:10774e272206c7a8d6ebfb106b37d535de77f7e96ba57657b4dad960639f8519",
|
||||
"after_sha256": "sha256:d4c4acfcb8987071aecf5cd957b3821c8d2619a2696398d37c2970f2527686f7"
|
||||
},
|
||||
{
|
||||
"path": "models/db/engine.go",
|
||||
"before_sha256": "sha256:774cedc74e1a8d9ae6f9e5f106c6cbb66f12c11b42e99ef07a7affd03269c3d2",
|
||||
"after_sha256": "sha256:d21a5b0b9ca37ac48222b79007467a232abc38edda99b3c5f54f1f4c5ab795e8"
|
||||
},
|
||||
{
|
||||
"path": "models/db/sensitive_sql.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:06a298d0d6e628e6920384d4ddeef449e40c090841ebaa3f6d175725a45ae584"
|
||||
},
|
||||
{
|
||||
"path": "models/db/sensitive_sql_logging_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:d924b35d30686473caf29601cf51cc363ba68158cb4ef0981c9e2fc7b03c04b7"
|
||||
},
|
||||
{
|
||||
"path": "models/db/sensitive_sql_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:de6353c955047c3311747e4777b9f9b2195e9efb19160b971dd758dfc94cf31b"
|
||||
},
|
||||
{
|
||||
"path": "models/db/tracing_context_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:612b60c67a2be7e2da9173b95d5ef5abd7b9e1e377360828e39a622610bba070"
|
||||
},
|
||||
{
|
||||
"path": "routers/web/admin/auths.go",
|
||||
"before_sha256": "sha256:eeed6999e4655993b55873bb51efb4fcd946532255b9473d16e086e04a1ec911",
|
||||
"after_sha256": "sha256:3498229e06fb9ff52396bcb5e68c0cc2d2ad24e6494533d8717d7c833f3e2923"
|
||||
},
|
||||
{
|
||||
"path": "routers/web/auth/oauth.go",
|
||||
"before_sha256": "sha256:6f454b3550b150178a8597a9437adb0c626556f34c15a13778c15d675b05659b",
|
||||
"after_sha256": "sha256:58bff778e0dc17a521706289bf45c3e0d76a759a0874f76c379b3d1ddb419b17"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:d16330043825d0284253e65d28bda2de4edbc633e4887d8c3618a106ee251daa"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_assurance.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_assurance_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_azp_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_callback_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_current_token.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_current_token_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_purpose_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:533dc3153fcf9bfcf7bfc08712c057092c7b3892882f217fb8f839fdbdd99939"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/access_engine_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:9112a142983c0a58c5d40344b58a20fa2c1bdbc7a79b5c586796362839a43b48"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/current_issuer_fixture_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:24fb430e72848326c5d9ef42eab5028cf915dd601f43fb06010b32b568908781"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/providers_openid.go",
|
||||
"before_sha256": "sha256:6ddca7fb4ab8b16e6b096481cfb40ca5abdf3a327f38c895b95b3846cc7c2248",
|
||||
"after_sha256": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/required_claim_values.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/required_claim_values_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/source.go",
|
||||
"before_sha256": "sha256:a6ee5146c07d8efb1837ef91f1ab5f3139f69a1ff66f814d5e9c6053bf9b0d8b",
|
||||
"after_sha256": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642"
|
||||
},
|
||||
{
|
||||
"path": "services/auth/source/oauth2/source_callout.go",
|
||||
"before_sha256": "sha256:c30c418182d4cb98b1e448f621c93322accfbbf92d6a59e6df1ae45a7aa8fc7b",
|
||||
"after_sha256": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/LICENSE",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/go.mod",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/internal/sign/sign.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/api/canonical.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/api/caring.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/api/resource_manifest.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/decisionpep/client.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/pkg/decisionpep/client_test.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/producer-source.json",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:c1d83e3d79f9860ffaa61ec19a6b3415e914d3058b11b07cdd05a7d1182cdceb"
|
||||
},
|
||||
{
|
||||
"path": "third_party/access-engine-pep/testsupport/signfixture/sign.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/LICENSE",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/go.mod",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/internal/jose/jose.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/pkg/tokenstatus/client.go",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575"
|
||||
},
|
||||
{
|
||||
"path": "third_party/keycape-tokenstatus/producer-source.json",
|
||||
"before_sha256": null,
|
||||
"after_sha256": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
}
|
||||
],
|
||||
"exact_replay": true,
|
||||
"fuzz": 0,
|
||||
"native_admission": false,
|
||||
"open_gates": [
|
||||
"Ongoing current identity checks on HTTP/API/Git/SSH selected by user; integration remains required.",
|
||||
"Actual resource/action PDP coverage and current group checks.",
|
||||
"Fresh current protected operation/inverse, exact effects/approvals, journal custody and every-release-writer exclusion.",
|
||||
"Complete release build, independent owner review, profile/package/type/custody admission and native acceptance."
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
{
|
||||
"returncode": 1,
|
||||
"seconds": 12.1,
|
||||
"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-current-token-r1",
|
||||
"/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/modules",
|
||||
"/work/modules",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/gocache",
|
||||
"/work/gocache",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/go",
|
||||
"/work/go",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/writable-input",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"1",
|
||||
"-json",
|
||||
"-tags",
|
||||
"sqlite sqlite_unlock_notify",
|
||||
"./services/auth/source/oauth2",
|
||||
"-run",
|
||||
"^TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims$",
|
||||
"-count=1"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"go.mod": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "sha256:19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"services/auth/source/oauth2/access_engine.go": "sha256:d5f07f9491ac2212410e1fa8c0248d68ba3a723e7ca99e363627c5292d187aa8",
|
||||
"services/auth/source/oauth2/access_engine_assurance.go": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642",
|
||||
"services/auth/source/oauth2/access_engine_assurance_test.go": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3",
|
||||
"services/auth/source/oauth2/access_engine_azp_test.go": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce",
|
||||
"services/auth/source/oauth2/access_engine_callback_test.go": "sha256:5cea36b630c895a9473dc876b7128526ddef65d9395b2fddc56343e162452472",
|
||||
"services/auth/source/oauth2/access_engine_purpose_test.go": "sha256:bf492f6384d7da0f84321f138d4efab5c77508c667c288c2807ad3cccc10383b",
|
||||
"services/auth/source/oauth2/access_engine_test.go": "sha256:99edd6cfe3e834d73e0447a7915f52cc4ab7b82644669c5ae61963f850b447e8",
|
||||
"services/auth/source/oauth2/assert_interface_test.go": "sha256:a0db85e38b7ea61e21b0291e55c8b451c89a877bd3db229e35dbb4418458f54f",
|
||||
"services/auth/source/oauth2/init.go": "sha256:98a8030ed005be67b237d54c3b197a629f8c289e3d74c8edfcf4d740831f87d1",
|
||||
"services/auth/source/oauth2/jwtsigningkey.go": "sha256:0c6e8ac131fd41ad299073ca748dd95e5fd9c044219c4d7f36d50c4ffcd5b266",
|
||||
"services/auth/source/oauth2/jwtsigningkey_test.go": "sha256:3e414a541479741773e48de84be7d5a3b794bcc7289627fbd2e8996832628321",
|
||||
"services/auth/source/oauth2/providers.go": "sha256:7df0051f19932a68d16a33a5d41898e06e9612f839195e27c86802eeebb2e3e9",
|
||||
"services/auth/source/oauth2/providers_base.go": "sha256:643dbc0561ec7aa7a975978ff2c21d256a5ab619b125e8077d2395281f628542",
|
||||
"services/auth/source/oauth2/providers_custom.go": "sha256:265a526d209d86c24cbcfdde4c2d73957aaeaccfd329438c11ce99c5951ebb39",
|
||||
"services/auth/source/oauth2/providers_openid.go": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf",
|
||||
"services/auth/source/oauth2/providers_simple.go": "sha256:53927b6bfc621dd1b68a074b6763abe5b27795d5a50ca6f0555f3e7a04fa1613",
|
||||
"services/auth/source/oauth2/required_claim_values.go": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda",
|
||||
"services/auth/source/oauth2/required_claim_values_test.go": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0",
|
||||
"services/auth/source/oauth2/source.go": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642",
|
||||
"services/auth/source/oauth2/source_authenticate.go": "sha256:c6551f00ef4349ccd6afdbb2cba847067528485ff621c3e507b9aab588bb96b5",
|
||||
"services/auth/source/oauth2/source_callout.go": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c",
|
||||
"services/auth/source/oauth2/source_name.go": "sha256:712943826e01f718ab4ca717df6c5f2171535cb2229dbf8460f542d757ef8d7f",
|
||||
"services/auth/source/oauth2/source_register.go": "sha256:5ad9ccf213881734dc7fe7619cbe6c47cb6a4afdbbfc3e674ab4493857ebbf91",
|
||||
"services/auth/source/oauth2/store.go": "sha256:35b244081569215384de07f8cdc1e83b5545a2e673ca1ac5a2a6ee686cb917a3",
|
||||
"services/auth/source/oauth2/token.go": "sha256:c0a8868f904bb31842202b4e635a76403902adff9e638d5cb581e7e2d4954933",
|
||||
"services/auth/source/oauth2/urlmapping.go": "sha256:f2132408d4a447e766e67495b1ca7b8cc2b47d4eb56b0c44b50ba01fe41ca83e",
|
||||
"third_party/keycape-tokenstatus/LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"third_party/keycape-tokenstatus/go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"third_party/keycape-tokenstatus/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"third_party/keycape-tokenstatus/pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575",
|
||||
"third_party/keycape-tokenstatus/producer-source.json": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 0,
|
||||
"fail": 3,
|
||||
"skip": 0
|
||||
},
|
||||
"failures": [
|
||||
"forgejo.org/services/auth/source/oauth2:TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti",
|
||||
"forgejo.org/services/auth/source/oauth2:TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope",
|
||||
"forgejo.org/services/auth/source/oauth2:TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims"
|
||||
],
|
||||
"real_credentials": false,
|
||||
"native_admission": false,
|
||||
"network": "private empty namespace",
|
||||
"pid_namespace": "private",
|
||||
"home": "empty allowlisted fixture HOME"
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{"Time":"2026-10-18T09:51:33.456606536Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"}
|
||||
{"Time":"2026-10-18T09:51:33.53889052Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims"}
|
||||
{"Time":"2026-10-18T09:51:33.538988645Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims\n"}
|
||||
{"Time":"2026-10-18T09:51:33.539004281Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti"}
|
||||
{"Time":"2026-10-18T09:51:33.539010693Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti\n"}
|
||||
{"Time":"2026-10-18T09:51:33.541219549Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":" access_engine_purpose_test.go:11: access-token grant fields were accepted as an ID token\n"}
|
||||
{"Time":"2026-10-18T09:51:33.541272137Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"--- FAIL: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:51:33.5412801Z","Action":"fail","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:51:33.541296159Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope"}
|
||||
{"Time":"2026-10-18T09:51:33.541304176Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope\n"}
|
||||
{"Time":"2026-10-18T09:51:33.541541645Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":" access_engine_purpose_test.go:11: access-token grant fields were accepted as an ID token\n"}
|
||||
{"Time":"2026-10-18T09:51:33.541568742Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"--- FAIL: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:51:33.541578426Z","Action":"fail","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:51:33.541588012Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"--- FAIL: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:51:33.541595794Z","Action":"fail","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:51:33.541602575Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"FAIL\n"}
|
||||
{"Time":"2026-10-18T09:51:33.547443935Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"FAIL\tforgejo.org/services/auth/source/oauth2\t0.090s\n"}
|
||||
{"Time":"2026-10-18T09:51:33.54749646Z","Action":"fail","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":0.091}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 16.16,
|
||||
"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-current-token-r1",
|
||||
"/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/modules",
|
||||
"/work/modules",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/gocache",
|
||||
"/work/gocache",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/go",
|
||||
"/work/go",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/writable-input",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"1",
|
||||
"-json",
|
||||
"-tags",
|
||||
"sqlite sqlite_unlock_notify",
|
||||
"./services/auth/source/oauth2",
|
||||
"-run",
|
||||
"TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent",
|
||||
"-count=1"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"go.mod": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "sha256:19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"services/auth/source/oauth2/access_engine.go": "sha256:896b5be0a865fad41aaa92dc68f36e62fca70029da2f0d693855292ab0415e3e",
|
||||
"services/auth/source/oauth2/access_engine_assurance.go": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642",
|
||||
"services/auth/source/oauth2/access_engine_assurance_test.go": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3",
|
||||
"services/auth/source/oauth2/access_engine_azp_test.go": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce",
|
||||
"services/auth/source/oauth2/access_engine_callback_test.go": "sha256:0dc1471854dab6f659adb662863a9bd241cd50655f0f0d0ee17b53a04992a939",
|
||||
"services/auth/source/oauth2/access_engine_current_token.go": "sha256:6e0052ee3d090f68136221ce77cd3c236790ff9dd2e4c082f6fc035660d09df1",
|
||||
"services/auth/source/oauth2/access_engine_current_token_test.go": "sha256:d3dd4c99178fc31e867c58aadc2afe9ccb3976001e9a26b352288c6148e1d91c",
|
||||
"services/auth/source/oauth2/access_engine_purpose_test.go": "sha256:bf492f6384d7da0f84321f138d4efab5c77508c667c288c2807ad3cccc10383b",
|
||||
"services/auth/source/oauth2/access_engine_test.go": "sha256:99edd6cfe3e834d73e0447a7915f52cc4ab7b82644669c5ae61963f850b447e8",
|
||||
"services/auth/source/oauth2/assert_interface_test.go": "sha256:a0db85e38b7ea61e21b0291e55c8b451c89a877bd3db229e35dbb4418458f54f",
|
||||
"services/auth/source/oauth2/init.go": "sha256:98a8030ed005be67b237d54c3b197a629f8c289e3d74c8edfcf4d740831f87d1",
|
||||
"services/auth/source/oauth2/jwtsigningkey.go": "sha256:0c6e8ac131fd41ad299073ca748dd95e5fd9c044219c4d7f36d50c4ffcd5b266",
|
||||
"services/auth/source/oauth2/jwtsigningkey_test.go": "sha256:3e414a541479741773e48de84be7d5a3b794bcc7289627fbd2e8996832628321",
|
||||
"services/auth/source/oauth2/providers.go": "sha256:7df0051f19932a68d16a33a5d41898e06e9612f839195e27c86802eeebb2e3e9",
|
||||
"services/auth/source/oauth2/providers_base.go": "sha256:643dbc0561ec7aa7a975978ff2c21d256a5ab619b125e8077d2395281f628542",
|
||||
"services/auth/source/oauth2/providers_custom.go": "sha256:265a526d209d86c24cbcfdde4c2d73957aaeaccfd329438c11ce99c5951ebb39",
|
||||
"services/auth/source/oauth2/providers_openid.go": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf",
|
||||
"services/auth/source/oauth2/providers_simple.go": "sha256:53927b6bfc621dd1b68a074b6763abe5b27795d5a50ca6f0555f3e7a04fa1613",
|
||||
"services/auth/source/oauth2/required_claim_values.go": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda",
|
||||
"services/auth/source/oauth2/required_claim_values_test.go": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0",
|
||||
"services/auth/source/oauth2/source.go": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642",
|
||||
"services/auth/source/oauth2/source_authenticate.go": "sha256:c6551f00ef4349ccd6afdbb2cba847067528485ff621c3e507b9aab588bb96b5",
|
||||
"services/auth/source/oauth2/source_callout.go": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c",
|
||||
"services/auth/source/oauth2/source_name.go": "sha256:712943826e01f718ab4ca717df6c5f2171535cb2229dbf8460f542d757ef8d7f",
|
||||
"services/auth/source/oauth2/source_register.go": "sha256:5ad9ccf213881734dc7fe7619cbe6c47cb6a4afdbbfc3e674ab4493857ebbf91",
|
||||
"services/auth/source/oauth2/store.go": "sha256:35b244081569215384de07f8cdc1e83b5545a2e673ca1ac5a2a6ee686cb917a3",
|
||||
"services/auth/source/oauth2/token.go": "sha256:c0a8868f904bb31842202b4e635a76403902adff9e638d5cb581e7e2d4954933",
|
||||
"services/auth/source/oauth2/urlmapping.go": "sha256:f2132408d4a447e766e67495b1ca7b8cc2b47d4eb56b0c44b50ba01fe41ca83e",
|
||||
"third_party/keycape-tokenstatus/LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"third_party/keycape-tokenstatus/go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"third_party/keycape-tokenstatus/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"third_party/keycape-tokenstatus/pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575",
|
||||
"third_party/keycape-tokenstatus/producer-source.json": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 88,
|
||||
"fail": 0,
|
||||
"skip": 0
|
||||
},
|
||||
"failures": [],
|
||||
"real_credentials": false,
|
||||
"native_admission": false,
|
||||
"network": "private empty namespace",
|
||||
"pid_namespace": "private",
|
||||
"home": "empty allowlisted fixture HOME"
|
||||
}
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
{"Time":"2026-10-18T09:53:43.654562185Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"}
|
||||
{"Time":"2026-10-18T09:53:43.73834898Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals"}
|
||||
{"Time":"2026-10-18T09:53:43.738399486Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals\n"}
|
||||
{"Time":"2026-10-18T09:53:43.738501994Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods"}
|
||||
{"Time":"2026-10-18T09:53:43.738512057Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods\n"}
|
||||
{"Time":"2026-10-18T09:53:43.740608304Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.740661343Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.740678503Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field"}
|
||||
{"Time":"2026-10-18T09:53:43.740686014Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field\n"}
|
||||
{"Time":"2026-10-18T09:53:43.740988185Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.741028519Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.741045913Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at"}
|
||||
{"Time":"2026-10-18T09:53:43.74106769Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/missing-at\n"}
|
||||
{"Time":"2026-10-18T09:53:43.741196718Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/missing-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.741226889Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.741241624Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at"}
|
||||
{"Time":"2026-10-18T09:53:43.741250208Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/string-at\n"}
|
||||
{"Time":"2026-10-18T09:53:43.741571123Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/string-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.741593612Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.741601547Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at"}
|
||||
{"Time":"2026-10-18T09:53:43.741628202Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/fractional-at\n"}
|
||||
{"Time":"2026-10-18T09:53:43.741910559Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/fractional-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.74193367Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.741940686Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa"}
|
||||
{"Time":"2026-10-18T09:53:43.741946006Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa\n"}
|
||||
{"Time":"2026-10-18T09:53:43.74227941Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.742307039Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.742317742Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level"}
|
||||
{"Time":"2026-10-18T09:53:43.742325082Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-level\n"}
|
||||
{"Time":"2026-10-18T09:53:43.742563435Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-level (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.742581461Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.742588314Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods"}
|
||||
{"Time":"2026-10-18T09:53:43.742593598Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/object-methods\n"}
|
||||
{"Time":"2026-10-18T09:53:43.742918102Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/object-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.742940582Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.742947922Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods"}
|
||||
{"Time":"2026-10-18T09:53:43.742953224Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/empty-methods\n"}
|
||||
{"Time":"2026-10-18T09:53:43.743239575Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/empty-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.743257637Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.743284418Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods"}
|
||||
{"Time":"2026-10-18T09:53:43.743291551Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods\n"}
|
||||
{"Time":"2026-10-18T09:53:43.743523087Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.743548948Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.743556315Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication"}
|
||||
{"Time":"2026-10-18T09:53:43.743561617Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication\n"}
|
||||
{"Time":"2026-10-18T09:53:43.743872348Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.743899544Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.743907785Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication"}
|
||||
{"Time":"2026-10-18T09:53:43.743913006Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/future-authentication\n"}
|
||||
{"Time":"2026-10-18T09:53:43.744198635Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/future-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.744231584Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.744240214Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa"}
|
||||
{"Time":"2026-10-18T09:53:43.744246328Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa\n"}
|
||||
{"Time":"2026-10-18T09:53:43.744483391Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.744500452Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.744507918Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source"}
|
||||
{"Time":"2026-10-18T09:53:43.744513437Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-source\n"}
|
||||
{"Time":"2026-10-18T09:53:43.744786659Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-source (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.744803945Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.745320733Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals (0.01s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.745341978Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T09:53:43.745357983Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds"}
|
||||
{"Time":"2026-10-18T09:53:43.745364043Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"=== RUN TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds\n"}
|
||||
{"Time":"2026-10-18T09:53:43.745776474Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"--- PASS: TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.745805068Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.745812182Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact"}
|
||||
{"Time":"2026-10-18T09:53:43.745816628Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"=== RUN TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact\n"}
|
||||
{"Time":"2026-10-18T09:53:43.746149081Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"--- PASS: TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.746170091Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.746177296Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient"}
|
||||
{"Time":"2026-10-18T09:53:43.746182571Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient\n"}
|
||||
{"Time":"2026-10-18T09:53:43.746189208Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client"}
|
||||
{"Time":"2026-10-18T09:53:43.746194303Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client\n"}
|
||||
{"Time":"2026-10-18T09:53:43.74656826Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.746589424Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.746597097Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number"}
|
||||
{"Time":"2026-10-18T09:53:43.746621083Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/number\n"}
|
||||
{"Time":"2026-10-18T09:53:43.746834314Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/number (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.74685133Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.746858616Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array"}
|
||||
{"Time":"2026-10-18T09:53:43.746864603Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/array\n"}
|
||||
{"Time":"2026-10-18T09:53:43.747141902Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/array (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.747157204Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.747163949Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty"}
|
||||
{"Time":"2026-10-18T09:53:43.747169233Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty\n"}
|
||||
{"Time":"2026-10-18T09:53:43.747411635Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.74743223Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.747440346Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.747446874Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.747452726Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient"}
|
||||
{"Time":"2026-10-18T09:53:43.747457812Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"=== RUN TestAccessEngineAuthorizedPartyAcceptsAdmittedClient\n"}
|
||||
{"Time":"2026-10-18T09:53:43.747955925Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyAcceptsAdmittedClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:43.747975609Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:43.747982436Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision"}
|
||||
{"Time":"2026-10-18T09:53:43.748001173Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision\n"}
|
||||
{"Time":"2026-10-18T09:53:43.748007774Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins"}
|
||||
{"Time":"2026-10-18T09:53:43.748013103Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins\n"}
|
||||
{"Time":"2026-10-18T09:53:44.006435868Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins (0.26s)\n"}
|
||||
{"Time":"2026-10-18T09:53:44.006488555Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Elapsed":0.26}
|
||||
{"Time":"2026-10-18T09:53:44.006837234Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-18T09:53:44.00686049Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-18T09:53:44.395447072Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners (0.39s)\n"}
|
||||
{"Time":"2026-10-18T09:53:44.395507966Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Elapsed":0.39}
|
||||
{"Time":"2026-10-18T09:53:44.395529905Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-18T09:53:44.395534918Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-18T09:53:44.687427704Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers (0.29s)\n"}
|
||||
{"Time":"2026-10-18T09:53:44.687472664Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Elapsed":0.29}
|
||||
{"Time":"2026-10-18T09:53:44.687482493Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision (0.94s)\n"}
|
||||
{"Time":"2026-10-18T09:53:44.687487822Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Elapsed":0.94}
|
||||
{"Time":"2026-10-18T09:53:44.687492989Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP"}
|
||||
{"Time":"2026-10-18T09:53:44.687497182Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP\n"}
|
||||
{"Time":"2026-10-18T09:53:44.687516876Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny"}
|
||||
{"Time":"2026-10-18T09:53:44.687521159Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny\n"}
|
||||
{"Time":"2026-10-18T09:53:45.100747655Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny (0.41s)\n"}
|
||||
{"Time":"2026-10-18T09:53:45.100792561Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Elapsed":0.41}
|
||||
{"Time":"2026-10-18T09:53:45.10080064Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned"}
|
||||
{"Time":"2026-10-18T09:53:45.100806141Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned\n"}
|
||||
{"Time":"2026-10-18T09:53:45.584505856Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned (0.48s)\n"}
|
||||
{"Time":"2026-10-18T09:53:45.584555911Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Elapsed":0.48}
|
||||
{"Time":"2026-10-18T09:53:45.584565027Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key"}
|
||||
{"Time":"2026-10-18T09:53:45.584569969Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key\n"}
|
||||
{"Time":"2026-10-18T09:53:45.756640557Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key (0.17s)\n"}
|
||||
{"Time":"2026-10-18T09:53:45.756703524Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Elapsed":0.17}
|
||||
{"Time":"2026-10-18T09:53:45.756719521Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin"}
|
||||
{"Time":"2026-10-18T09:53:45.756723732Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin\n"}
|
||||
{"Time":"2026-10-18T09:53:46.09718194Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin (0.34s)\n"}
|
||||
{"Time":"2026-10-18T09:53:46.097251066Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Elapsed":0.34}
|
||||
{"Time":"2026-10-18T09:53:46.097272067Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding"}
|
||||
{"Time":"2026-10-18T09:53:46.097280129Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding\n"}
|
||||
{"Time":"2026-10-18T09:53:46.453092909Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding (0.36s)\n"}
|
||||
{"Time":"2026-10-18T09:53:46.453197446Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Elapsed":0.36}
|
||||
{"Time":"2026-10-18T09:53:46.453216761Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired"}
|
||||
{"Time":"2026-10-18T09:53:46.453224885Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired\n"}
|
||||
{"Time":"2026-10-18T09:53:46.716353049Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired (0.26s)\n"}
|
||||
{"Time":"2026-10-18T09:53:46.716405972Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Elapsed":0.26}
|
||||
{"Time":"2026-10-18T09:53:46.716417938Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP (2.03s)\n"}
|
||||
{"Time":"2026-10-18T09:53:46.716428028Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Elapsed":2.03}
|
||||
{"Time":"2026-10-18T09:53:46.716434556Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP"}
|
||||
{"Time":"2026-10-18T09:53:46.716440048Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP\n"}
|
||||
{"Time":"2026-10-18T09:53:46.716545757Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source"}
|
||||
{"Time":"2026-10-18T09:53:46.716558643Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source\n"}
|
||||
{"Time":"2026-10-18T09:53:46.786931237Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source (0.07s)\n"}
|
||||
{"Time":"2026-10-18T09:53:46.786985611Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Elapsed":0.07}
|
||||
{"Time":"2026-10-18T09:53:46.786997181Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience"}
|
||||
{"Time":"2026-10-18T09:53:46.787004025Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience\n"}
|
||||
{"Time":"2026-10-18T09:53:46.905975097Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience (0.12s)\n"}
|
||||
{"Time":"2026-10-18T09:53:46.906028136Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Elapsed":0.12}
|
||||
{"Time":"2026-10-18T09:53:46.90605599Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token"}
|
||||
{"Time":"2026-10-18T09:53:46.906063781Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token\n"}
|
||||
{"Time":"2026-10-18T09:53:47.115155772Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token (0.21s)\n"}
|
||||
{"Time":"2026-10-18T09:53:47.115211508Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Elapsed":0.21}
|
||||
{"Time":"2026-10-18T09:53:47.115224312Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token"}
|
||||
{"Time":"2026-10-18T09:53:47.115232563Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token\n"}
|
||||
{"Time":"2026-10-18T09:53:47.386174997Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token (0.27s)\n"}
|
||||
{"Time":"2026-10-18T09:53:47.386227433Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Elapsed":0.27}
|
||||
{"Time":"2026-10-18T09:53:47.386238145Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user"}
|
||||
{"Time":"2026-10-18T09:53:47.38624774Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user\n"}
|
||||
{"Time":"2026-10-18T09:53:47.591265206Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user (0.21s)\n"}
|
||||
{"Time":"2026-10-18T09:53:47.591314954Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Elapsed":0.21}
|
||||
{"Time":"2026-10-18T09:53:47.591325432Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject"}
|
||||
{"Time":"2026-10-18T09:53:47.591331978Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject\n"}
|
||||
{"Time":"2026-10-18T09:53:47.972378643Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject (0.38s)\n"}
|
||||
{"Time":"2026-10-18T09:53:47.972437625Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Elapsed":0.38}
|
||||
{"Time":"2026-10-18T09:53:47.972451133Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups"}
|
||||
{"Time":"2026-10-18T09:53:47.972474569Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups\n"}
|
||||
{"Time":"2026-10-18T09:53:48.116564299Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups (0.14s)\n"}
|
||||
{"Time":"2026-10-18T09:53:48.116619827Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Elapsed":0.14}
|
||||
{"Time":"2026-10-18T09:53:48.116632848Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider"}
|
||||
{"Time":"2026-10-18T09:53:48.116640324Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider\n"}
|
||||
{"Time":"2026-10-18T09:53:48.451621047Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider (0.33s)\n"}
|
||||
{"Time":"2026-10-18T09:53:48.451699065Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Elapsed":0.33}
|
||||
{"Time":"2026-10-18T09:53:48.451735359Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission"}
|
||||
{"Time":"2026-10-18T09:53:48.451749011Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission\n"}
|
||||
{"Time":"2026-10-18T09:53:48.487589584Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission (0.04s)\n"}
|
||||
{"Time":"2026-10-18T09:53:48.487642398Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Elapsed":0.04}
|
||||
{"Time":"2026-10-18T09:53:48.487654382Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity"}
|
||||
{"Time":"2026-10-18T09:53:48.487661289Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity\n"}
|
||||
{"Time":"2026-10-18T09:53:48.803153699Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity (0.32s)\n"}
|
||||
{"Time":"2026-10-18T09:53:48.803208317Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Elapsed":0.32}
|
||||
{"Time":"2026-10-18T09:53:48.803221644Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP (2.09s)\n"}
|
||||
{"Time":"2026-10-18T09:53:48.803229958Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Elapsed":2.09}
|
||||
{"Time":"2026-10-18T09:53:48.803289562Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision"}
|
||||
{"Time":"2026-10-18T09:53:48.803298246Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision\n"}
|
||||
{"Time":"2026-10-18T09:53:48.803306587Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing"}
|
||||
{"Time":"2026-10-18T09:53:48.803312737Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing\n"}
|
||||
{"Time":"2026-10-18T09:53:49.027724667Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing (0.22s)\n"}
|
||||
{"Time":"2026-10-18T09:53:49.027806976Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Elapsed":0.22}
|
||||
{"Time":"2026-10-18T09:53:49.027825218Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access"}
|
||||
{"Time":"2026-10-18T09:53:49.027835886Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access\n"}
|
||||
{"Time":"2026-10-18T09:53:49.704792262Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access (0.68s)\n"}
|
||||
{"Time":"2026-10-18T09:53:49.704845257Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Elapsed":0.68}
|
||||
{"Time":"2026-10-18T09:53:49.704896736Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject"}
|
||||
{"Time":"2026-10-18T09:53:49.704912489Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/subject\n"}
|
||||
{"Time":"2026-10-18T09:53:50.106158612Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/subject (0.40s)\n"}
|
||||
{"Time":"2026-10-18T09:53:50.106212761Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Elapsed":0.4}
|
||||
{"Time":"2026-10-18T09:53:50.10622525Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant"}
|
||||
{"Time":"2026-10-18T09:53:50.106232382Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant\n"}
|
||||
{"Time":"2026-10-18T09:53:50.388799147Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant (0.28s)\n"}
|
||||
{"Time":"2026-10-18T09:53:50.38885124Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Elapsed":0.28}
|
||||
{"Time":"2026-10-18T09:53:50.388876235Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal"}
|
||||
{"Time":"2026-10-18T09:53:50.388882674Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/principal\n"}
|
||||
{"Time":"2026-10-18T09:53:50.600904666Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/principal (0.21s)\n"}
|
||||
{"Time":"2026-10-18T09:53:50.600962413Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Elapsed":0.21}
|
||||
{"Time":"2026-10-18T09:53:50.600975443Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance"}
|
||||
{"Time":"2026-10-18T09:53:50.600983495Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance\n"}
|
||||
{"Time":"2026-10-18T09:53:50.865187766Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance (0.26s)\n"}
|
||||
{"Time":"2026-10-18T09:53:50.865235738Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Elapsed":0.26}
|
||||
{"Time":"2026-10-18T09:53:50.865244899Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time"}
|
||||
{"Time":"2026-10-18T09:53:50.865250138Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time\n"}
|
||||
{"Time":"2026-10-18T09:53:51.275873984Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time (0.41s)\n"}
|
||||
{"Time":"2026-10-18T09:53:51.275919061Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Elapsed":0.41}
|
||||
{"Time":"2026-10-18T09:53:51.275926617Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source"}
|
||||
{"Time":"2026-10-18T09:53:51.275930549Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source\n"}
|
||||
{"Time":"2026-10-18T09:53:51.457577292Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source (0.18s)\n"}
|
||||
{"Time":"2026-10-18T09:53:51.457639187Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Elapsed":0.18}
|
||||
{"Time":"2026-10-18T09:53:51.457652316Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at"}
|
||||
{"Time":"2026-10-18T09:53:51.457660125Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at\n"}
|
||||
{"Time":"2026-10-18T09:53:51.800451766Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at (0.34s)\n"}
|
||||
{"Time":"2026-10-18T09:53:51.800557169Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Elapsed":0.34}
|
||||
{"Time":"2026-10-18T09:53:51.800576989Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive"}
|
||||
{"Time":"2026-10-18T09:53:51.800585537Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive\n"}
|
||||
{"Time":"2026-10-18T09:53:52.052231669Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive (0.25s)\n"}
|
||||
{"Time":"2026-10-18T09:53:52.052279659Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Elapsed":0.25}
|
||||
{"Time":"2026-10-18T09:53:52.05229002Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable"}
|
||||
{"Time":"2026-10-18T09:53:52.05229342Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable\n"}
|
||||
{"Time":"2026-10-18T09:53:52.464961499Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable (0.41s)\n"}
|
||||
{"Time":"2026-10-18T09:53:52.465011202Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Elapsed":0.41}
|
||||
{"Time":"2026-10-18T09:53:52.465021626Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status"}
|
||||
{"Time":"2026-10-18T09:53:52.46502746Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status\n"}
|
||||
{"Time":"2026-10-18T09:53:52.912239967Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status (0.45s)\n"}
|
||||
{"Time":"2026-10-18T09:53:52.912291311Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Elapsed":0.45}
|
||||
{"Time":"2026-10-18T09:53:52.912301537Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission"}
|
||||
{"Time":"2026-10-18T09:53:52.912307109Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission\n"}
|
||||
{"Time":"2026-10-18T09:53:53.532045087Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission (0.62s)\n"}
|
||||
{"Time":"2026-10-18T09:53:53.532170553Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Elapsed":0.62}
|
||||
{"Time":"2026-10-18T09:53:53.53219104Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client"}
|
||||
{"Time":"2026-10-18T09:53:53.532198308Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client\n"}
|
||||
{"Time":"2026-10-18T09:53:53.733177444Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client (0.20s)\n"}
|
||||
{"Time":"2026-10-18T09:53:53.733230367Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Elapsed":0.2}
|
||||
{"Time":"2026-10-18T09:53:53.73324235Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key"}
|
||||
{"Time":"2026-10-18T09:53:53.733249095Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key\n"}
|
||||
{"Time":"2026-10-18T09:53:53.91638578Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key (0.18s)\n"}
|
||||
{"Time":"2026-10-18T09:53:53.916443139Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Elapsed":0.18}
|
||||
{"Time":"2026-10-18T09:53:53.916457161Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision (5.11s)\n"}
|
||||
{"Time":"2026-10-18T09:53:53.916466566Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Elapsed":5.11}
|
||||
{"Time":"2026-10-18T09:53:53.916475367Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn"}
|
||||
{"Time":"2026-10-18T09:53:53.916482364Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"=== RUN TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn\n"}
|
||||
{"Time":"2026-10-18T09:53:54.640660044Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"--- PASS: TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn (0.72s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.640714021Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Elapsed":0.72}
|
||||
{"Time":"2026-10-18T09:53:54.640724724Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims"}
|
||||
{"Time":"2026-10-18T09:53:54.640730468Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims\n"}
|
||||
{"Time":"2026-10-18T09:53:54.64073651Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti"}
|
||||
{"Time":"2026-10-18T09:53:54.640741451Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti\n"}
|
||||
{"Time":"2026-10-18T09:53:54.641134495Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.641153783Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.641163287Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope"}
|
||||
{"Time":"2026-10-18T09:53:54.641168409Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope\n"}
|
||||
{"Time":"2026-10-18T09:53:54.641607982Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.641622833Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.641629876Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.641635476Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.641643032Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE"}
|
||||
{"Time":"2026-10-18T09:53:54.641648533Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE\n"}
|
||||
{"Time":"2026-10-18T09:53:54.642092524Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience"}
|
||||
{"Time":"2026-10-18T09:53:54.642106257Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/audience\n"}
|
||||
{"Time":"2026-10-18T09:53:54.642377027Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/audience (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.642390661Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.642398118Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired"}
|
||||
{"Time":"2026-10-18T09:53:54.642402961Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/expired\n"}
|
||||
{"Time":"2026-10-18T09:53:54.642673271Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/expired (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.642685597Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.642704281Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued"}
|
||||
{"Time":"2026-10-18T09:53:54.642708934Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/future-issued\n"}
|
||||
{"Time":"2026-10-18T09:53:54.642991508Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/future-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.64300369Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.64301075Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued"}
|
||||
{"Time":"2026-10-18T09:53:54.643018316Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/old-issued\n"}
|
||||
{"Time":"2026-10-18T09:53:54.643287796Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/old-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.643300168Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.643307219Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp"}
|
||||
{"Time":"2026-10-18T09:53:54.643311376Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-exp\n"}
|
||||
{"Time":"2026-10-18T09:53:54.64358927Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-exp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.643623761Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.643632535Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant"}
|
||||
{"Time":"2026-10-18T09:53:54.643636791Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-tenant\n"}
|
||||
{"Time":"2026-10-18T09:53:54.643904811Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-tenant (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.643930861Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.643936867Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance"}
|
||||
{"Time":"2026-10-18T09:53:54.643940943Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-assurance\n"}
|
||||
{"Time":"2026-10-18T09:53:54.644211866Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-assurance (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.644245554Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.644254058Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject"}
|
||||
{"Time":"2026-10-18T09:53:54.644258476Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/empty-subject\n"}
|
||||
{"Time":"2026-10-18T09:53:54.644507109Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/empty-subject (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.644519507Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.644526631Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer"}
|
||||
{"Time":"2026-10-18T09:53:54.644530761Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/issuer\n"}
|
||||
{"Time":"2026-10-18T09:53:54.644799601Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/issuer (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.644812279Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.644819313Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat"}
|
||||
{"Time":"2026-10-18T09:53:54.644823452Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-iat\n"}
|
||||
{"Time":"2026-10-18T09:53:54.64512507Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-iat (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.645137514Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.645144673Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal"}
|
||||
{"Time":"2026-10-18T09:53:54.64514883Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-principal\n"}
|
||||
{"Time":"2026-10-18T09:53:54.645475398Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-principal (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.645504127Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.64551446Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp"}
|
||||
{"Time":"2026-10-18T09:53:54.645521097Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp\n"}
|
||||
{"Time":"2026-10-18T09:53:54.645808631Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.645828758Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.645836855Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key"}
|
||||
{"Time":"2026-10-18T09:53:54.645841229Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-key\n"}
|
||||
{"Time":"2026-10-18T09:53:54.646196048Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.646216878Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.646288728Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key"}
|
||||
{"Time":"2026-10-18T09:53:54.646299521Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/unknown-key\n"}
|
||||
{"Time":"2026-10-18T09:53:54.646532527Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/unknown-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.646550769Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.646559408Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type"}
|
||||
{"Time":"2026-10-18T09:53:54.646563835Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-type\n"}
|
||||
{"Time":"2026-10-18T09:53:54.646815335Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-type (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.64683979Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.646848275Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion"}
|
||||
{"Time":"2026-10-18T09:53:54.646852495Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647011235Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647026573Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.647053544Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none"}
|
||||
{"Time":"2026-10-18T09:53:54.647058593Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/none\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647073472Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/none (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647174709Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.647191796Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE (0.01s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647199488Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T09:53:54.647287292Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation"}
|
||||
{"Time":"2026-10-18T09:53:54.647336868Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"=== RUN TestRequiredClaimExactAnyOfAndNoMutation\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647349997Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"--- PASS: TestRequiredClaimExactAnyOfAndNoMutation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647364335Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.64743567Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation"}
|
||||
{"Time":"2026-10-18T09:53:54.647448826Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"=== RUN TestRequiredClaimConfigurationRefusalsAndLegacyIsolation\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647458114Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"--- PASS: TestRequiredClaimConfigurationRefusalsAndLegacyIsolation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.647463046Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.647514733Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable"}
|
||||
{"Time":"2026-10-18T09:53:54.647524896Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"=== RUN TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable\n"}
|
||||
{"Time":"2026-10-18T09:53:54.650207859Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"--- PASS: TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.65026804Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.650279393Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval"}
|
||||
{"Time":"2026-10-18T09:53:54.650285903Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"=== RUN TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval\n"}
|
||||
{"Time":"2026-10-18T09:53:54.650317049Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"--- PASS: TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.650325669Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.650342423Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse"}
|
||||
{"Time":"2026-10-18T09:53:54.650348771Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"=== RUN TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse\n"}
|
||||
{"Time":"2026-10-18T09:53:54.65039273Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"--- PASS: TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:53:54.650429584Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:53:54.65045421Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T09:53:54.658262068Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"ok \tforgejo.org/services/auth/source/oauth2\t11.003s\n"}
|
||||
{"Time":"2026-10-18T09:53:54.658318119Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":11.004}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 16.66,
|
||||
"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-current-token-r1",
|
||||
"/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/modules",
|
||||
"/work/modules",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/gocache",
|
||||
"/work/gocache",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/go",
|
||||
"/work/go",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/writable-input",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"1",
|
||||
"-json",
|
||||
"-tags",
|
||||
"sqlite sqlite_unlock_notify",
|
||||
"./services/auth/source/oauth2",
|
||||
"-run",
|
||||
"TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent",
|
||||
"-count=1"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"go.mod": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "sha256:19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"services/auth/source/oauth2/access_engine.go": "sha256:ac845a1373cd7017dc475ca9d2b53885c2283df67336021bd51dfe02da24179c",
|
||||
"services/auth/source/oauth2/access_engine_assurance.go": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642",
|
||||
"services/auth/source/oauth2/access_engine_assurance_test.go": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3",
|
||||
"services/auth/source/oauth2/access_engine_azp_test.go": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce",
|
||||
"services/auth/source/oauth2/access_engine_callback_test.go": "sha256:1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580",
|
||||
"services/auth/source/oauth2/access_engine_current_token.go": "sha256:4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8",
|
||||
"services/auth/source/oauth2/access_engine_current_token_test.go": "sha256:58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1",
|
||||
"services/auth/source/oauth2/access_engine_purpose_test.go": "sha256:bf492f6384d7da0f84321f138d4efab5c77508c667c288c2807ad3cccc10383b",
|
||||
"services/auth/source/oauth2/access_engine_test.go": "sha256:99edd6cfe3e834d73e0447a7915f52cc4ab7b82644669c5ae61963f850b447e8",
|
||||
"services/auth/source/oauth2/assert_interface_test.go": "sha256:a0db85e38b7ea61e21b0291e55c8b451c89a877bd3db229e35dbb4418458f54f",
|
||||
"services/auth/source/oauth2/init.go": "sha256:98a8030ed005be67b237d54c3b197a629f8c289e3d74c8edfcf4d740831f87d1",
|
||||
"services/auth/source/oauth2/jwtsigningkey.go": "sha256:0c6e8ac131fd41ad299073ca748dd95e5fd9c044219c4d7f36d50c4ffcd5b266",
|
||||
"services/auth/source/oauth2/jwtsigningkey_test.go": "sha256:3e414a541479741773e48de84be7d5a3b794bcc7289627fbd2e8996832628321",
|
||||
"services/auth/source/oauth2/providers.go": "sha256:7df0051f19932a68d16a33a5d41898e06e9612f839195e27c86802eeebb2e3e9",
|
||||
"services/auth/source/oauth2/providers_base.go": "sha256:643dbc0561ec7aa7a975978ff2c21d256a5ab619b125e8077d2395281f628542",
|
||||
"services/auth/source/oauth2/providers_custom.go": "sha256:265a526d209d86c24cbcfdde4c2d73957aaeaccfd329438c11ce99c5951ebb39",
|
||||
"services/auth/source/oauth2/providers_openid.go": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf",
|
||||
"services/auth/source/oauth2/providers_simple.go": "sha256:53927b6bfc621dd1b68a074b6763abe5b27795d5a50ca6f0555f3e7a04fa1613",
|
||||
"services/auth/source/oauth2/required_claim_values.go": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda",
|
||||
"services/auth/source/oauth2/required_claim_values_test.go": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0",
|
||||
"services/auth/source/oauth2/source.go": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642",
|
||||
"services/auth/source/oauth2/source_authenticate.go": "sha256:c6551f00ef4349ccd6afdbb2cba847067528485ff621c3e507b9aab588bb96b5",
|
||||
"services/auth/source/oauth2/source_callout.go": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c",
|
||||
"services/auth/source/oauth2/source_name.go": "sha256:712943826e01f718ab4ca717df6c5f2171535cb2229dbf8460f542d757ef8d7f",
|
||||
"services/auth/source/oauth2/source_register.go": "sha256:5ad9ccf213881734dc7fe7619cbe6c47cb6a4afdbbfc3e674ab4493857ebbf91",
|
||||
"services/auth/source/oauth2/store.go": "sha256:35b244081569215384de07f8cdc1e83b5545a2e673ca1ac5a2a6ee686cb917a3",
|
||||
"services/auth/source/oauth2/token.go": "sha256:c0a8868f904bb31842202b4e635a76403902adff9e638d5cb581e7e2d4954933",
|
||||
"services/auth/source/oauth2/urlmapping.go": "sha256:f2132408d4a447e766e67495b1ca7b8cc2b47d4eb56b0c44b50ba01fe41ca83e",
|
||||
"third_party/keycape-tokenstatus/LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"third_party/keycape-tokenstatus/go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"third_party/keycape-tokenstatus/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"third_party/keycape-tokenstatus/pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575",
|
||||
"third_party/keycape-tokenstatus/producer-source.json": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 90,
|
||||
"fail": 0,
|
||||
"skip": 0
|
||||
},
|
||||
"failures": [],
|
||||
"real_credentials": false,
|
||||
"native_admission": false,
|
||||
"network": "private empty namespace",
|
||||
"pid_namespace": "private",
|
||||
"home": "empty allowlisted fixture HOME"
|
||||
}
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
{"Time":"2026-10-18T09:58:37.45062086Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"}
|
||||
{"Time":"2026-10-18T09:58:37.536556468Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals"}
|
||||
{"Time":"2026-10-18T09:58:37.536627317Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals\n"}
|
||||
{"Time":"2026-10-18T09:58:37.536767229Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source"}
|
||||
{"Time":"2026-10-18T09:58:37.536785344Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-source\n"}
|
||||
{"Time":"2026-10-18T09:58:37.538905524Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-source (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.538941286Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.538954154Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level"}
|
||||
{"Time":"2026-10-18T09:58:37.538961692Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-level\n"}
|
||||
{"Time":"2026-10-18T09:58:37.539208377Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-level (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.539230524Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.539240758Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods"}
|
||||
{"Time":"2026-10-18T09:58:37.539247061Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/object-methods\n"}
|
||||
{"Time":"2026-10-18T09:58:37.539565614Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/object-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.539583206Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.539596705Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods"}
|
||||
{"Time":"2026-10-18T09:58:37.539603522Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/empty-methods\n"}
|
||||
{"Time":"2026-10-18T09:58:37.53988551Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/empty-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.539903733Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.539917061Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods"}
|
||||
{"Time":"2026-10-18T09:58:37.539942968Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods\n"}
|
||||
{"Time":"2026-10-18T09:58:37.540199256Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.54021922Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.54023381Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication"}
|
||||
{"Time":"2026-10-18T09:58:37.5402528Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication\n"}
|
||||
{"Time":"2026-10-18T09:58:37.540567484Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.540584319Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.540594734Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication"}
|
||||
{"Time":"2026-10-18T09:58:37.540609369Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/future-authentication\n"}
|
||||
{"Time":"2026-10-18T09:58:37.540966948Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/future-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.540994018Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.541006137Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at"}
|
||||
{"Time":"2026-10-18T09:58:37.541011953Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/missing-at\n"}
|
||||
{"Time":"2026-10-18T09:58:37.541309991Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/missing-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.541326592Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.541333499Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at"}
|
||||
{"Time":"2026-10-18T09:58:37.541338937Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/string-at\n"}
|
||||
{"Time":"2026-10-18T09:58:37.541601735Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/string-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.541634459Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.541643684Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at"}
|
||||
{"Time":"2026-10-18T09:58:37.541649085Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/fractional-at\n"}
|
||||
{"Time":"2026-10-18T09:58:37.541949324Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/fractional-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.541977079Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.541984987Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods"}
|
||||
{"Time":"2026-10-18T09:58:37.541995177Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods\n"}
|
||||
{"Time":"2026-10-18T09:58:37.542246478Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.542281897Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.542293025Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field"}
|
||||
{"Time":"2026-10-18T09:58:37.542298336Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field\n"}
|
||||
{"Time":"2026-10-18T09:58:37.542554804Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.542569899Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.542578096Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa"}
|
||||
{"Time":"2026-10-18T09:58:37.542583524Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa\n"}
|
||||
{"Time":"2026-10-18T09:58:37.542870056Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.54288553Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.542892113Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa"}
|
||||
{"Time":"2026-10-18T09:58:37.542897577Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa\n"}
|
||||
{"Time":"2026-10-18T09:58:37.543190259Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.543205318Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.543659922Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals (0.01s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.543676812Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T09:58:37.54368802Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds"}
|
||||
{"Time":"2026-10-18T09:58:37.543695108Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"=== RUN TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds\n"}
|
||||
{"Time":"2026-10-18T09:58:37.544141949Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"--- PASS: TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.544163672Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.544171255Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact"}
|
||||
{"Time":"2026-10-18T09:58:37.544176891Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"=== RUN TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact\n"}
|
||||
{"Time":"2026-10-18T09:58:37.544598312Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"--- PASS: TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.544656419Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.544678539Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient"}
|
||||
{"Time":"2026-10-18T09:58:37.54468724Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient\n"}
|
||||
{"Time":"2026-10-18T09:58:37.544698052Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client"}
|
||||
{"Time":"2026-10-18T09:58:37.544704021Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client\n"}
|
||||
{"Time":"2026-10-18T09:58:37.545309169Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.54534421Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.545356076Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number"}
|
||||
{"Time":"2026-10-18T09:58:37.545391415Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/number\n"}
|
||||
{"Time":"2026-10-18T09:58:37.545977166Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/number (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.546018294Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.546027257Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array"}
|
||||
{"Time":"2026-10-18T09:58:37.546033587Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/array\n"}
|
||||
{"Time":"2026-10-18T09:58:37.546411816Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/array (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.546435459Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.546442646Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty"}
|
||||
{"Time":"2026-10-18T09:58:37.546448092Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty\n"}
|
||||
{"Time":"2026-10-18T09:58:37.546763624Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.546785031Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.546793074Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.546799585Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.546805743Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient"}
|
||||
{"Time":"2026-10-18T09:58:37.546811009Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"=== RUN TestAccessEngineAuthorizedPartyAcceptsAdmittedClient\n"}
|
||||
{"Time":"2026-10-18T09:58:37.547333343Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyAcceptsAdmittedClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:37.547360584Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:37.547369366Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision"}
|
||||
{"Time":"2026-10-18T09:58:37.547392153Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision\n"}
|
||||
{"Time":"2026-10-18T09:58:37.547406238Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins"}
|
||||
{"Time":"2026-10-18T09:58:37.547412045Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins\n"}
|
||||
{"Time":"2026-10-18T09:58:38.025832897Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins (0.48s)\n"}
|
||||
{"Time":"2026-10-18T09:58:38.02588012Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Elapsed":0.48}
|
||||
{"Time":"2026-10-18T09:58:38.02589086Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-18T09:58:38.025895242Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-18T09:58:38.617849139Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners (0.59s)\n"}
|
||||
{"Time":"2026-10-18T09:58:38.617891998Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Elapsed":0.59}
|
||||
{"Time":"2026-10-18T09:58:38.61790382Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-18T09:58:38.617907463Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-18T09:58:38.753330459Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers (0.14s)\n"}
|
||||
{"Time":"2026-10-18T09:58:38.753381154Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Elapsed":0.14}
|
||||
{"Time":"2026-10-18T09:58:38.753392291Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision (1.21s)\n"}
|
||||
{"Time":"2026-10-18T09:58:38.753399838Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Elapsed":1.21}
|
||||
{"Time":"2026-10-18T09:58:38.753407124Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP"}
|
||||
{"Time":"2026-10-18T09:58:38.753412697Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP\n"}
|
||||
{"Time":"2026-10-18T09:58:38.753438522Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny"}
|
||||
{"Time":"2026-10-18T09:58:38.753444104Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny\n"}
|
||||
{"Time":"2026-10-18T09:58:38.966863993Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny (0.21s)\n"}
|
||||
{"Time":"2026-10-18T09:58:38.966906383Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Elapsed":0.21}
|
||||
{"Time":"2026-10-18T09:58:38.966913904Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned"}
|
||||
{"Time":"2026-10-18T09:58:38.966917862Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned\n"}
|
||||
{"Time":"2026-10-18T09:58:39.087701691Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned (0.12s)\n"}
|
||||
{"Time":"2026-10-18T09:58:39.087768112Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Elapsed":0.12}
|
||||
{"Time":"2026-10-18T09:58:39.087788374Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key"}
|
||||
{"Time":"2026-10-18T09:58:39.087795633Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key\n"}
|
||||
{"Time":"2026-10-18T09:58:39.347725726Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key (0.26s)\n"}
|
||||
{"Time":"2026-10-18T09:58:39.347771326Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Elapsed":0.26}
|
||||
{"Time":"2026-10-18T09:58:39.347781534Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin"}
|
||||
{"Time":"2026-10-18T09:58:39.347785853Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin\n"}
|
||||
{"Time":"2026-10-18T09:58:39.635769444Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin (0.29s)\n"}
|
||||
{"Time":"2026-10-18T09:58:39.635832132Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Elapsed":0.29}
|
||||
{"Time":"2026-10-18T09:58:39.635846677Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding"}
|
||||
{"Time":"2026-10-18T09:58:39.635853828Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding\n"}
|
||||
{"Time":"2026-10-18T09:58:40.276369484Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding (0.64s)\n"}
|
||||
{"Time":"2026-10-18T09:58:40.276414246Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Elapsed":0.64}
|
||||
{"Time":"2026-10-18T09:58:40.276422488Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired"}
|
||||
{"Time":"2026-10-18T09:58:40.276430964Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired\n"}
|
||||
{"Time":"2026-10-18T09:58:40.74500589Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired (0.47s)\n"}
|
||||
{"Time":"2026-10-18T09:58:40.745058605Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Elapsed":0.47}
|
||||
{"Time":"2026-10-18T09:58:40.745072158Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP (1.99s)\n"}
|
||||
{"Time":"2026-10-18T09:58:40.745080833Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Elapsed":1.99}
|
||||
{"Time":"2026-10-18T09:58:40.745093078Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP"}
|
||||
{"Time":"2026-10-18T09:58:40.745100806Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP\n"}
|
||||
{"Time":"2026-10-18T09:58:40.745108759Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user"}
|
||||
{"Time":"2026-10-18T09:58:40.745115414Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user\n"}
|
||||
{"Time":"2026-10-18T09:58:40.92587352Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user (0.18s)\n"}
|
||||
{"Time":"2026-10-18T09:58:40.925939572Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Elapsed":0.18}
|
||||
{"Time":"2026-10-18T09:58:40.925956686Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups"}
|
||||
{"Time":"2026-10-18T09:58:40.925965442Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups\n"}
|
||||
{"Time":"2026-10-18T09:58:41.162049521Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups (0.24s)\n"}
|
||||
{"Time":"2026-10-18T09:58:41.162099323Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Elapsed":0.24}
|
||||
{"Time":"2026-10-18T09:58:41.16212724Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source"}
|
||||
{"Time":"2026-10-18T09:58:41.162134752Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source\n"}
|
||||
{"Time":"2026-10-18T09:58:41.275350242Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source (0.11s)\n"}
|
||||
{"Time":"2026-10-18T09:58:41.275401928Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Elapsed":0.11}
|
||||
{"Time":"2026-10-18T09:58:41.275413371Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience"}
|
||||
{"Time":"2026-10-18T09:58:41.275420441Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience\n"}
|
||||
{"Time":"2026-10-18T09:58:41.373726607Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience (0.10s)\n"}
|
||||
{"Time":"2026-10-18T09:58:41.373779674Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Elapsed":0.1}
|
||||
{"Time":"2026-10-18T09:58:41.373790287Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token"}
|
||||
{"Time":"2026-10-18T09:58:41.373797239Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token\n"}
|
||||
{"Time":"2026-10-18T09:58:41.400130968Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token (0.03s)\n"}
|
||||
{"Time":"2026-10-18T09:58:41.400198183Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Elapsed":0.03}
|
||||
{"Time":"2026-10-18T09:58:41.400211375Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity"}
|
||||
{"Time":"2026-10-18T09:58:41.400222926Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity\n"}
|
||||
{"Time":"2026-10-18T09:58:41.926269875Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity (0.53s)\n"}
|
||||
{"Time":"2026-10-18T09:58:41.92632241Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Elapsed":0.53}
|
||||
{"Time":"2026-10-18T09:58:41.926332347Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject"}
|
||||
{"Time":"2026-10-18T09:58:41.926355557Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject\n"}
|
||||
{"Time":"2026-10-18T09:58:42.411601029Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject (0.49s)\n"}
|
||||
{"Time":"2026-10-18T09:58:42.41166057Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Elapsed":0.49}
|
||||
{"Time":"2026-10-18T09:58:42.411672085Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider"}
|
||||
{"Time":"2026-10-18T09:58:42.411679037Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider\n"}
|
||||
{"Time":"2026-10-18T09:58:42.891560595Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider (0.48s)\n"}
|
||||
{"Time":"2026-10-18T09:58:42.891610307Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Elapsed":0.48}
|
||||
{"Time":"2026-10-18T09:58:42.891620695Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission"}
|
||||
{"Time":"2026-10-18T09:58:42.891636917Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission\n"}
|
||||
{"Time":"2026-10-18T09:58:43.053469024Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission (0.16s)\n"}
|
||||
{"Time":"2026-10-18T09:58:43.053521838Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Elapsed":0.16}
|
||||
{"Time":"2026-10-18T09:58:43.053536897Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token"}
|
||||
{"Time":"2026-10-18T09:58:43.053544192Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token\n"}
|
||||
{"Time":"2026-10-18T09:58:43.507997548Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token (0.45s)\n"}
|
||||
{"Time":"2026-10-18T09:58:43.508045917Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Elapsed":0.45}
|
||||
{"Time":"2026-10-18T09:58:43.508058234Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP (2.76s)\n"}
|
||||
{"Time":"2026-10-18T09:58:43.50807268Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Elapsed":2.76}
|
||||
{"Time":"2026-10-18T09:58:43.508093888Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision"}
|
||||
{"Time":"2026-10-18T09:58:43.508100354Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision\n"}
|
||||
{"Time":"2026-10-18T09:58:43.508110868Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing"}
|
||||
{"Time":"2026-10-18T09:58:43.508116413Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing\n"}
|
||||
{"Time":"2026-10-18T09:58:43.706699832Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing (0.20s)\n"}
|
||||
{"Time":"2026-10-18T09:58:43.706757299Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Elapsed":0.2}
|
||||
{"Time":"2026-10-18T09:58:43.706771077Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access"}
|
||||
{"Time":"2026-10-18T09:58:43.706777317Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access\n"}
|
||||
{"Time":"2026-10-18T09:58:44.713937979Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access (1.01s)\n"}
|
||||
{"Time":"2026-10-18T09:58:44.713982678Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Elapsed":1.01}
|
||||
{"Time":"2026-10-18T09:58:44.713993408Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject"}
|
||||
{"Time":"2026-10-18T09:58:44.713998314Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/subject\n"}
|
||||
{"Time":"2026-10-18T09:58:44.980513474Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/subject (0.27s)\n"}
|
||||
{"Time":"2026-10-18T09:58:44.980557036Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Elapsed":0.27}
|
||||
{"Time":"2026-10-18T09:58:44.980565269Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant"}
|
||||
{"Time":"2026-10-18T09:58:44.980569859Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant\n"}
|
||||
{"Time":"2026-10-18T09:58:45.392707481Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant (0.41s)\n"}
|
||||
{"Time":"2026-10-18T09:58:45.39275043Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Elapsed":0.41}
|
||||
{"Time":"2026-10-18T09:58:45.392770818Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal"}
|
||||
{"Time":"2026-10-18T09:58:45.392775453Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/principal\n"}
|
||||
{"Time":"2026-10-18T09:58:45.484306902Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/principal (0.09s)\n"}
|
||||
{"Time":"2026-10-18T09:58:45.484499321Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Elapsed":0.09}
|
||||
{"Time":"2026-10-18T09:58:45.484509645Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance"}
|
||||
{"Time":"2026-10-18T09:58:45.484514839Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance\n"}
|
||||
{"Time":"2026-10-18T09:58:45.725947057Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance (0.24s)\n"}
|
||||
{"Time":"2026-10-18T09:58:45.72599383Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Elapsed":0.24}
|
||||
{"Time":"2026-10-18T09:58:45.726003451Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time"}
|
||||
{"Time":"2026-10-18T09:58:45.72601209Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time\n"}
|
||||
{"Time":"2026-10-18T09:58:45.971587025Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time (0.25s)\n"}
|
||||
{"Time":"2026-10-18T09:58:45.971639605Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Elapsed":0.25}
|
||||
{"Time":"2026-10-18T09:58:45.971651913Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source"}
|
||||
{"Time":"2026-10-18T09:58:45.971658379Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source\n"}
|
||||
{"Time":"2026-10-18T09:58:46.193451086Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source (0.22s)\n"}
|
||||
{"Time":"2026-10-18T09:58:46.193492962Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Elapsed":0.22}
|
||||
{"Time":"2026-10-18T09:58:46.193503368Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at"}
|
||||
{"Time":"2026-10-18T09:58:46.193508292Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at\n"}
|
||||
{"Time":"2026-10-18T09:58:46.534895389Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at (0.34s)\n"}
|
||||
{"Time":"2026-10-18T09:58:46.534949619Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Elapsed":0.34}
|
||||
{"Time":"2026-10-18T09:58:46.534960385Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive"}
|
||||
{"Time":"2026-10-18T09:58:46.534967184Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive\n"}
|
||||
{"Time":"2026-10-18T09:58:46.697529474Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive (0.16s)\n"}
|
||||
{"Time":"2026-10-18T09:58:46.697637627Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Elapsed":0.16}
|
||||
{"Time":"2026-10-18T09:58:46.697688267Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable"}
|
||||
{"Time":"2026-10-18T09:58:46.697728656Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable\n"}
|
||||
{"Time":"2026-10-18T09:58:46.897859053Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable (0.20s)\n"}
|
||||
{"Time":"2026-10-18T09:58:46.897914374Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Elapsed":0.2}
|
||||
{"Time":"2026-10-18T09:58:46.897925789Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status"}
|
||||
{"Time":"2026-10-18T09:58:46.89793267Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status\n"}
|
||||
{"Time":"2026-10-18T09:58:47.133344699Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status (0.24s)\n"}
|
||||
{"Time":"2026-10-18T09:58:47.133394357Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Elapsed":0.24}
|
||||
{"Time":"2026-10-18T09:58:47.13340992Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission"}
|
||||
{"Time":"2026-10-18T09:58:47.133416629Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission\n"}
|
||||
{"Time":"2026-10-18T09:58:47.395932851Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission (0.26s)\n"}
|
||||
{"Time":"2026-10-18T09:58:47.395990976Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Elapsed":0.26}
|
||||
{"Time":"2026-10-18T09:58:47.39600039Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client"}
|
||||
{"Time":"2026-10-18T09:58:47.39600626Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client\n"}
|
||||
{"Time":"2026-10-18T09:58:47.551597522Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client (0.16s)\n"}
|
||||
{"Time":"2026-10-18T09:58:47.551686179Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Elapsed":0.16}
|
||||
{"Time":"2026-10-18T09:58:47.551699741Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key"}
|
||||
{"Time":"2026-10-18T09:58:47.551704575Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key\n"}
|
||||
{"Time":"2026-10-18T09:58:47.726988817Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key (0.18s)\n"}
|
||||
{"Time":"2026-10-18T09:58:47.72703833Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Elapsed":0.18}
|
||||
{"Time":"2026-10-18T09:58:47.727048051Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant"}
|
||||
{"Time":"2026-10-18T09:58:47.727054174Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant\n"}
|
||||
{"Time":"2026-10-18T09:58:48.064860713Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant (0.34s)\n"}
|
||||
{"Time":"2026-10-18T09:58:48.064901462Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Elapsed":0.34}
|
||||
{"Time":"2026-10-18T09:58:48.064911327Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash"}
|
||||
{"Time":"2026-10-18T09:58:48.064915394Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash\n"}
|
||||
{"Time":"2026-10-18T09:58:48.243746078Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash (0.18s)\n"}
|
||||
{"Time":"2026-10-18T09:58:48.243796917Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Elapsed":0.18}
|
||||
{"Time":"2026-10-18T09:58:48.243811894Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision (4.74s)\n"}
|
||||
{"Time":"2026-10-18T09:58:48.243834897Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Elapsed":4.74}
|
||||
{"Time":"2026-10-18T09:58:48.243843365Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn"}
|
||||
{"Time":"2026-10-18T09:58:48.243849217Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"=== RUN TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn\n"}
|
||||
{"Time":"2026-10-18T09:58:48.998762292Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"--- PASS: TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn (0.75s)\n"}
|
||||
{"Time":"2026-10-18T09:58:48.998818226Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Elapsed":0.75}
|
||||
{"Time":"2026-10-18T09:58:48.998829127Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims"}
|
||||
{"Time":"2026-10-18T09:58:48.998833474Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims\n"}
|
||||
{"Time":"2026-10-18T09:58:48.998937126Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti"}
|
||||
{"Time":"2026-10-18T09:58:48.998955846Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti\n"}
|
||||
{"Time":"2026-10-18T09:58:48.99944807Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:48.99946477Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:48.999474085Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope"}
|
||||
{"Time":"2026-10-18T09:58:48.999479072Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope\n"}
|
||||
{"Time":"2026-10-18T09:58:48.999951999Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:48.999969331Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:48.999977158Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:48.999982911Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:48.999992388Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE"}
|
||||
{"Time":"2026-10-18T09:58:49.000015616Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE\n"}
|
||||
{"Time":"2026-10-18T09:58:49.000441862Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer"}
|
||||
{"Time":"2026-10-18T09:58:49.000461492Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/issuer\n"}
|
||||
{"Time":"2026-10-18T09:58:49.000842526Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/issuer (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.000886647Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.00089561Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience"}
|
||||
{"Time":"2026-10-18T09:58:49.000899686Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/audience\n"}
|
||||
{"Time":"2026-10-18T09:58:49.001272494Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/audience (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.001290881Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.001299213Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired"}
|
||||
{"Time":"2026-10-18T09:58:49.001307427Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/expired\n"}
|
||||
{"Time":"2026-10-18T09:58:49.001671841Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/expired (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.001691093Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.001699921Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued"}
|
||||
{"Time":"2026-10-18T09:58:49.001704691Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/future-issued\n"}
|
||||
{"Time":"2026-10-18T09:58:49.002152984Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/future-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.002172127Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.002187466Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp"}
|
||||
{"Time":"2026-10-18T09:58:49.002192317Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-exp\n"}
|
||||
{"Time":"2026-10-18T09:58:49.002621078Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-exp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.002639518Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.002650114Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat"}
|
||||
{"Time":"2026-10-18T09:58:49.002655245Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-iat\n"}
|
||||
{"Time":"2026-10-18T09:58:49.003012301Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-iat (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.003025556Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.003033599Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal"}
|
||||
{"Time":"2026-10-18T09:58:49.003038153Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-principal\n"}
|
||||
{"Time":"2026-10-18T09:58:49.003403442Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-principal (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.003416237Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.003495625Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance"}
|
||||
{"Time":"2026-10-18T09:58:49.003506581Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-assurance\n"}
|
||||
{"Time":"2026-10-18T09:58:49.003856378Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-assurance (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.003869444Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.003877298Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued"}
|
||||
{"Time":"2026-10-18T09:58:49.003881951Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/old-issued\n"}
|
||||
{"Time":"2026-10-18T09:58:49.004269783Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/old-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.004287258Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.00429688Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant"}
|
||||
{"Time":"2026-10-18T09:58:49.004316141Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-tenant\n"}
|
||||
{"Time":"2026-10-18T09:58:49.004847635Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-tenant (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.004889656Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.004899503Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp"}
|
||||
{"Time":"2026-10-18T09:58:49.00490421Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp\n"}
|
||||
{"Time":"2026-10-18T09:58:49.005633795Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.005659756Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.005761227Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject"}
|
||||
{"Time":"2026-10-18T09:58:49.00577707Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/empty-subject\n"}
|
||||
{"Time":"2026-10-18T09:58:49.006380045Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/empty-subject (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.00642184Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.006436664Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key"}
|
||||
{"Time":"2026-10-18T09:58:49.006443301Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-key\n"}
|
||||
{"Time":"2026-10-18T09:58:49.007106546Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.007138449Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.007233185Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key"}
|
||||
{"Time":"2026-10-18T09:58:49.007244817Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/unknown-key\n"}
|
||||
{"Time":"2026-10-18T09:58:49.007592279Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/unknown-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.007606877Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.0076409Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type"}
|
||||
{"Time":"2026-10-18T09:58:49.007646851Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-type\n"}
|
||||
{"Time":"2026-10-18T09:58:49.007988722Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-type (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.008003718Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.008091933Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion"}
|
||||
{"Time":"2026-10-18T09:58:49.008109526Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion\n"}
|
||||
{"Time":"2026-10-18T09:58:49.008333839Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.00834961Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.008443453Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none"}
|
||||
{"Time":"2026-10-18T09:58:49.008454724Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/none\n"}
|
||||
{"Time":"2026-10-18T09:58:49.008737262Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/none (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.008762808Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.008770103Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE (0.01s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.008775369Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T09:58:49.009000809Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation"}
|
||||
{"Time":"2026-10-18T09:58:49.009015516Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"=== RUN TestRequiredClaimExactAnyOfAndNoMutation\n"}
|
||||
{"Time":"2026-10-18T09:58:49.009194148Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"--- PASS: TestRequiredClaimExactAnyOfAndNoMutation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.009207385Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.009304753Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation"}
|
||||
{"Time":"2026-10-18T09:58:49.009329884Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"=== RUN TestRequiredClaimConfigurationRefusalsAndLegacyIsolation\n"}
|
||||
{"Time":"2026-10-18T09:58:49.00944493Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"--- PASS: TestRequiredClaimConfigurationRefusalsAndLegacyIsolation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.00948576Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.009621457Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable"}
|
||||
{"Time":"2026-10-18T09:58:49.009639897Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"=== RUN TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable\n"}
|
||||
{"Time":"2026-10-18T09:58:49.013367051Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"--- PASS: TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.013414951Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.013425961Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval"}
|
||||
{"Time":"2026-10-18T09:58:49.013431488Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"=== RUN TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval\n"}
|
||||
{"Time":"2026-10-18T09:58:49.013592455Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"--- PASS: TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.013605395Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.013617388Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse"}
|
||||
{"Time":"2026-10-18T09:58:49.013622221Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"=== RUN TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse\n"}
|
||||
{"Time":"2026-10-18T09:58:49.013774414Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"--- PASS: TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse (0.00s)\n"}
|
||||
{"Time":"2026-10-18T09:58:49.013787363Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Elapsed":0}
|
||||
{"Time":"2026-10-18T09:58:49.01379491Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T09:58:49.022903528Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"ok \tforgejo.org/services/auth/source/oauth2\t11.572s\n"}
|
||||
{"Time":"2026-10-18T09:58:49.022961085Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":11.572}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 11.5,
|
||||
"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-current-token-r1",
|
||||
"/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/modules",
|
||||
"/work/modules",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/gocache",
|
||||
"/work/gocache",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/go",
|
||||
"/work/go",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/writable-input",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-c",
|
||||
"-tags",
|
||||
"sqlite sqlite_unlock_notify current_issuer_fixture",
|
||||
"-o",
|
||||
"/work/forgejo-oauth2.test",
|
||||
"./services/auth/source/oauth2"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"go.mod": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "sha256:19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"services/auth/source/oauth2/access_engine.go": "sha256:d16330043825d0284253e65d28bda2de4edbc633e4887d8c3618a106ee251daa",
|
||||
"services/auth/source/oauth2/access_engine_assurance.go": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642",
|
||||
"services/auth/source/oauth2/access_engine_assurance_test.go": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3",
|
||||
"services/auth/source/oauth2/access_engine_azp_test.go": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce",
|
||||
"services/auth/source/oauth2/access_engine_callback_test.go": "sha256:1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580",
|
||||
"services/auth/source/oauth2/access_engine_current_token.go": "sha256:4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8",
|
||||
"services/auth/source/oauth2/access_engine_current_token_test.go": "sha256:58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1",
|
||||
"services/auth/source/oauth2/access_engine_purpose_test.go": "sha256:bf492f6384d7da0f84321f138d4efab5c77508c667c288c2807ad3cccc10383b",
|
||||
"services/auth/source/oauth2/access_engine_test.go": "sha256:9112a142983c0a58c5d40344b58a20fa2c1bdbc7a79b5c586796362839a43b48",
|
||||
"services/auth/source/oauth2/assert_interface_test.go": "sha256:a0db85e38b7ea61e21b0291e55c8b451c89a877bd3db229e35dbb4418458f54f",
|
||||
"services/auth/source/oauth2/current_issuer_fixture_test.go": "sha256:24fb430e72848326c5d9ef42eab5028cf915dd601f43fb06010b32b568908781",
|
||||
"services/auth/source/oauth2/init.go": "sha256:98a8030ed005be67b237d54c3b197a629f8c289e3d74c8edfcf4d740831f87d1",
|
||||
"services/auth/source/oauth2/jwtsigningkey.go": "sha256:0c6e8ac131fd41ad299073ca748dd95e5fd9c044219c4d7f36d50c4ffcd5b266",
|
||||
"services/auth/source/oauth2/jwtsigningkey_test.go": "sha256:3e414a541479741773e48de84be7d5a3b794bcc7289627fbd2e8996832628321",
|
||||
"services/auth/source/oauth2/providers.go": "sha256:7df0051f19932a68d16a33a5d41898e06e9612f839195e27c86802eeebb2e3e9",
|
||||
"services/auth/source/oauth2/providers_base.go": "sha256:643dbc0561ec7aa7a975978ff2c21d256a5ab619b125e8077d2395281f628542",
|
||||
"services/auth/source/oauth2/providers_custom.go": "sha256:265a526d209d86c24cbcfdde4c2d73957aaeaccfd329438c11ce99c5951ebb39",
|
||||
"services/auth/source/oauth2/providers_openid.go": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf",
|
||||
"services/auth/source/oauth2/providers_simple.go": "sha256:53927b6bfc621dd1b68a074b6763abe5b27795d5a50ca6f0555f3e7a04fa1613",
|
||||
"services/auth/source/oauth2/required_claim_values.go": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda",
|
||||
"services/auth/source/oauth2/required_claim_values_test.go": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0",
|
||||
"services/auth/source/oauth2/source.go": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642",
|
||||
"services/auth/source/oauth2/source_authenticate.go": "sha256:c6551f00ef4349ccd6afdbb2cba847067528485ff621c3e507b9aab588bb96b5",
|
||||
"services/auth/source/oauth2/source_callout.go": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c",
|
||||
"services/auth/source/oauth2/source_name.go": "sha256:712943826e01f718ab4ca717df6c5f2171535cb2229dbf8460f542d757ef8d7f",
|
||||
"services/auth/source/oauth2/source_register.go": "sha256:5ad9ccf213881734dc7fe7619cbe6c47cb6a4afdbbfc3e674ab4493857ebbf91",
|
||||
"services/auth/source/oauth2/store.go": "sha256:35b244081569215384de07f8cdc1e83b5545a2e673ca1ac5a2a6ee686cb917a3",
|
||||
"services/auth/source/oauth2/token.go": "sha256:c0a8868f904bb31842202b4e635a76403902adff9e638d5cb581e7e2d4954933",
|
||||
"services/auth/source/oauth2/urlmapping.go": "sha256:f2132408d4a447e766e67495b1ca7b8cc2b47d4eb56b0c44b50ba01fe41ca83e",
|
||||
"third_party/keycape-tokenstatus/LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"third_party/keycape-tokenstatus/go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"third_party/keycape-tokenstatus/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"third_party/keycape-tokenstatus/pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575",
|
||||
"third_party/keycape-tokenstatus/producer-source.json": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 0,
|
||||
"fail": 0,
|
||||
"skip": 0
|
||||
},
|
||||
"failures": [],
|
||||
"real_credentials": false,
|
||||
"native_admission": false,
|
||||
"network": "private empty namespace",
|
||||
"pid_namespace": "private",
|
||||
"home": "empty allowlisted fixture HOME",
|
||||
"compiled_fixture_only": true
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 114.15,
|
||||
"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-current-token-r1",
|
||||
"/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/modules",
|
||||
"/work/modules",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/gocache",
|
||||
"/work/gocache",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/go",
|
||||
"/work/go",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/writable-input",
|
||||
"--",
|
||||
"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|TestSensitiveSQL",
|
||||
"-count=1"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"go.mod": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "sha256:19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"services/auth/source/oauth2/access_engine.go": "sha256:d16330043825d0284253e65d28bda2de4edbc633e4887d8c3618a106ee251daa",
|
||||
"services/auth/source/oauth2/access_engine_assurance.go": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642",
|
||||
"services/auth/source/oauth2/access_engine_assurance_test.go": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3",
|
||||
"services/auth/source/oauth2/access_engine_azp_test.go": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce",
|
||||
"services/auth/source/oauth2/access_engine_callback_test.go": "sha256:1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580",
|
||||
"services/auth/source/oauth2/access_engine_current_token.go": "sha256:4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8",
|
||||
"services/auth/source/oauth2/access_engine_current_token_test.go": "sha256:58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1",
|
||||
"services/auth/source/oauth2/access_engine_purpose_test.go": "sha256:bf492f6384d7da0f84321f138d4efab5c77508c667c288c2807ad3cccc10383b",
|
||||
"services/auth/source/oauth2/access_engine_test.go": "sha256:9112a142983c0a58c5d40344b58a20fa2c1bdbc7a79b5c586796362839a43b48",
|
||||
"services/auth/source/oauth2/assert_interface_test.go": "sha256:a0db85e38b7ea61e21b0291e55c8b451c89a877bd3db229e35dbb4418458f54f",
|
||||
"services/auth/source/oauth2/current_issuer_fixture_test.go": "sha256:24fb430e72848326c5d9ef42eab5028cf915dd601f43fb06010b32b568908781",
|
||||
"services/auth/source/oauth2/init.go": "sha256:98a8030ed005be67b237d54c3b197a629f8c289e3d74c8edfcf4d740831f87d1",
|
||||
"services/auth/source/oauth2/jwtsigningkey.go": "sha256:0c6e8ac131fd41ad299073ca748dd95e5fd9c044219c4d7f36d50c4ffcd5b266",
|
||||
"services/auth/source/oauth2/jwtsigningkey_test.go": "sha256:3e414a541479741773e48de84be7d5a3b794bcc7289627fbd2e8996832628321",
|
||||
"services/auth/source/oauth2/providers.go": "sha256:7df0051f19932a68d16a33a5d41898e06e9612f839195e27c86802eeebb2e3e9",
|
||||
"services/auth/source/oauth2/providers_base.go": "sha256:643dbc0561ec7aa7a975978ff2c21d256a5ab619b125e8077d2395281f628542",
|
||||
"services/auth/source/oauth2/providers_custom.go": "sha256:265a526d209d86c24cbcfdde4c2d73957aaeaccfd329438c11ce99c5951ebb39",
|
||||
"services/auth/source/oauth2/providers_openid.go": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf",
|
||||
"services/auth/source/oauth2/providers_simple.go": "sha256:53927b6bfc621dd1b68a074b6763abe5b27795d5a50ca6f0555f3e7a04fa1613",
|
||||
"services/auth/source/oauth2/required_claim_values.go": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda",
|
||||
"services/auth/source/oauth2/required_claim_values_test.go": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0",
|
||||
"services/auth/source/oauth2/source.go": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642",
|
||||
"services/auth/source/oauth2/source_authenticate.go": "sha256:c6551f00ef4349ccd6afdbb2cba847067528485ff621c3e507b9aab588bb96b5",
|
||||
"services/auth/source/oauth2/source_callout.go": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c",
|
||||
"services/auth/source/oauth2/source_name.go": "sha256:712943826e01f718ab4ca717df6c5f2171535cb2229dbf8460f542d757ef8d7f",
|
||||
"services/auth/source/oauth2/source_register.go": "sha256:5ad9ccf213881734dc7fe7619cbe6c47cb6a4afdbbfc3e674ab4493857ebbf91",
|
||||
"services/auth/source/oauth2/store.go": "sha256:35b244081569215384de07f8cdc1e83b5545a2e673ca1ac5a2a6ee686cb917a3",
|
||||
"services/auth/source/oauth2/token.go": "sha256:c0a8868f904bb31842202b4e635a76403902adff9e638d5cb581e7e2d4954933",
|
||||
"services/auth/source/oauth2/urlmapping.go": "sha256:f2132408d4a447e766e67495b1ca7b8cc2b47d4eb56b0c44b50ba01fe41ca83e",
|
||||
"third_party/keycape-tokenstatus/LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"third_party/keycape-tokenstatus/go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"third_party/keycape-tokenstatus/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"third_party/keycape-tokenstatus/pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575",
|
||||
"third_party/keycape-tokenstatus/producer-source.json": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 114,
|
||||
"fail": 0,
|
||||
"skip": 1
|
||||
},
|
||||
"failures": [],
|
||||
"real_credentials": false,
|
||||
"native_admission": false,
|
||||
"network": "private empty namespace",
|
||||
"pid_namespace": "private",
|
||||
"home": "empty allowlisted fixture HOME",
|
||||
"compiled_fixture_only": false
|
||||
}
|
||||
|
|
@ -0,0 +1,521 @@
|
|||
{"Time":"2026-10-18T10:02:36.286761203Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"}
|
||||
{"Time":"2026-10-18T10:02:36.377445354Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals"}
|
||||
{"Time":"2026-10-18T10:02:36.37749613Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals\n"}
|
||||
{"Time":"2026-10-18T10:02:36.377567952Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication"}
|
||||
{"Time":"2026-10-18T10:02:36.377576095Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/future-authentication\n"}
|
||||
{"Time":"2026-10-18T10:02:36.380110564Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/future-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.380152233Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.380177508Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at"}
|
||||
{"Time":"2026-10-18T10:02:36.380183892Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/missing-at\n"}
|
||||
{"Time":"2026-10-18T10:02:36.380437238Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/missing-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.380488294Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.380503172Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source"}
|
||||
{"Time":"2026-10-18T10:02:36.380509503Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-source\n"}
|
||||
{"Time":"2026-10-18T10:02:36.380779768Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-source (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.380814565Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.380841851Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level"}
|
||||
{"Time":"2026-10-18T10:02:36.380850697Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-level\n"}
|
||||
{"Time":"2026-10-18T10:02:36.381211448Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-level (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.38125675Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.381267039Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods"}
|
||||
{"Time":"2026-10-18T10:02:36.381294587Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/object-methods\n"}
|
||||
{"Time":"2026-10-18T10:02:36.3815904Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/object-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.381636262Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.381659021Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods"}
|
||||
{"Time":"2026-10-18T10:02:36.381667362Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/empty-methods\n"}
|
||||
{"Time":"2026-10-18T10:02:36.382058251Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/empty-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.382129253Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.382141219Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods"}
|
||||
{"Time":"2026-10-18T10:02:36.382147125Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods\n"}
|
||||
{"Time":"2026-10-18T10:02:36.382462404Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.382489438Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.382497175Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field"}
|
||||
{"Time":"2026-10-18T10:02:36.382501963Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field\n"}
|
||||
{"Time":"2026-10-18T10:02:36.382838588Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.382883512Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.382903548Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication"}
|
||||
{"Time":"2026-10-18T10:02:36.382913359Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication\n"}
|
||||
{"Time":"2026-10-18T10:02:36.383350324Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.383402912Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.3834178Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at"}
|
||||
{"Time":"2026-10-18T10:02:36.383423111Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/string-at\n"}
|
||||
{"Time":"2026-10-18T10:02:36.383844108Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/string-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.383877616Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.383884451Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at"}
|
||||
{"Time":"2026-10-18T10:02:36.383889763Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/fractional-at\n"}
|
||||
{"Time":"2026-10-18T10:02:36.384426397Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/fractional-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.384460897Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.384469175Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa"}
|
||||
{"Time":"2026-10-18T10:02:36.384474107Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa\n"}
|
||||
{"Time":"2026-10-18T10:02:36.384897181Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.384926451Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.384933223Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa"}
|
||||
{"Time":"2026-10-18T10:02:36.384937813Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa\n"}
|
||||
{"Time":"2026-10-18T10:02:36.385189115Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.385201405Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.385208628Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods"}
|
||||
{"Time":"2026-10-18T10:02:36.385212884Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods\n"}
|
||||
{"Time":"2026-10-18T10:02:36.385602186Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.385612943Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.386242293Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.386258209Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:02:36.386265215Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds"}
|
||||
{"Time":"2026-10-18T10:02:36.386268669Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"=== RUN TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds\n"}
|
||||
{"Time":"2026-10-18T10:02:36.386801345Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"--- PASS: TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.386821904Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.386833789Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact"}
|
||||
{"Time":"2026-10-18T10:02:36.386839127Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"=== RUN TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact\n"}
|
||||
{"Time":"2026-10-18T10:02:36.387402493Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"--- PASS: TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.387453251Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.387477814Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient"}
|
||||
{"Time":"2026-10-18T10:02:36.387487651Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient\n"}
|
||||
{"Time":"2026-10-18T10:02:36.387502223Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client"}
|
||||
{"Time":"2026-10-18T10:02:36.387511015Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client\n"}
|
||||
{"Time":"2026-10-18T10:02:36.388612403Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.388670555Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.388679284Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number"}
|
||||
{"Time":"2026-10-18T10:02:36.38870271Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/number\n"}
|
||||
{"Time":"2026-10-18T10:02:36.389148399Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/number (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.38919179Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.38919986Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array"}
|
||||
{"Time":"2026-10-18T10:02:36.389204441Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/array\n"}
|
||||
{"Time":"2026-10-18T10:02:36.389841582Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/array (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.389872169Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.389881394Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty"}
|
||||
{"Time":"2026-10-18T10:02:36.389884992Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty\n"}
|
||||
{"Time":"2026-10-18T10:02:36.390402311Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.390442609Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.39045159Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.390456162Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.390462492Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient"}
|
||||
{"Time":"2026-10-18T10:02:36.390466162Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"=== RUN TestAccessEngineAuthorizedPartyAcceptsAdmittedClient\n"}
|
||||
{"Time":"2026-10-18T10:02:36.391172134Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyAcceptsAdmittedClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.391221765Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:36.391236021Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision"}
|
||||
{"Time":"2026-10-18T10:02:36.391261369Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision\n"}
|
||||
{"Time":"2026-10-18T10:02:36.391279007Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins"}
|
||||
{"Time":"2026-10-18T10:02:36.391285463Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins\n"}
|
||||
{"Time":"2026-10-18T10:02:36.844977507Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins (0.45s)\n"}
|
||||
{"Time":"2026-10-18T10:02:36.84502923Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Elapsed":0.45}
|
||||
{"Time":"2026-10-18T10:02:36.845039906Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-18T10:02:36.845045461Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-18T10:02:37.376071266Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners (0.53s)\n"}
|
||||
{"Time":"2026-10-18T10:02:37.376116578Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Elapsed":0.53}
|
||||
{"Time":"2026-10-18T10:02:37.37613215Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-18T10:02:37.37613647Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-18T10:02:37.469947459Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers (0.09s)\n"}
|
||||
{"Time":"2026-10-18T10:02:37.470014746Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Elapsed":0.09}
|
||||
{"Time":"2026-10-18T10:02:37.470038714Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision (1.08s)\n"}
|
||||
{"Time":"2026-10-18T10:02:37.470050373Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Elapsed":1.08}
|
||||
{"Time":"2026-10-18T10:02:37.470059444Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP"}
|
||||
{"Time":"2026-10-18T10:02:37.470066613Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP\n"}
|
||||
{"Time":"2026-10-18T10:02:37.470102475Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny"}
|
||||
{"Time":"2026-10-18T10:02:37.470110257Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny\n"}
|
||||
{"Time":"2026-10-18T10:02:37.665831862Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny (0.20s)\n"}
|
||||
{"Time":"2026-10-18T10:02:37.665877498Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Elapsed":0.2}
|
||||
{"Time":"2026-10-18T10:02:37.665887913Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned"}
|
||||
{"Time":"2026-10-18T10:02:37.665891592Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned\n"}
|
||||
{"Time":"2026-10-18T10:02:38.122760652Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned (0.46s)\n"}
|
||||
{"Time":"2026-10-18T10:02:38.122812664Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Elapsed":0.46}
|
||||
{"Time":"2026-10-18T10:02:38.122822889Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key"}
|
||||
{"Time":"2026-10-18T10:02:38.122829679Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key\n"}
|
||||
{"Time":"2026-10-18T10:02:38.672838887Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key (0.55s)\n"}
|
||||
{"Time":"2026-10-18T10:02:38.672886335Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Elapsed":0.55}
|
||||
{"Time":"2026-10-18T10:02:38.672894334Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin"}
|
||||
{"Time":"2026-10-18T10:02:38.672899465Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin\n"}
|
||||
{"Time":"2026-10-18T10:02:38.73110489Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin (0.06s)\n"}
|
||||
{"Time":"2026-10-18T10:02:38.731187948Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Elapsed":0.06}
|
||||
{"Time":"2026-10-18T10:02:38.731204873Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding"}
|
||||
{"Time":"2026-10-18T10:02:38.731210834Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding\n"}
|
||||
{"Time":"2026-10-18T10:02:39.033993684Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding (0.30s)\n"}
|
||||
{"Time":"2026-10-18T10:02:39.034046633Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Elapsed":0.3}
|
||||
{"Time":"2026-10-18T10:02:39.03405593Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired"}
|
||||
{"Time":"2026-10-18T10:02:39.034061701Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired\n"}
|
||||
{"Time":"2026-10-18T10:02:39.342124377Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired (0.31s)\n"}
|
||||
{"Time":"2026-10-18T10:02:39.342180239Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Elapsed":0.31}
|
||||
{"Time":"2026-10-18T10:02:39.342194748Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP (1.87s)\n"}
|
||||
{"Time":"2026-10-18T10:02:39.342200384Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Elapsed":1.87}
|
||||
{"Time":"2026-10-18T10:02:39.34220602Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP"}
|
||||
{"Time":"2026-10-18T10:02:39.342211646Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP\n"}
|
||||
{"Time":"2026-10-18T10:02:39.342229464Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user"}
|
||||
{"Time":"2026-10-18T10:02:39.342238346Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user\n"}
|
||||
{"Time":"2026-10-18T10:02:39.894189509Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user (0.55s)\n"}
|
||||
{"Time":"2026-10-18T10:02:39.894243793Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Elapsed":0.55}
|
||||
{"Time":"2026-10-18T10:02:39.894254947Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups"}
|
||||
{"Time":"2026-10-18T10:02:39.894262702Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups\n"}
|
||||
{"Time":"2026-10-18T10:02:40.677135971Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups (0.78s)\n"}
|
||||
{"Time":"2026-10-18T10:02:40.677190191Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Elapsed":0.78}
|
||||
{"Time":"2026-10-18T10:02:40.677235774Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source"}
|
||||
{"Time":"2026-10-18T10:02:40.677245503Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source\n"}
|
||||
{"Time":"2026-10-18T10:02:41.300213165Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source (0.62s)\n"}
|
||||
{"Time":"2026-10-18T10:02:41.300263625Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Elapsed":0.62}
|
||||
{"Time":"2026-10-18T10:02:41.300274022Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission"}
|
||||
{"Time":"2026-10-18T10:02:41.300280568Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission\n"}
|
||||
{"Time":"2026-10-18T10:02:41.456629808Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission (0.16s)\n"}
|
||||
{"Time":"2026-10-18T10:02:41.456692199Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Elapsed":0.16}
|
||||
{"Time":"2026-10-18T10:02:41.456704642Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token"}
|
||||
{"Time":"2026-10-18T10:02:41.456710404Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token\n"}
|
||||
{"Time":"2026-10-18T10:02:41.754661946Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token (0.30s)\n"}
|
||||
{"Time":"2026-10-18T10:02:41.754734246Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Elapsed":0.3}
|
||||
{"Time":"2026-10-18T10:02:41.754746113Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity"}
|
||||
{"Time":"2026-10-18T10:02:41.754751361Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity\n"}
|
||||
{"Time":"2026-10-18T10:02:41.884522785Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity (0.13s)\n"}
|
||||
{"Time":"2026-10-18T10:02:41.884580333Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Elapsed":0.13}
|
||||
{"Time":"2026-10-18T10:02:41.884592335Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token"}
|
||||
{"Time":"2026-10-18T10:02:41.884616105Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token\n"}
|
||||
{"Time":"2026-10-18T10:02:41.992453903Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token (0.11s)\n"}
|
||||
{"Time":"2026-10-18T10:02:41.992503462Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Elapsed":0.11}
|
||||
{"Time":"2026-10-18T10:02:41.99252036Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject"}
|
||||
{"Time":"2026-10-18T10:02:41.99252779Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject\n"}
|
||||
{"Time":"2026-10-18T10:02:42.50912818Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject (0.52s)\n"}
|
||||
{"Time":"2026-10-18T10:02:42.509187342Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Elapsed":0.52}
|
||||
{"Time":"2026-10-18T10:02:42.509199651Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider"}
|
||||
{"Time":"2026-10-18T10:02:42.509208542Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider\n"}
|
||||
{"Time":"2026-10-18T10:02:42.777293385Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider (0.27s)\n"}
|
||||
{"Time":"2026-10-18T10:02:42.777347641Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Elapsed":0.27}
|
||||
{"Time":"2026-10-18T10:02:42.777359319Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience"}
|
||||
{"Time":"2026-10-18T10:02:42.777366271Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience\n"}
|
||||
{"Time":"2026-10-18T10:02:43.344358491Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience (0.57s)\n"}
|
||||
{"Time":"2026-10-18T10:02:43.344413054Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Elapsed":0.57}
|
||||
{"Time":"2026-10-18T10:02:43.344429204Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP (4.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:43.3444375Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Elapsed":4}
|
||||
{"Time":"2026-10-18T10:02:43.344465093Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision"}
|
||||
{"Time":"2026-10-18T10:02:43.344472631Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision\n"}
|
||||
{"Time":"2026-10-18T10:02:43.344483704Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing"}
|
||||
{"Time":"2026-10-18T10:02:43.344490007Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing\n"}
|
||||
{"Time":"2026-10-18T10:02:43.409503243Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing (0.06s)\n"}
|
||||
{"Time":"2026-10-18T10:02:43.40957943Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Elapsed":0.06}
|
||||
{"Time":"2026-10-18T10:02:43.409592829Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access"}
|
||||
{"Time":"2026-10-18T10:02:43.409598772Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access\n"}
|
||||
{"Time":"2026-10-18T10:02:43.76422413Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access (0.35s)\n"}
|
||||
{"Time":"2026-10-18T10:02:43.764276971Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Elapsed":0.35}
|
||||
{"Time":"2026-10-18T10:02:43.764288332Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject"}
|
||||
{"Time":"2026-10-18T10:02:43.76429514Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/subject\n"}
|
||||
{"Time":"2026-10-18T10:02:44.154085873Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/subject (0.39s)\n"}
|
||||
{"Time":"2026-10-18T10:02:44.154136811Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Elapsed":0.39}
|
||||
{"Time":"2026-10-18T10:02:44.154146081Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant"}
|
||||
{"Time":"2026-10-18T10:02:44.154151293Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant\n"}
|
||||
{"Time":"2026-10-18T10:02:44.563624408Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant (0.41s)\n"}
|
||||
{"Time":"2026-10-18T10:02:44.563696411Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Elapsed":0.41}
|
||||
{"Time":"2026-10-18T10:02:44.563734743Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal"}
|
||||
{"Time":"2026-10-18T10:02:44.563744906Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/principal\n"}
|
||||
{"Time":"2026-10-18T10:02:44.723009997Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/principal (0.16s)\n"}
|
||||
{"Time":"2026-10-18T10:02:44.723065174Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Elapsed":0.16}
|
||||
{"Time":"2026-10-18T10:02:44.723077672Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance"}
|
||||
{"Time":"2026-10-18T10:02:44.723093154Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance\n"}
|
||||
{"Time":"2026-10-18T10:02:44.965427968Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance (0.24s)\n"}
|
||||
{"Time":"2026-10-18T10:02:44.965471368Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Elapsed":0.24}
|
||||
{"Time":"2026-10-18T10:02:44.965478519Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time"}
|
||||
{"Time":"2026-10-18T10:02:44.965481901Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time\n"}
|
||||
{"Time":"2026-10-18T10:02:45.104444944Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time (0.14s)\n"}
|
||||
{"Time":"2026-10-18T10:02:45.104493844Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Elapsed":0.14}
|
||||
{"Time":"2026-10-18T10:02:45.104502889Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source"}
|
||||
{"Time":"2026-10-18T10:02:45.10450774Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source\n"}
|
||||
{"Time":"2026-10-18T10:02:45.181514471Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source (0.08s)\n"}
|
||||
{"Time":"2026-10-18T10:02:45.181563191Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Elapsed":0.08}
|
||||
{"Time":"2026-10-18T10:02:45.181572587Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at"}
|
||||
{"Time":"2026-10-18T10:02:45.181577835Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at\n"}
|
||||
{"Time":"2026-10-18T10:02:45.584085762Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at (0.40s)\n"}
|
||||
{"Time":"2026-10-18T10:02:45.584141867Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Elapsed":0.4}
|
||||
{"Time":"2026-10-18T10:02:45.584152796Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive"}
|
||||
{"Time":"2026-10-18T10:02:45.584158747Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive\n"}
|
||||
{"Time":"2026-10-18T10:02:45.890012871Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive (0.31s)\n"}
|
||||
{"Time":"2026-10-18T10:02:45.890066316Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Elapsed":0.31}
|
||||
{"Time":"2026-10-18T10:02:45.890079401Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable"}
|
||||
{"Time":"2026-10-18T10:02:45.890084504Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable\n"}
|
||||
{"Time":"2026-10-18T10:02:45.982451047Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable (0.09s)\n"}
|
||||
{"Time":"2026-10-18T10:02:45.982502562Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Elapsed":0.09}
|
||||
{"Time":"2026-10-18T10:02:45.982512752Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status"}
|
||||
{"Time":"2026-10-18T10:02:45.982518514Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status\n"}
|
||||
{"Time":"2026-10-18T10:02:46.383704879Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status (0.40s)\n"}
|
||||
{"Time":"2026-10-18T10:02:46.383757531Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Elapsed":0.4}
|
||||
{"Time":"2026-10-18T10:02:46.383769226Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission"}
|
||||
{"Time":"2026-10-18T10:02:46.383775655Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission\n"}
|
||||
{"Time":"2026-10-18T10:02:46.805394646Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission (0.42s)\n"}
|
||||
{"Time":"2026-10-18T10:02:46.805457848Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Elapsed":0.42}
|
||||
{"Time":"2026-10-18T10:02:46.805468714Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client"}
|
||||
{"Time":"2026-10-18T10:02:46.805474764Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client\n"}
|
||||
{"Time":"2026-10-18T10:02:47.11370227Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client (0.31s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.113793272Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Elapsed":0.31}
|
||||
{"Time":"2026-10-18T10:02:47.11380981Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key"}
|
||||
{"Time":"2026-10-18T10:02:47.113815987Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key\n"}
|
||||
{"Time":"2026-10-18T10:02:47.159427206Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key (0.05s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.15948076Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Elapsed":0.05}
|
||||
{"Time":"2026-10-18T10:02:47.159490985Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant"}
|
||||
{"Time":"2026-10-18T10:02:47.15949681Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant\n"}
|
||||
{"Time":"2026-10-18T10:02:47.357792416Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant (0.20s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.357840144Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Elapsed":0.2}
|
||||
{"Time":"2026-10-18T10:02:47.357850397Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash"}
|
||||
{"Time":"2026-10-18T10:02:47.357856177Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash\n"}
|
||||
{"Time":"2026-10-18T10:02:47.539451646Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash (0.18s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.539504478Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Elapsed":0.18}
|
||||
{"Time":"2026-10-18T10:02:47.53952832Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision (4.20s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.539584398Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Elapsed":4.2}
|
||||
{"Time":"2026-10-18T10:02:47.539599475Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn"}
|
||||
{"Time":"2026-10-18T10:02:47.539607338Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"=== RUN TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn\n"}
|
||||
{"Time":"2026-10-18T10:02:47.649544749Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"--- PASS: TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn (0.11s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.649588771Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Elapsed":0.11}
|
||||
{"Time":"2026-10-18T10:02:47.64959749Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims"}
|
||||
{"Time":"2026-10-18T10:02:47.649601837Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims\n"}
|
||||
{"Time":"2026-10-18T10:02:47.649717672Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti"}
|
||||
{"Time":"2026-10-18T10:02:47.649728015Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti\n"}
|
||||
{"Time":"2026-10-18T10:02:47.650301593Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.650430831Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.650459046Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope"}
|
||||
{"Time":"2026-10-18T10:02:47.650465809Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope\n"}
|
||||
{"Time":"2026-10-18T10:02:47.650997418Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.651016553Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.651026012Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.651031161Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.651122803Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE"}
|
||||
{"Time":"2026-10-18T10:02:47.651186086Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE\n"}
|
||||
{"Time":"2026-10-18T10:02:47.651866582Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject"}
|
||||
{"Time":"2026-10-18T10:02:47.651890162Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/empty-subject\n"}
|
||||
{"Time":"2026-10-18T10:02:47.65231406Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/empty-subject (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.652356865Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.652379589Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience"}
|
||||
{"Time":"2026-10-18T10:02:47.652386397Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/audience\n"}
|
||||
{"Time":"2026-10-18T10:02:47.652862985Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/audience (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.652885537Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.652896493Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp"}
|
||||
{"Time":"2026-10-18T10:02:47.652903653Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-exp\n"}
|
||||
{"Time":"2026-10-18T10:02:47.653352038Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-exp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.653377269Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.653385601Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat"}
|
||||
{"Time":"2026-10-18T10:02:47.65338937Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-iat\n"}
|
||||
{"Time":"2026-10-18T10:02:47.653837464Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-iat (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.653867681Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.653879818Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal"}
|
||||
{"Time":"2026-10-18T10:02:47.653904209Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-principal\n"}
|
||||
{"Time":"2026-10-18T10:02:47.654293322Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-principal (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.654307885Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.654394904Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance"}
|
||||
{"Time":"2026-10-18T10:02:47.65441769Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-assurance\n"}
|
||||
{"Time":"2026-10-18T10:02:47.65472716Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-assurance (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.654740208Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.654754211Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer"}
|
||||
{"Time":"2026-10-18T10:02:47.654757719Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/issuer\n"}
|
||||
{"Time":"2026-10-18T10:02:47.655093061Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/issuer (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.655104135Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.655195723Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired"}
|
||||
{"Time":"2026-10-18T10:02:47.655206544Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/expired\n"}
|
||||
{"Time":"2026-10-18T10:02:47.65573527Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/expired (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.655764549Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.65577326Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued"}
|
||||
{"Time":"2026-10-18T10:02:47.655777624Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/future-issued\n"}
|
||||
{"Time":"2026-10-18T10:02:47.656264877Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/future-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.656294643Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.656303399Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued"}
|
||||
{"Time":"2026-10-18T10:02:47.656328945Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/old-issued\n"}
|
||||
{"Time":"2026-10-18T10:02:47.656705517Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/old-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.656726942Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.656735544Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant"}
|
||||
{"Time":"2026-10-18T10:02:47.656740594Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-tenant\n"}
|
||||
{"Time":"2026-10-18T10:02:47.657116135Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-tenant (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.657127145Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.6572392Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp"}
|
||||
{"Time":"2026-10-18T10:02:47.657258542Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp\n"}
|
||||
{"Time":"2026-10-18T10:02:47.657552516Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.657565654Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.657572751Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key"}
|
||||
{"Time":"2026-10-18T10:02:47.657576637Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-key\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658047671Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658062333Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.658070927Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key"}
|
||||
{"Time":"2026-10-18T10:02:47.658079755Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/unknown-key\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658335313Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/unknown-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658347729Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.658367306Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type"}
|
||||
{"Time":"2026-10-18T10:02:47.658372229Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-type\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658553296Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-type (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658565884Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.658571294Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion"}
|
||||
{"Time":"2026-10-18T10:02:47.658575478Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658699411Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.658789773Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.658808195Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none"}
|
||||
{"Time":"2026-10-18T10:02:47.658814868Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/none\n"}
|
||||
{"Time":"2026-10-18T10:02:47.659024645Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/none (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.659036611Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.659042598Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.659075457Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:02:47.659260302Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation"}
|
||||
{"Time":"2026-10-18T10:02:47.659277958Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"=== RUN TestRequiredClaimExactAnyOfAndNoMutation\n"}
|
||||
{"Time":"2026-10-18T10:02:47.659288156Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"--- PASS: TestRequiredClaimExactAnyOfAndNoMutation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.659325984Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.659352197Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation"}
|
||||
{"Time":"2026-10-18T10:02:47.659369176Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"=== RUN TestRequiredClaimConfigurationRefusalsAndLegacyIsolation\n"}
|
||||
{"Time":"2026-10-18T10:02:47.659377039Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"--- PASS: TestRequiredClaimConfigurationRefusalsAndLegacyIsolation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.659452297Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.659465282Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable"}
|
||||
{"Time":"2026-10-18T10:02:47.659469421Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"=== RUN TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable\n"}
|
||||
{"Time":"2026-10-18T10:02:47.666116781Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"--- PASS: TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.666394522Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:02:47.66642335Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval"}
|
||||
{"Time":"2026-10-18T10:02:47.666445695Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"=== RUN TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval\n"}
|
||||
{"Time":"2026-10-18T10:02:47.666669096Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"--- PASS: TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.666684362Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.666692234Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse"}
|
||||
{"Time":"2026-10-18T10:02:47.66669668Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"=== RUN TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse\n"}
|
||||
{"Time":"2026-10-18T10:02:47.666829775Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"--- PASS: TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:02:47.666841749Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:02:47.666927089Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:02:47.674386398Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"ok \tforgejo.org/services/auth/source/oauth2\t11.387s\n"}
|
||||
{"Time":"2026-10-18T10:02:47.674438184Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":11.388}
|
||||
{"Time":"2026-10-18T10:03:26.827401418Z","Action":"start","Package":"forgejo.org/cmd"}
|
||||
{"Time":"2026-10-18T10:03:27.069297722Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069480465Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4152700923/attachments\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069490673Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069496155Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4152700923/avatars\n"}
|
||||
{"Time":"2026-10-18T10:03:27.06950116Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069505948Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4152700923/repo-avatars\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069510637Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069518076Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4152700923/repo-archive\n"}
|
||||
{"Time":"2026-10-18T10:03:27.06952474Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069529384Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4152700923/packages\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069544244Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069549276Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata4152700923/actions_log\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069553992Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:27.069558591Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:03:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build3627027042/b1036/data/actions_artifacts\n"}
|
||||
{"Time":"2026-10-18T10:03:27.246060561Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata"}
|
||||
{"Time":"2026-10-18T10:03:27.246110976Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"=== RUN TestPrivateSecretMetadata\n"}
|
||||
{"Time":"2026-10-18T10:03:27.246126657Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"--- PASS: TestPrivateSecretMetadata (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:03:27.246140408Z","Action":"pass","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:03:27.246295791Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement"}
|
||||
{"Time":"2026-10-18T10:03:27.246314853Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"=== RUN TestPrivateSecretOpenedFileReplacement\n"}
|
||||
{"Time":"2026-10-18T10:03:27.247006389Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":" admin_auth_oauth_secret_linux_test.go:57: requires isolated read-only bind fixture\n"}
|
||||
{"Time":"2026-10-18T10:03:27.247092116Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"--- SKIP: TestPrivateSecretOpenedFileReplacement (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:03:27.247110069Z","Action":"skip","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:03:27.247482923Z","Action":"output","Package":"forgejo.org/cmd","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:03:27.27933977Z","Action":"output","Package":"forgejo.org/cmd","Output":"ok \tforgejo.org/cmd\t0.452s\n"}
|
||||
{"Time":"2026-10-18T10:03:27.279403044Z","Action":"pass","Package":"forgejo.org/cmd","Elapsed":0.452}
|
||||
{"Time":"2026-10-18T10:03:33.209698168Z","Action":"start","Package":"forgejo.org/models/auth"}
|
||||
{"Time":"2026-10-18T10:03:33.396487319Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396686844Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata920743698/attachments\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396699721Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396706916Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata920743698/avatars\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396712128Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396717232Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata920743698/repo-avatars\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396722246Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396727052Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata920743698/repo-archive\n"}
|
||||
{"Time":"2026-10-18T10:03:33.39673193Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396736682Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata920743698/packages\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396748495Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396753599Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata920743698/actions_log\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396758504Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:03:33.396763292Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:03:33 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build3627027042/b1316/data/actions_artifacts\n"}
|
||||
{"Time":"2026-10-18T10:03:33.537899252Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion"}
|
||||
{"Time":"2026-10-18T10:03:33.538041787Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"=== RUN TestSourceOperationDatabaseWriterExclusion\n"}
|
||||
{"Time":"2026-10-18T10:03:33.639093097Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"--- PASS: TestSourceOperationDatabaseWriterExclusion (0.10s)\n"}
|
||||
{"Time":"2026-10-18T10:03:33.639145596Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Elapsed":0.1}
|
||||
{"Time":"2026-10-18T10:03:33.639161105Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging"}
|
||||
{"Time":"2026-10-18T10:03:33.63916547Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"=== RUN TestSourceOperationRefusesActualXORMArgumentLogging\n"}
|
||||
{"Time":"2026-10-18T10:03:33.639173297Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 10:03:33.639134 [SQL] BEGIN TRANSACTION [] - 50.659µs\n"}
|
||||
{"Time":"2026-10-18T10:03:33.63917877Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 10:03:33.639160 [SQL] ROLLBACK [] - 13.354µs\n"}
|
||||
{"Time":"2026-10-18T10:03:33.639264695Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"--- PASS: TestSourceOperationRefusesActualXORMArgumentLogging (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:03:33.639286427Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:03:33.64030111Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery"}
|
||||
{"Time":"2026-10-18T10:03:33.640336881Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"=== RUN TestSourceOperationProcessRecovery\n"}
|
||||
{"Time":"2026-10-18T10:03:35.896451485Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"--- PASS: TestSourceOperationProcessRecovery (2.26s)\n"}
|
||||
{"Time":"2026-10-18T10:03:35.89650374Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Elapsed":2.26}
|
||||
{"Time":"2026-10-18T10:03:35.896514723Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse"}
|
||||
{"Time":"2026-10-18T10:03:35.896519079Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"=== RUN TestSourceOperationCreateAndInverse\n"}
|
||||
{"Time":"2026-10-18T10:03:35.926108994Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"--- PASS: TestSourceOperationCreateAndInverse (0.03s)\n"}
|
||||
{"Time":"2026-10-18T10:03:35.926165208Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Elapsed":0.03}
|
||||
{"Time":"2026-10-18T10:03:35.926178905Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting"}
|
||||
{"Time":"2026-10-18T10:03:35.92618427Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"=== RUN TestSourceOperationRestoresExactRawPreexisting\n"}
|
||||
{"Time":"2026-10-18T10:03:35.945407969Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"--- PASS: TestSourceOperationRestoresExactRawPreexisting (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:35.945456852Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:35.945482542Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting"}
|
||||
{"Time":"2026-10-18T10:03:35.945488547Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"=== RUN TestSourceOperationUnchangedNeverDeletesPreexisting\n"}
|
||||
{"Time":"2026-10-18T10:03:35.974070972Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"--- PASS: TestSourceOperationUnchangedNeverDeletesPreexisting (0.03s)\n"}
|
||||
{"Time":"2026-10-18T10:03:35.974123101Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Elapsed":0.03}
|
||||
{"Time":"2026-10-18T10:03:35.974140486Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses"}
|
||||
{"Time":"2026-10-18T10:03:35.974146185Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"=== RUN TestSourceOperationPrivateDriftRefuses\n"}
|
||||
{"Time":"2026-10-18T10:03:35.974374853Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply"}
|
||||
{"Time":"2026-10-18T10:03:35.974391923Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-apply\n"}
|
||||
{"Time":"2026-10-18T10:03:35.996858429Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-apply (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:35.996908303Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:35.996923822Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse"}
|
||||
{"Time":"2026-10-18T10:03:35.996929782Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-inverse\n"}
|
||||
{"Time":"2026-10-18T10:03:36.01523478Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-inverse (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.015283482Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.015293933Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses (0.04s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.015300128Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Elapsed":0.04}
|
||||
{"Time":"2026-10-18T10:03:36.01530937Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses"}
|
||||
{"Time":"2026-10-18T10:03:36.015314438Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"=== RUN TestSourceOperationStockCRUDDriftRefuses\n"}
|
||||
{"Time":"2026-10-18T10:03:36.034994343Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"--- PASS: TestSourceOperationStockCRUDDriftRefuses (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.0350675Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.035103605Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion"}
|
||||
{"Time":"2026-10-18T10:03:36.035147528Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion\n"}
|
||||
{"Time":"2026-10-18T10:03:36.035187998Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user"}
|
||||
{"Time":"2026-10-18T10:03:36.035212696Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/user\n"}
|
||||
{"Time":"2026-10-18T10:03:36.053512886Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/user (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.053565583Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.053577865Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external"}
|
||||
{"Time":"2026-10-18T10:03:36.053583464Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/external\n"}
|
||||
{"Time":"2026-10-18T10:03:36.072400155Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/external (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.072451382Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.072461643Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion (0.04s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.072468388Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Elapsed":0.04}
|
||||
{"Time":"2026-10-18T10:03:36.07247369Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse"}
|
||||
{"Time":"2026-10-18T10:03:36.072478415Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"=== RUN TestSourceOperationPreparedCancellationAndIDReuse\n"}
|
||||
{"Time":"2026-10-18T10:03:36.091777715Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"--- PASS: TestSourceOperationPreparedCancellationAndIDReuse (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.091833793Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.091845642Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime"}
|
||||
{"Time":"2026-10-18T10:03:36.091852702Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"=== RUN TestSourceOperationRejectsUnsafeRuntime\n"}
|
||||
{"Time":"2026-10-18T10:03:36.109374155Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"--- PASS: TestSourceOperationRejectsUnsafeRuntime (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.109427438Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.109438096Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource"}
|
||||
{"Time":"2026-10-18T10:03:36.109443984Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"=== RUN TestSourceOperationRefusesSharedManagedSource\n"}
|
||||
{"Time":"2026-10-18T10:03:36.129206721Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"--- PASS: TestSourceOperationRefusesSharedManagedSource (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.129260365Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.129270437Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody"}
|
||||
{"Time":"2026-10-18T10:03:36.129276398Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"=== RUN TestSourceOperationBoundsPrivateJournalCustody\n"}
|
||||
{"Time":"2026-10-18T10:03:36.150126051Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"--- PASS: TestSourceOperationBoundsPrivateJournalCustody (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.150180975Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.15019772Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState"}
|
||||
{"Time":"2026-10-18T10:03:36.150204708Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"=== RUN TestSourceOperationRequiresExactPostState\n"}
|
||||
{"Time":"2026-10-18T10:03:36.170095586Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"--- PASS: TestSourceOperationRequiresExactPostState (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:03:36.170150014Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:03:36.170459161Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:03:36.204795981Z","Action":"output","Package":"forgejo.org/models/auth","Output":"ok \tforgejo.org/models/auth\t2.995s\n"}
|
||||
{"Time":"2026-10-18T10:03:36.204885053Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":2.995}
|
||||
{"Time":"2026-10-18T10:04:34.442600416Z","Action":"start","Package":"forgejo.org/models/db"}
|
||||
{"Time":"2026-10-18T10:04:34.65276927Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652859993Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata838365758/attachments\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652870462Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652880426Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata838365758/avatars\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652888244Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652895999Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata838365758/repo-avatars\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652904917Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652912356Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata838365758/repo-archive\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652935774Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:04:34.652942266Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata838365758/packages\n"}
|
||||
{"Time":"2026-10-18T10:04:34.653046127Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:04:34.653072637Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata838365758/actions_log\n"}
|
||||
{"Time":"2026-10-18T10:04:34.653090194Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:04:34.653148139Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:04:34 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build3627027042/b1337/data/actions_artifacts\n"}
|
||||
{"Time":"2026-10-18T10:04:34.76957674Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine"}
|
||||
{"Time":"2026-10-18T10:04:34.769624865Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine\n"}
|
||||
{"Time":"2026-10-18T10:04:34.769780476Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine"}
|
||||
{"Time":"2026-10-18T10:04:34.769798285Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/engine\n"}
|
||||
{"Time":"2026-10-18T10:04:34.769807825Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/engine (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:04:34.769812686Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:04:34.770585777Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session"}
|
||||
{"Time":"2026-10-18T10:04:34.770631098Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/session\n"}
|
||||
{"Time":"2026-10-18T10:04:34.77064484Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/session (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:04:34.77065173Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:04:34.770666716Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:04:34.770672974Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:04:34.770682442Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks"}
|
||||
{"Time":"2026-10-18T10:04:34.770688213Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"=== RUN TestSensitiveSQLHooks\n"}
|
||||
{"Time":"2026-10-18T10:04:34.776085941Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"--- PASS: TestSensitiveSQLHooks (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:04:34.776132479Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:04:34.776145013Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation"}
|
||||
{"Time":"2026-10-18T10:04:34.776149684Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"=== RUN TestSensitiveSQLActualContextPropagation\n"}
|
||||
{"Time":"2026-10-18T10:04:34.778748834Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"--- PASS: TestSensitiveSQLActualContextPropagation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:04:34.778786039Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:04:34.779167757Z","Action":"output","Package":"forgejo.org/models/db","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:04:34.811424991Z","Action":"output","Package":"forgejo.org/models/db","Output":"ok \tforgejo.org/models/db\t0.369s\n"}
|
||||
{"Time":"2026-10-18T10:04:34.811472836Z","Action":"pass","Package":"forgejo.org/models/db","Elapsed":0.369}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 15.36,
|
||||
"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-current-token-r1",
|
||||
"/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/modules",
|
||||
"/work/modules",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/gocache",
|
||||
"/work/gocache",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/go",
|
||||
"/work/go",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/descriptor-fixtures",
|
||||
"/run/writable-input",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"1",
|
||||
"-json",
|
||||
"-tags",
|
||||
"sqlite sqlite_unlock_notify",
|
||||
"./services/auth/source/oauth2",
|
||||
"-run",
|
||||
"TestAccessEngine|TestVerifiedApplicationIdentity|TestRequired|TestCurrent",
|
||||
"-count=1"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"go.mod": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "sha256:19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"services/auth/source/oauth2/access_engine.go": "sha256:d16330043825d0284253e65d28bda2de4edbc633e4887d8c3618a106ee251daa",
|
||||
"services/auth/source/oauth2/access_engine_assurance.go": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642",
|
||||
"services/auth/source/oauth2/access_engine_assurance_test.go": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3",
|
||||
"services/auth/source/oauth2/access_engine_azp_test.go": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce",
|
||||
"services/auth/source/oauth2/access_engine_callback_test.go": "sha256:1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580",
|
||||
"services/auth/source/oauth2/access_engine_current_token.go": "sha256:4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8",
|
||||
"services/auth/source/oauth2/access_engine_current_token_test.go": "sha256:58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1",
|
||||
"services/auth/source/oauth2/access_engine_purpose_test.go": "sha256:533dc3153fcf9bfcf7bfc08712c057092c7b3892882f217fb8f839fdbdd99939",
|
||||
"services/auth/source/oauth2/access_engine_test.go": "sha256:9112a142983c0a58c5d40344b58a20fa2c1bdbc7a79b5c586796362839a43b48",
|
||||
"services/auth/source/oauth2/assert_interface_test.go": "sha256:a0db85e38b7ea61e21b0291e55c8b451c89a877bd3db229e35dbb4418458f54f",
|
||||
"services/auth/source/oauth2/current_issuer_fixture_test.go": "sha256:24fb430e72848326c5d9ef42eab5028cf915dd601f43fb06010b32b568908781",
|
||||
"services/auth/source/oauth2/init.go": "sha256:98a8030ed005be67b237d54c3b197a629f8c289e3d74c8edfcf4d740831f87d1",
|
||||
"services/auth/source/oauth2/jwtsigningkey.go": "sha256:0c6e8ac131fd41ad299073ca748dd95e5fd9c044219c4d7f36d50c4ffcd5b266",
|
||||
"services/auth/source/oauth2/jwtsigningkey_test.go": "sha256:3e414a541479741773e48de84be7d5a3b794bcc7289627fbd2e8996832628321",
|
||||
"services/auth/source/oauth2/providers.go": "sha256:7df0051f19932a68d16a33a5d41898e06e9612f839195e27c86802eeebb2e3e9",
|
||||
"services/auth/source/oauth2/providers_base.go": "sha256:643dbc0561ec7aa7a975978ff2c21d256a5ab619b125e8077d2395281f628542",
|
||||
"services/auth/source/oauth2/providers_custom.go": "sha256:265a526d209d86c24cbcfdde4c2d73957aaeaccfd329438c11ce99c5951ebb39",
|
||||
"services/auth/source/oauth2/providers_openid.go": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf",
|
||||
"services/auth/source/oauth2/providers_simple.go": "sha256:53927b6bfc621dd1b68a074b6763abe5b27795d5a50ca6f0555f3e7a04fa1613",
|
||||
"services/auth/source/oauth2/required_claim_values.go": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda",
|
||||
"services/auth/source/oauth2/required_claim_values_test.go": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0",
|
||||
"services/auth/source/oauth2/source.go": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642",
|
||||
"services/auth/source/oauth2/source_authenticate.go": "sha256:c6551f00ef4349ccd6afdbb2cba847067528485ff621c3e507b9aab588bb96b5",
|
||||
"services/auth/source/oauth2/source_callout.go": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c",
|
||||
"services/auth/source/oauth2/source_name.go": "sha256:712943826e01f718ab4ca717df6c5f2171535cb2229dbf8460f542d757ef8d7f",
|
||||
"services/auth/source/oauth2/source_register.go": "sha256:5ad9ccf213881734dc7fe7619cbe6c47cb6a4afdbbfc3e674ab4493857ebbf91",
|
||||
"services/auth/source/oauth2/store.go": "sha256:35b244081569215384de07f8cdc1e83b5545a2e673ca1ac5a2a6ee686cb917a3",
|
||||
"services/auth/source/oauth2/token.go": "sha256:c0a8868f904bb31842202b4e635a76403902adff9e638d5cb581e7e2d4954933",
|
||||
"services/auth/source/oauth2/urlmapping.go": "sha256:f2132408d4a447e766e67495b1ca7b8cc2b47d4eb56b0c44b50ba01fe41ca83e",
|
||||
"third_party/keycape-tokenstatus/LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"third_party/keycape-tokenstatus/go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"third_party/keycape-tokenstatus/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"third_party/keycape-tokenstatus/pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575",
|
||||
"third_party/keycape-tokenstatus/producer-source.json": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 91,
|
||||
"fail": 0,
|
||||
"skip": 0
|
||||
},
|
||||
"failures": [],
|
||||
"real_credentials": false,
|
||||
"native_admission": false,
|
||||
"network": "private empty namespace",
|
||||
"pid_namespace": "private",
|
||||
"home": "empty allowlisted fixture HOME",
|
||||
"compiled_fixture_only": false
|
||||
}
|
||||
|
|
@ -0,0 +1,368 @@
|
|||
{"Time":"2026-10-18T10:09:21.652984262Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"}
|
||||
{"Time":"2026-10-18T10:09:21.739313615Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals"}
|
||||
{"Time":"2026-10-18T10:09:21.739371325Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals\n"}
|
||||
{"Time":"2026-10-18T10:09:21.739438711Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods"}
|
||||
{"Time":"2026-10-18T10:09:21.73958782Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods\n"}
|
||||
{"Time":"2026-10-18T10:09:21.741715043Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.741764647Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.741777307Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field"}
|
||||
{"Time":"2026-10-18T10:09:21.74178288Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742049899Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742065868Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.742077699Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication"}
|
||||
{"Time":"2026-10-18T10:09:21.742083443Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742358226Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742373546Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.742380679Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication"}
|
||||
{"Time":"2026-10-18T10:09:21.742385854Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/future-authentication\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742648437Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/future-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742666354Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.742692657Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at"}
|
||||
{"Time":"2026-10-18T10:09:21.742699258Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/missing-at\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742933319Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/missing-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.742948107Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.742954897Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at"}
|
||||
{"Time":"2026-10-18T10:09:21.742960109Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/string-at\n"}
|
||||
{"Time":"2026-10-18T10:09:21.743202069Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/string-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.74321656Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.743223413Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at"}
|
||||
{"Time":"2026-10-18T10:09:21.743228543Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/fractional-at\n"}
|
||||
{"Time":"2026-10-18T10:09:21.743526239Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/fractional-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.743540874Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.743547538Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa"}
|
||||
{"Time":"2026-10-18T10:09:21.743552632Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa\n"}
|
||||
{"Time":"2026-10-18T10:09:21.744006958Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.744039302Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.744047652Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level"}
|
||||
{"Time":"2026-10-18T10:09:21.744053018Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-level\n"}
|
||||
{"Time":"2026-10-18T10:09:21.744149863Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-level (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.744178835Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.744188817Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods"}
|
||||
{"Time":"2026-10-18T10:09:21.744194426Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods\n"}
|
||||
{"Time":"2026-10-18T10:09:21.74449764Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.744513312Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.744520481Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa"}
|
||||
{"Time":"2026-10-18T10:09:21.744525729Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa\n"}
|
||||
{"Time":"2026-10-18T10:09:21.744781647Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.744796796Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.744803514Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source"}
|
||||
{"Time":"2026-10-18T10:09:21.74480878Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-source\n"}
|
||||
{"Time":"2026-10-18T10:09:21.745064861Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-source (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.745080001Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.74508662Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods"}
|
||||
{"Time":"2026-10-18T10:09:21.745091886Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/object-methods\n"}
|
||||
{"Time":"2026-10-18T10:09:21.745345189Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/object-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.745360347Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.745367011Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods"}
|
||||
{"Time":"2026-10-18T10:09:21.745372223Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/empty-methods\n"}
|
||||
{"Time":"2026-10-18T10:09:21.745616609Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/empty-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.745631749Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.746096245Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.74611162Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:09:21.746119582Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds"}
|
||||
{"Time":"2026-10-18T10:09:21.746124776Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"=== RUN TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds\n"}
|
||||
{"Time":"2026-10-18T10:09:21.746535169Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"--- PASS: TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.746550074Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.746556801Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact"}
|
||||
{"Time":"2026-10-18T10:09:21.746562022Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"=== RUN TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact\n"}
|
||||
{"Time":"2026-10-18T10:09:21.746889384Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"--- PASS: TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.746913803Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.746920963Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient"}
|
||||
{"Time":"2026-10-18T10:09:21.746926427Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient\n"}
|
||||
{"Time":"2026-10-18T10:09:21.746932793Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client"}
|
||||
{"Time":"2026-10-18T10:09:21.746937924Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client\n"}
|
||||
{"Time":"2026-10-18T10:09:21.747143572Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.747159469Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.74716689Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number"}
|
||||
{"Time":"2026-10-18T10:09:21.747185339Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/number\n"}
|
||||
{"Time":"2026-10-18T10:09:21.747471574Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/number (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.747487138Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.747493675Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array"}
|
||||
{"Time":"2026-10-18T10:09:21.74749886Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/array\n"}
|
||||
{"Time":"2026-10-18T10:09:21.747779365Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/array (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.747832224Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.747844001Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty"}
|
||||
{"Time":"2026-10-18T10:09:21.747853387Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty\n"}
|
||||
{"Time":"2026-10-18T10:09:21.748101997Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.748122448Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.748132701Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.748139662Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.748145631Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient"}
|
||||
{"Time":"2026-10-18T10:09:21.748151006Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"=== RUN TestAccessEngineAuthorizedPartyAcceptsAdmittedClient\n"}
|
||||
{"Time":"2026-10-18T10:09:21.748570758Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyAcceptsAdmittedClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:21.748586557Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:21.748593356Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision"}
|
||||
{"Time":"2026-10-18T10:09:21.748617558Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision\n"}
|
||||
{"Time":"2026-10-18T10:09:21.748649082Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins"}
|
||||
{"Time":"2026-10-18T10:09:21.748655106Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins\n"}
|
||||
{"Time":"2026-10-18T10:09:22.227315514Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins (0.48s)\n"}
|
||||
{"Time":"2026-10-18T10:09:22.227380195Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Elapsed":0.48}
|
||||
{"Time":"2026-10-18T10:09:22.22739281Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-18T10:09:22.227397175Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-18T10:09:22.399228426Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners (0.17s)\n"}
|
||||
{"Time":"2026-10-18T10:09:22.399284631Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Elapsed":0.17}
|
||||
{"Time":"2026-10-18T10:09:22.399302693Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-18T10:09:22.399309329Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-18T10:09:22.649647613Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers (0.25s)\n"}
|
||||
{"Time":"2026-10-18T10:09:22.649692816Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Elapsed":0.25}
|
||||
{"Time":"2026-10-18T10:09:22.649701888Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision (0.90s)\n"}
|
||||
{"Time":"2026-10-18T10:09:22.649707244Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Elapsed":0.9}
|
||||
{"Time":"2026-10-18T10:09:22.6497126Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP"}
|
||||
{"Time":"2026-10-18T10:09:22.649717091Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP\n"}
|
||||
{"Time":"2026-10-18T10:09:22.649736469Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny"}
|
||||
{"Time":"2026-10-18T10:09:22.649741356Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny\n"}
|
||||
{"Time":"2026-10-18T10:09:23.055988774Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny (0.41s)\n"}
|
||||
{"Time":"2026-10-18T10:09:23.056034834Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Elapsed":0.41}
|
||||
{"Time":"2026-10-18T10:09:23.0560435Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned"}
|
||||
{"Time":"2026-10-18T10:09:23.05604808Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned\n"}
|
||||
{"Time":"2026-10-18T10:09:23.697794424Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned (0.64s)\n"}
|
||||
{"Time":"2026-10-18T10:09:23.697846571Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Elapsed":0.64}
|
||||
{"Time":"2026-10-18T10:09:23.697856156Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key"}
|
||||
{"Time":"2026-10-18T10:09:23.697861792Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key\n"}
|
||||
{"Time":"2026-10-18T10:09:23.991183953Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key (0.29s)\n"}
|
||||
{"Time":"2026-10-18T10:09:23.991250473Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Elapsed":0.29}
|
||||
{"Time":"2026-10-18T10:09:23.991266668Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin"}
|
||||
{"Time":"2026-10-18T10:09:23.991275713Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin\n"}
|
||||
{"Time":"2026-10-18T10:09:24.321292756Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin (0.33s)\n"}
|
||||
{"Time":"2026-10-18T10:09:24.321336093Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Elapsed":0.33}
|
||||
{"Time":"2026-10-18T10:09:24.32134474Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding"}
|
||||
{"Time":"2026-10-18T10:09:24.321348536Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding\n"}
|
||||
{"Time":"2026-10-18T10:09:24.61751747Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding (0.30s)\n"}
|
||||
{"Time":"2026-10-18T10:09:24.617563557Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Elapsed":0.3}
|
||||
{"Time":"2026-10-18T10:09:24.617576326Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired"}
|
||||
{"Time":"2026-10-18T10:09:24.617582782Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired\n"}
|
||||
{"Time":"2026-10-18T10:09:25.26096403Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired (0.64s)\n"}
|
||||
{"Time":"2026-10-18T10:09:25.261022272Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Elapsed":0.64}
|
||||
{"Time":"2026-10-18T10:09:25.26103918Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP (2.61s)\n"}
|
||||
{"Time":"2026-10-18T10:09:25.261045357Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Elapsed":2.61}
|
||||
{"Time":"2026-10-18T10:09:25.261050758Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP"}
|
||||
{"Time":"2026-10-18T10:09:25.26105533Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP\n"}
|
||||
{"Time":"2026-10-18T10:09:25.261063869Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject"}
|
||||
{"Time":"2026-10-18T10:09:25.261068188Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject\n"}
|
||||
{"Time":"2026-10-18T10:09:25.344178469Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject (0.08s)\n"}
|
||||
{"Time":"2026-10-18T10:09:25.344231662Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Elapsed":0.08}
|
||||
{"Time":"2026-10-18T10:09:25.34424425Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups"}
|
||||
{"Time":"2026-10-18T10:09:25.344251346Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups\n"}
|
||||
{"Time":"2026-10-18T10:09:25.376462508Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups (0.03s)\n"}
|
||||
{"Time":"2026-10-18T10:09:25.376515232Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Elapsed":0.03}
|
||||
{"Time":"2026-10-18T10:09:25.3765436Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider"}
|
||||
{"Time":"2026-10-18T10:09:25.376551247Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider\n"}
|
||||
{"Time":"2026-10-18T10:09:25.616257893Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider (0.24s)\n"}
|
||||
{"Time":"2026-10-18T10:09:25.616310716Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Elapsed":0.24}
|
||||
{"Time":"2026-10-18T10:09:25.616321338Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission"}
|
||||
{"Time":"2026-10-18T10:09:25.616328038Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission\n"}
|
||||
{"Time":"2026-10-18T10:09:25.760690699Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission (0.14s)\n"}
|
||||
{"Time":"2026-10-18T10:09:25.760743441Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Elapsed":0.14}
|
||||
{"Time":"2026-10-18T10:09:25.760755434Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token"}
|
||||
{"Time":"2026-10-18T10:09:25.760761936Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token\n"}
|
||||
{"Time":"2026-10-18T10:09:26.084506958Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token (0.32s)\n"}
|
||||
{"Time":"2026-10-18T10:09:26.084558447Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Elapsed":0.32}
|
||||
{"Time":"2026-10-18T10:09:26.084570241Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user"}
|
||||
{"Time":"2026-10-18T10:09:26.084577293Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user\n"}
|
||||
{"Time":"2026-10-18T10:09:26.198029843Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user (0.11s)\n"}
|
||||
{"Time":"2026-10-18T10:09:26.198080655Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Elapsed":0.11}
|
||||
{"Time":"2026-10-18T10:09:26.198091052Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source"}
|
||||
{"Time":"2026-10-18T10:09:26.198110096Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source\n"}
|
||||
{"Time":"2026-10-18T10:09:26.622334558Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source (0.42s)\n"}
|
||||
{"Time":"2026-10-18T10:09:26.622401448Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Elapsed":0.42}
|
||||
{"Time":"2026-10-18T10:09:26.622416922Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience"}
|
||||
{"Time":"2026-10-18T10:09:26.622425253Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience\n"}
|
||||
{"Time":"2026-10-18T10:09:26.704846966Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience (0.08s)\n"}
|
||||
{"Time":"2026-10-18T10:09:26.704899393Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Elapsed":0.08}
|
||||
{"Time":"2026-10-18T10:09:26.704910321Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token"}
|
||||
{"Time":"2026-10-18T10:09:26.704917283Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token\n"}
|
||||
{"Time":"2026-10-18T10:09:26.977061118Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token (0.27s)\n"}
|
||||
{"Time":"2026-10-18T10:09:26.977127421Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Elapsed":0.27}
|
||||
{"Time":"2026-10-18T10:09:26.977144482Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity"}
|
||||
{"Time":"2026-10-18T10:09:26.977173157Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity\n"}
|
||||
{"Time":"2026-10-18T10:09:27.381016906Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity (0.40s)\n"}
|
||||
{"Time":"2026-10-18T10:09:27.381127412Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Elapsed":0.4}
|
||||
{"Time":"2026-10-18T10:09:27.381153382Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP (2.12s)\n"}
|
||||
{"Time":"2026-10-18T10:09:27.381163156Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Elapsed":2.12}
|
||||
{"Time":"2026-10-18T10:09:27.381188378Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision"}
|
||||
{"Time":"2026-10-18T10:09:27.381196331Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision\n"}
|
||||
{"Time":"2026-10-18T10:09:27.381204419Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing"}
|
||||
{"Time":"2026-10-18T10:09:27.38121056Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing\n"}
|
||||
{"Time":"2026-10-18T10:09:27.771854866Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing (0.39s)\n"}
|
||||
{"Time":"2026-10-18T10:09:27.771908798Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Elapsed":0.39}
|
||||
{"Time":"2026-10-18T10:09:27.771919745Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access"}
|
||||
{"Time":"2026-10-18T10:09:27.7719263Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access\n"}
|
||||
{"Time":"2026-10-18T10:09:27.900679683Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access (0.13s)\n"}
|
||||
{"Time":"2026-10-18T10:09:27.900731668Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Elapsed":0.13}
|
||||
{"Time":"2026-10-18T10:09:27.900743499Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject"}
|
||||
{"Time":"2026-10-18T10:09:27.900751479Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/subject\n"}
|
||||
{"Time":"2026-10-18T10:09:28.34277205Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/subject (0.44s)\n"}
|
||||
{"Time":"2026-10-18T10:09:28.342840978Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Elapsed":0.44}
|
||||
{"Time":"2026-10-18T10:09:28.342850581Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant"}
|
||||
{"Time":"2026-10-18T10:09:28.342856343Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant\n"}
|
||||
{"Time":"2026-10-18T10:09:28.650368344Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant (0.31s)\n"}
|
||||
{"Time":"2026-10-18T10:09:28.650413133Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Elapsed":0.31}
|
||||
{"Time":"2026-10-18T10:09:28.650431979Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal"}
|
||||
{"Time":"2026-10-18T10:09:28.650436713Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/principal\n"}
|
||||
{"Time":"2026-10-18T10:09:28.849675466Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/principal (0.20s)\n"}
|
||||
{"Time":"2026-10-18T10:09:28.849751536Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Elapsed":0.2}
|
||||
{"Time":"2026-10-18T10:09:28.849768732Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance"}
|
||||
{"Time":"2026-10-18T10:09:28.849777695Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance\n"}
|
||||
{"Time":"2026-10-18T10:09:29.118605137Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance (0.27s)\n"}
|
||||
{"Time":"2026-10-18T10:09:29.11865548Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Elapsed":0.27}
|
||||
{"Time":"2026-10-18T10:09:29.118666265Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time"}
|
||||
{"Time":"2026-10-18T10:09:29.118672171Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time\n"}
|
||||
{"Time":"2026-10-18T10:09:29.271436552Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time (0.15s)\n"}
|
||||
{"Time":"2026-10-18T10:09:29.27149511Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Elapsed":0.15}
|
||||
{"Time":"2026-10-18T10:09:29.271516391Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source"}
|
||||
{"Time":"2026-10-18T10:09:29.271520674Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source\n"}
|
||||
{"Time":"2026-10-18T10:09:29.481276211Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source (0.21s)\n"}
|
||||
{"Time":"2026-10-18T10:09:29.481406411Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Elapsed":0.21}
|
||||
{"Time":"2026-10-18T10:09:29.48142807Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at"}
|
||||
{"Time":"2026-10-18T10:09:29.481453517Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at\n"}
|
||||
{"Time":"2026-10-18T10:09:29.545882857Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at (0.06s)\n"}
|
||||
{"Time":"2026-10-18T10:09:29.545953228Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Elapsed":0.06}
|
||||
{"Time":"2026-10-18T10:09:29.545968927Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive"}
|
||||
{"Time":"2026-10-18T10:09:29.54599707Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive\n"}
|
||||
{"Time":"2026-10-18T10:09:29.630397662Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive (0.08s)\n"}
|
||||
{"Time":"2026-10-18T10:09:29.630466121Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Elapsed":0.08}
|
||||
{"Time":"2026-10-18T10:09:29.630484074Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable"}
|
||||
{"Time":"2026-10-18T10:09:29.630493091Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable\n"}
|
||||
{"Time":"2026-10-18T10:09:29.899917373Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable (0.27s)\n"}
|
||||
{"Time":"2026-10-18T10:09:29.899978185Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Elapsed":0.27}
|
||||
{"Time":"2026-10-18T10:09:29.899995733Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status"}
|
||||
{"Time":"2026-10-18T10:09:29.900001306Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status\n"}
|
||||
{"Time":"2026-10-18T10:09:30.468436594Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status (0.57s)\n"}
|
||||
{"Time":"2026-10-18T10:09:30.468505053Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Elapsed":0.57}
|
||||
{"Time":"2026-10-18T10:09:30.468545207Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission"}
|
||||
{"Time":"2026-10-18T10:09:30.468553764Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission\n"}
|
||||
{"Time":"2026-10-18T10:09:30.591377637Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission (0.12s)\n"}
|
||||
{"Time":"2026-10-18T10:09:30.591448693Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Elapsed":0.12}
|
||||
{"Time":"2026-10-18T10:09:30.591460334Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client"}
|
||||
{"Time":"2026-10-18T10:09:30.591467187Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client\n"}
|
||||
{"Time":"2026-10-18T10:09:30.654186655Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client (0.06s)\n"}
|
||||
{"Time":"2026-10-18T10:09:30.654249442Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Elapsed":0.06}
|
||||
{"Time":"2026-10-18T10:09:30.654264339Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key"}
|
||||
{"Time":"2026-10-18T10:09:30.654272878Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key\n"}
|
||||
{"Time":"2026-10-18T10:09:30.930781669Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key (0.28s)\n"}
|
||||
{"Time":"2026-10-18T10:09:30.930837134Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Elapsed":0.28}
|
||||
{"Time":"2026-10-18T10:09:30.9308502Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant"}
|
||||
{"Time":"2026-10-18T10:09:30.930858Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant\n"}
|
||||
{"Time":"2026-10-18T10:09:31.009144574Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant (0.08s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.009224088Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Elapsed":0.08}
|
||||
{"Time":"2026-10-18T10:09:31.009238173Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash"}
|
||||
{"Time":"2026-10-18T10:09:31.009244863Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash\n"}
|
||||
{"Time":"2026-10-18T10:09:31.26552281Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash (0.26s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.265582829Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Elapsed":0.26}
|
||||
{"Time":"2026-10-18T10:09:31.265594335Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision (3.88s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.265617166Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Elapsed":3.88}
|
||||
{"Time":"2026-10-18T10:09:31.265624786Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn"}
|
||||
{"Time":"2026-10-18T10:09:31.265630575Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"=== RUN TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn\n"}
|
||||
{"Time":"2026-10-18T10:09:31.830001811Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"--- PASS: TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn (0.56s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.830063877Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Elapsed":0.56}
|
||||
{"Time":"2026-10-18T10:09:31.830079955Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims"}
|
||||
{"Time":"2026-10-18T10:09:31.830086772Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims\n"}
|
||||
{"Time":"2026-10-18T10:09:31.830291475Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti"}
|
||||
{"Time":"2026-10-18T10:09:31.830311493Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti\n"}
|
||||
{"Time":"2026-10-18T10:09:31.830886983Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.830911844Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.831040715Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope"}
|
||||
{"Time":"2026-10-18T10:09:31.831065612Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope\n"}
|
||||
{"Time":"2026-10-18T10:09:31.83159451Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.831625213Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.831634312Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.831642599Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.831652815Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant"}
|
||||
{"Time":"2026-10-18T10:09:31.831676467Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant","Output":"=== RUN TestVerifiedApplicationIdentityCannotChooseApplicationTenant\n"}
|
||||
{"Time":"2026-10-18T10:09:31.832327892Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant","Output":"--- PASS: TestVerifiedApplicationIdentityCannotChooseApplicationTenant (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.832349308Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.832355584Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE"}
|
||||
{"Time":"2026-10-18T10:09:31.832359226Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE\n"}
|
||||
{"Time":"2026-10-18T10:09:31.8328725Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject"}
|
||||
{"Time":"2026-10-18T10:09:31.832894845Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/empty-subject\n"}
|
||||
{"Time":"2026-10-18T10:09:31.833212965Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/empty-subject (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.833225435Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.833237969Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer"}
|
||||
{"Time":"2026-10-18T10:09:31.833241757Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/issuer\n"}
|
||||
{"Time":"2026-10-18T10:09:31.833643818Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/issuer (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.833663944Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.833672213Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience"}
|
||||
{"Time":"2026-10-18T10:09:31.833683043Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/audience\n"}
|
||||
{"Time":"2026-10-18T10:09:31.833959629Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/audience (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.83397659Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.83398825Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued"}
|
||||
{"Time":"2026-10-18T10:09:31.833993119Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/old-issued\n"}
|
||||
{"Time":"2026-10-18T10:09:31.834352021Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/old-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.834382887Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.834389127Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp"}
|
||||
{"Time":"2026-10-18T10:09:31.83439332Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-exp\n"}
|
||||
{"Time":"2026-10-18T10:09:31.834639223Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-exp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.834662361Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.834670287Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat"}
|
||||
{"Time":"2026-10-18T10:09:31.834674516Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-iat\n"}
|
||||
{"Time":"2026-10-18T10:09:31.835035498Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-iat (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.835060106Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.835066464Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant"}
|
||||
{"Time":"2026-10-18T10:09:31.835070882Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-tenant\n"}
|
||||
{"Time":"2026-10-18T10:09:31.835356365Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-tenant (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.835378286Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.835384129Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal"}
|
||||
{"Time":"2026-10-18T10:09:31.835388313Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-principal\n"}
|
||||
{"Time":"2026-10-18T10:09:31.835612404Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-principal (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.835629771Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.835637292Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance"}
|
||||
{"Time":"2026-10-18T10:09:31.835658437Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-assurance\n"}
|
||||
{"Time":"2026-10-18T10:09:31.835989321Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-assurance (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.836018384Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.8360264Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired"}
|
||||
{"Time":"2026-10-18T10:09:31.836030593Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/expired\n"}
|
||||
{"Time":"2026-10-18T10:09:31.83635345Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/expired (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.836374614Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.836382459Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued"}
|
||||
{"Time":"2026-10-18T10:09:31.836386778Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/future-issued\n"}
|
||||
{"Time":"2026-10-18T10:09:31.836906503Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/future-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.836955665Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.836970129Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp"}
|
||||
{"Time":"2026-10-18T10:09:31.83697617Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp\n"}
|
||||
{"Time":"2026-10-18T10:09:31.837492273Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.837526078Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.83753615Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key"}
|
||||
{"Time":"2026-10-18T10:09:31.83754129Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-key\n"}
|
||||
{"Time":"2026-10-18T10:09:31.83795749Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.837970565Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.837998014Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key"}
|
||||
{"Time":"2026-10-18T10:09:31.838004524Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/unknown-key\n"}
|
||||
{"Time":"2026-10-18T10:09:31.838216042Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/unknown-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.83822826Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.838235537Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type"}
|
||||
{"Time":"2026-10-18T10:09:31.83824001Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-type\n"}
|
||||
{"Time":"2026-10-18T10:09:31.838487353Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-type (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.838500175Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.838512394Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion"}
|
||||
{"Time":"2026-10-18T10:09:31.838518796Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion\n"}
|
||||
{"Time":"2026-10-18T10:09:31.83868245Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.83869484Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.83870218Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none"}
|
||||
{"Time":"2026-10-18T10:09:31.838706553Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/none\n"}
|
||||
{"Time":"2026-10-18T10:09:31.838876068Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/none (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.838890676Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.838898278Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.838903517Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:09:31.838915501Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation"}
|
||||
{"Time":"2026-10-18T10:09:31.838930884Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"=== RUN TestRequiredClaimExactAnyOfAndNoMutation\n"}
|
||||
{"Time":"2026-10-18T10:09:31.839071292Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"--- PASS: TestRequiredClaimExactAnyOfAndNoMutation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.839083213Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.839088785Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation"}
|
||||
{"Time":"2026-10-18T10:09:31.839093132Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"=== RUN TestRequiredClaimConfigurationRefusalsAndLegacyIsolation\n"}
|
||||
{"Time":"2026-10-18T10:09:31.839134566Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"--- PASS: TestRequiredClaimConfigurationRefusalsAndLegacyIsolation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.839143854Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.839169697Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable"}
|
||||
{"Time":"2026-10-18T10:09:31.839178227Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"=== RUN TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable\n"}
|
||||
{"Time":"2026-10-18T10:09:31.845802439Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"--- PASS: TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.845846786Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:09:31.845856254Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval"}
|
||||
{"Time":"2026-10-18T10:09:31.845860267Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"=== RUN TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval\n"}
|
||||
{"Time":"2026-10-18T10:09:31.845991979Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"--- PASS: TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.846005956Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.846012655Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse"}
|
||||
{"Time":"2026-10-18T10:09:31.846016325Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"=== RUN TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse\n"}
|
||||
{"Time":"2026-10-18T10:09:31.846108761Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"--- PASS: TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:09:31.846129861Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:09:31.846155461Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:09:31.854854546Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"ok \tforgejo.org/services/auth/source/oauth2\t10.201s\n"}
|
||||
{"Time":"2026-10-18T10:09:31.854922455Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":10.202}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 36.83,
|
||||
"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-current-token-r1",
|
||||
"/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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/modules",
|
||||
"/work/modules",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-successor-attempt2/gocache",
|
||||
"/work/gocache",
|
||||
"--dir",
|
||||
"/run",
|
||||
"--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",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-successor-r3/go",
|
||||
"/work/go",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-current-token-r1/descriptor-fixtures-attempt7",
|
||||
"/run/forgejo-oidc",
|
||||
"--bind",
|
||||
"/tmp/custodian-forgejo-current-token-r1/descriptor-fixtures-attempt7",
|
||||
"/run/writable-input",
|
||||
"--",
|
||||
"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|TestSensitiveSQL",
|
||||
"-count=1"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"go.mod": "sha256:11fe525ccb3f099aa281738b86097bbd587a99b4640ddae8dd04dd7d046c49f0",
|
||||
"go.sum": "sha256:19c305c7e68de9d3f1bc0dda2de09c5d38597f1ee7fa411c7b36b92467ae1eaf",
|
||||
"services/auth/source/oauth2/access_engine.go": "sha256:d16330043825d0284253e65d28bda2de4edbc633e4887d8c3618a106ee251daa",
|
||||
"services/auth/source/oauth2/access_engine_assurance.go": "sha256:42de751b1e9a243301d566260954813b770558ecb449639acaeab307fe788642",
|
||||
"services/auth/source/oauth2/access_engine_assurance_test.go": "sha256:44e7459f6c1106a264093c19d61d01a8365f6ad22682906552d832ba39d61dd3",
|
||||
"services/auth/source/oauth2/access_engine_azp_test.go": "sha256:5f94f43ade9a79b6b7ef529c80115a8c32acfece425dc8ed3f24e6ad839fc0ce",
|
||||
"services/auth/source/oauth2/access_engine_callback_test.go": "sha256:1cbe378cc20149be7b354484ab3297916733564f03a2441cd276f9039d043580",
|
||||
"services/auth/source/oauth2/access_engine_current_token.go": "sha256:4a2e484ece7cf74686173d5e85c127643287570c2298af135ddfdc7c450b18d8",
|
||||
"services/auth/source/oauth2/access_engine_current_token_test.go": "sha256:58f799c08a7b4638780abc511bd1a7359bd5e691c7805ff54582d02d2f42cae1",
|
||||
"services/auth/source/oauth2/access_engine_purpose_test.go": "sha256:533dc3153fcf9bfcf7bfc08712c057092c7b3892882f217fb8f839fdbdd99939",
|
||||
"services/auth/source/oauth2/access_engine_test.go": "sha256:9112a142983c0a58c5d40344b58a20fa2c1bdbc7a79b5c586796362839a43b48",
|
||||
"services/auth/source/oauth2/assert_interface_test.go": "sha256:a0db85e38b7ea61e21b0291e55c8b451c89a877bd3db229e35dbb4418458f54f",
|
||||
"services/auth/source/oauth2/current_issuer_fixture_test.go": "sha256:24fb430e72848326c5d9ef42eab5028cf915dd601f43fb06010b32b568908781",
|
||||
"services/auth/source/oauth2/init.go": "sha256:98a8030ed005be67b237d54c3b197a629f8c289e3d74c8edfcf4d740831f87d1",
|
||||
"services/auth/source/oauth2/jwtsigningkey.go": "sha256:0c6e8ac131fd41ad299073ca748dd95e5fd9c044219c4d7f36d50c4ffcd5b266",
|
||||
"services/auth/source/oauth2/jwtsigningkey_test.go": "sha256:3e414a541479741773e48de84be7d5a3b794bcc7289627fbd2e8996832628321",
|
||||
"services/auth/source/oauth2/providers.go": "sha256:7df0051f19932a68d16a33a5d41898e06e9612f839195e27c86802eeebb2e3e9",
|
||||
"services/auth/source/oauth2/providers_base.go": "sha256:643dbc0561ec7aa7a975978ff2c21d256a5ab619b125e8077d2395281f628542",
|
||||
"services/auth/source/oauth2/providers_custom.go": "sha256:265a526d209d86c24cbcfdde4c2d73957aaeaccfd329438c11ce99c5951ebb39",
|
||||
"services/auth/source/oauth2/providers_openid.go": "sha256:5dd9b906cce434c774968d57fcc790cb678147345fea4d2e58477ec6eed70bcf",
|
||||
"services/auth/source/oauth2/providers_simple.go": "sha256:53927b6bfc621dd1b68a074b6763abe5b27795d5a50ca6f0555f3e7a04fa1613",
|
||||
"services/auth/source/oauth2/required_claim_values.go": "sha256:29f16d07464bbe12e3f65616b6c1eb60561d04cd8ff89ecbfb56f93dbeaf1cda",
|
||||
"services/auth/source/oauth2/required_claim_values_test.go": "sha256:54a8bd97dc51c524a9adf0c6aefb9a19fa152d234b3bb929bf1a90932662d3d0",
|
||||
"services/auth/source/oauth2/source.go": "sha256:44e99883c2ec479427b3f72c73353fe33936eed2e5c962b2fa40803576aa5642",
|
||||
"services/auth/source/oauth2/source_authenticate.go": "sha256:c6551f00ef4349ccd6afdbb2cba847067528485ff621c3e507b9aab588bb96b5",
|
||||
"services/auth/source/oauth2/source_callout.go": "sha256:6abba69df97afc62467f0328eaa5a6383cfb4900202a081dec4b84a21b37349c",
|
||||
"services/auth/source/oauth2/source_name.go": "sha256:712943826e01f718ab4ca717df6c5f2171535cb2229dbf8460f542d757ef8d7f",
|
||||
"services/auth/source/oauth2/source_register.go": "sha256:5ad9ccf213881734dc7fe7619cbe6c47cb6a4afdbbfc3e674ab4493857ebbf91",
|
||||
"services/auth/source/oauth2/store.go": "sha256:35b244081569215384de07f8cdc1e83b5545a2e673ca1ac5a2a6ee686cb917a3",
|
||||
"services/auth/source/oauth2/token.go": "sha256:c0a8868f904bb31842202b4e635a76403902adff9e638d5cb581e7e2d4954933",
|
||||
"services/auth/source/oauth2/urlmapping.go": "sha256:f2132408d4a447e766e67495b1ca7b8cc2b47d4eb56b0c44b50ba01fe41ca83e",
|
||||
"third_party/keycape-tokenstatus/LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"third_party/keycape-tokenstatus/go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"third_party/keycape-tokenstatus/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"third_party/keycape-tokenstatus/pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575",
|
||||
"third_party/keycape-tokenstatus/producer-source.json": "sha256:3bd9b25b912e7c17b331997ca5fee9da8a7c314f336e09e3c116885954799b73"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 116,
|
||||
"fail": 0,
|
||||
"skip": 0
|
||||
},
|
||||
"failures": [],
|
||||
"real_credentials": false,
|
||||
"native_admission": false,
|
||||
"network": "private empty namespace",
|
||||
"pid_namespace": "private",
|
||||
"home": "empty allowlisted fixture HOME",
|
||||
"compiled_fixture_only": false
|
||||
}
|
||||
|
|
@ -0,0 +1,524 @@
|
|||
{"Time":"2026-10-18T10:18:51.989800388Z","Action":"start","Package":"forgejo.org/services/auth/source/oauth2"}
|
||||
{"Time":"2026-10-18T10:18:52.078314108Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals"}
|
||||
{"Time":"2026-10-18T10:18:52.078368671Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals\n"}
|
||||
{"Time":"2026-10-18T10:18:52.078428248Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level"}
|
||||
{"Time":"2026-10-18T10:18:52.07843502Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-level\n"}
|
||||
{"Time":"2026-10-18T10:18:52.080814438Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-level (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.080866549Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-level","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.080937506Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods"}
|
||||
{"Time":"2026-10-18T10:18:52.080944386Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/empty-methods\n"}
|
||||
{"Time":"2026-10-18T10:18:52.081152369Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/empty-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.081167022Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/empty-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.081246103Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at"}
|
||||
{"Time":"2026-10-18T10:18:52.081257393Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/missing-at\n"}
|
||||
{"Time":"2026-10-18T10:18:52.08160407Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/missing-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.081622717Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/missing-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.081628146Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa"}
|
||||
{"Time":"2026-10-18T10:18:52.08163224Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa\n"}
|
||||
{"Time":"2026-10-18T10:18:52.081969782Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.081988574Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/integer-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.081994021Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods"}
|
||||
{"Time":"2026-10-18T10:18:52.08213377Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/object-methods\n"}
|
||||
{"Time":"2026-10-18T10:18:52.082323696Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/object-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.082345158Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/object-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.082352146Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods"}
|
||||
{"Time":"2026-10-18T10:18:52.082356763Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods\n"}
|
||||
{"Time":"2026-10-18T10:18:52.082638456Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.082653434Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/mixed-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.082658899Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods"}
|
||||
{"Time":"2026-10-18T10:18:52.082663443Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods\n"}
|
||||
{"Time":"2026-10-18T10:18:52.08295553Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.082968443Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/duplicate-methods","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.082980814Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field"}
|
||||
{"Time":"2026-10-18T10:18:52.082985152Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field\n"}
|
||||
{"Time":"2026-10-18T10:18:52.083309953Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.083322063Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/unknown-object-field","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.083331504Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication"}
|
||||
{"Time":"2026-10-18T10:18:52.083335715Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication\n"}
|
||||
{"Time":"2026-10-18T10:18:52.083665395Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.083690562Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/stale-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.083696189Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication"}
|
||||
{"Time":"2026-10-18T10:18:52.083700472Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/future-authentication\n"}
|
||||
{"Time":"2026-10-18T10:18:52.083970944Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/future-authentication (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.083984227Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/future-authentication","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.083992414Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at"}
|
||||
{"Time":"2026-10-18T10:18:52.08399704Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/string-at\n"}
|
||||
{"Time":"2026-10-18T10:18:52.084267531Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/string-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.084279551Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/string-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.084284853Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at"}
|
||||
{"Time":"2026-10-18T10:18:52.0842891Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/fractional-at\n"}
|
||||
{"Time":"2026-10-18T10:18:52.084632666Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/fractional-at (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.084646417Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/fractional-at","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.084652279Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa"}
|
||||
{"Time":"2026-10-18T10:18:52.084656535Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa\n"}
|
||||
{"Time":"2026-10-18T10:18:52.085089939Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.085143159Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/inconsistent-mfa","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.085156992Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source"}
|
||||
{"Time":"2026-10-18T10:18:52.085164593Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"=== RUN TestAccessEngineKeyCapeAssuranceRefusals/wrong-source\n"}
|
||||
{"Time":"2026-10-18T10:18:52.085467134Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals/wrong-source (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.085497116Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals/wrong-source","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.086130296Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Output":"--- PASS: TestAccessEngineKeyCapeAssuranceRefusals (0.01s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.086163885Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeAssuranceRefusals","Elapsed":0.01}
|
||||
{"Time":"2026-10-18T10:18:52.086171433Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds"}
|
||||
{"Time":"2026-10-18T10:18:52.086175617Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"=== RUN TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds\n"}
|
||||
{"Time":"2026-10-18T10:18:52.086638129Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Output":"--- PASS: TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.086650943Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.086656903Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact"}
|
||||
{"Time":"2026-10-18T10:18:52.086660934Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"=== RUN TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact\n"}
|
||||
{"Time":"2026-10-18T10:18:52.086945122Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Output":"--- PASS: TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.086956655Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.086961813Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient"}
|
||||
{"Time":"2026-10-18T10:18:52.086969089Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient\n"}
|
||||
{"Time":"2026-10-18T10:18:52.087092878Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client"}
|
||||
{"Time":"2026-10-18T10:18:52.08710479Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client\n"}
|
||||
{"Time":"2026-10-18T10:18:52.08727234Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.08728481Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/other-client","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.087291014Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number"}
|
||||
{"Time":"2026-10-18T10:18:52.087309878Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/number\n"}
|
||||
{"Time":"2026-10-18T10:18:52.087596221Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/number (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.087607736Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/number","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.087613011Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array"}
|
||||
{"Time":"2026-10-18T10:18:52.08761697Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/array\n"}
|
||||
{"Time":"2026-10-18T10:18:52.087911482Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/array (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.087927335Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/array","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.087938976Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty"}
|
||||
{"Time":"2026-10-18T10:18:52.087943656Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"=== RUN TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty\n"}
|
||||
{"Time":"2026-10-18T10:18:52.088260278Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.088277168Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient/empty","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.088283435Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyRefusesAnotherClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.088288376Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyRefusesAnotherClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.088292822Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient"}
|
||||
{"Time":"2026-10-18T10:18:52.088296888Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"=== RUN TestAccessEngineAuthorizedPartyAcceptsAdmittedClient\n"}
|
||||
{"Time":"2026-10-18T10:18:52.088972Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Output":"--- PASS: TestAccessEngineAuthorizedPartyAcceptsAdmittedClient (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.089012452Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineAuthorizedPartyAcceptsAdmittedClient","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:18:52.089019702Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision"}
|
||||
{"Time":"2026-10-18T10:18:52.089038133Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision\n"}
|
||||
{"Time":"2026-10-18T10:18:52.08904761Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins"}
|
||||
{"Time":"2026-10-18T10:18:52.089052443Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins\n"}
|
||||
{"Time":"2026-10-18T10:18:52.34556438Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins (0.26s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.345612667Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-admins","Elapsed":0.26}
|
||||
{"Time":"2026-10-18T10:18:52.345622153Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners"}
|
||||
{"Time":"2026-10-18T10:18:52.345626878Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners\n"}
|
||||
{"Time":"2026-10-18T10:18:52.583273278Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners (0.24s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.583327761Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-owners","Elapsed":0.24}
|
||||
{"Time":"2026-10-18T10:18:52.583344803Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers"}
|
||||
{"Time":"2026-10-18T10:18:52.583352044Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"=== RUN TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers\n"}
|
||||
{"Time":"2026-10-18T10:18:52.870643054Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers (0.29s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.870695029Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision/forge-coulomb-readers","Elapsed":0.29}
|
||||
{"Time":"2026-10-18T10:18:52.870708041Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Output":"--- PASS: TestAccessEngineActualCallbackAllowsAuthenticatedDecision (0.78s)\n"}
|
||||
{"Time":"2026-10-18T10:18:52.870716878Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackAllowsAuthenticatedDecision","Elapsed":0.78}
|
||||
{"Time":"2026-10-18T10:18:52.870725129Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP"}
|
||||
{"Time":"2026-10-18T10:18:52.870731694Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP\n"}
|
||||
{"Time":"2026-10-18T10:18:52.870755481Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny"}
|
||||
{"Time":"2026-10-18T10:18:52.870762271Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny\n"}
|
||||
{"Time":"2026-10-18T10:18:53.194695978Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny (0.32s)\n"}
|
||||
{"Time":"2026-10-18T10:18:53.194752778Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/deny","Elapsed":0.32}
|
||||
{"Time":"2026-10-18T10:18:53.194761191Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned"}
|
||||
{"Time":"2026-10-18T10:18:53.194765538Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned\n"}
|
||||
{"Time":"2026-10-18T10:18:53.276909407Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned (0.08s)\n"}
|
||||
{"Time":"2026-10-18T10:18:53.276959291Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/unsigned","Elapsed":0.08}
|
||||
{"Time":"2026-10-18T10:18:53.276984801Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key"}
|
||||
{"Time":"2026-10-18T10:18:53.276989643Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key\n"}
|
||||
{"Time":"2026-10-18T10:18:53.801961933Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key (0.52s)\n"}
|
||||
{"Time":"2026-10-18T10:18:53.802010816Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-key","Elapsed":0.52}
|
||||
{"Time":"2026-10-18T10:18:53.802019842Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin"}
|
||||
{"Time":"2026-10-18T10:18:53.802025108Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin\n"}
|
||||
{"Time":"2026-10-18T10:18:54.086698191Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin (0.28s)\n"}
|
||||
{"Time":"2026-10-18T10:18:54.086747578Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-pin","Elapsed":0.28}
|
||||
{"Time":"2026-10-18T10:18:54.086757461Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding"}
|
||||
{"Time":"2026-10-18T10:18:54.086762448Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding\n"}
|
||||
{"Time":"2026-10-18T10:18:54.41909618Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding (0.33s)\n"}
|
||||
{"Time":"2026-10-18T10:18:54.419142754Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/wrong-binding","Elapsed":0.33}
|
||||
{"Time":"2026-10-18T10:18:54.419150184Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired"}
|
||||
{"Time":"2026-10-18T10:18:54.41915986Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"=== RUN TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired\n"}
|
||||
{"Time":"2026-10-18T10:18:54.628540634Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired (0.21s)\n"}
|
||||
{"Time":"2026-10-18T10:18:54.628591816Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP/expired","Elapsed":0.21}
|
||||
{"Time":"2026-10-18T10:18:54.628609778Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Output":"--- PASS: TestAccessEngineActualCallbackRefusesUnverifiedPDP (1.76s)\n"}
|
||||
{"Time":"2026-10-18T10:18:54.628617857Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackRefusesUnverifiedPDP","Elapsed":1.76}
|
||||
{"Time":"2026-10-18T10:18:54.628625098Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP"}
|
||||
{"Time":"2026-10-18T10:18:54.62867215Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP\n"}
|
||||
{"Time":"2026-10-18T10:18:54.628686488Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity"}
|
||||
{"Time":"2026-10-18T10:18:54.628692484Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity\n"}
|
||||
{"Time":"2026-10-18T10:18:55.497486439Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity (0.87s)\n"}
|
||||
{"Time":"2026-10-18T10:18:55.497539767Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/untrusted-identity","Elapsed":0.87}
|
||||
{"Time":"2026-10-18T10:18:55.497550362Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token"}
|
||||
{"Time":"2026-10-18T10:18:55.497555258Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token\n"}
|
||||
{"Time":"2026-10-18T10:18:55.819513064Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token (0.32s)\n"}
|
||||
{"Time":"2026-10-18T10:18:55.819563236Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-token","Elapsed":0.32}
|
||||
{"Time":"2026-10-18T10:18:55.819588394Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user"}
|
||||
{"Time":"2026-10-18T10:18:55.819594039Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user\n"}
|
||||
{"Time":"2026-10-18T10:18:55.981381644Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user (0.16s)\n"}
|
||||
{"Time":"2026-10-18T10:18:55.981437894Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-user","Elapsed":0.16}
|
||||
{"Time":"2026-10-18T10:18:55.981450464Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject"}
|
||||
{"Time":"2026-10-18T10:18:55.981457993Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject\n"}
|
||||
{"Time":"2026-10-18T10:18:56.171524198Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject (0.19s)\n"}
|
||||
{"Time":"2026-10-18T10:18:56.1715774Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/mismatched-subject","Elapsed":0.19}
|
||||
{"Time":"2026-10-18T10:18:56.17158924Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups"}
|
||||
{"Time":"2026-10-18T10:18:56.171596498Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups\n"}
|
||||
{"Time":"2026-10-18T10:18:56.75293653Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups (0.58s)\n"}
|
||||
{"Time":"2026-10-18T10:18:56.753000553Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-groups","Elapsed":0.58}
|
||||
{"Time":"2026-10-18T10:18:56.753014052Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source"}
|
||||
{"Time":"2026-10-18T10:18:56.753022735Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source\n"}
|
||||
{"Time":"2026-10-18T10:18:57.218656261Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source (0.47s)\n"}
|
||||
{"Time":"2026-10-18T10:18:57.218708218Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-source","Elapsed":0.47}
|
||||
{"Time":"2026-10-18T10:18:57.218718543Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider"}
|
||||
{"Time":"2026-10-18T10:18:57.218737497Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider\n"}
|
||||
{"Time":"2026-10-18T10:18:57.319133141Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider (0.10s)\n"}
|
||||
{"Time":"2026-10-18T10:18:57.319184458Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-provider","Elapsed":0.1}
|
||||
{"Time":"2026-10-18T10:18:57.319194422Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission"}
|
||||
{"Time":"2026-10-18T10:18:57.319200356Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission\n"}
|
||||
{"Time":"2026-10-18T10:18:57.76671532Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission (0.45s)\n"}
|
||||
{"Time":"2026-10-18T10:18:57.766758125Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/missing-admission","Elapsed":0.45}
|
||||
{"Time":"2026-10-18T10:18:57.766766232Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token"}
|
||||
{"Time":"2026-10-18T10:18:57.76677074Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token\n"}
|
||||
{"Time":"2026-10-18T10:18:58.299106878Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token (0.53s)\n"}
|
||||
{"Time":"2026-10-18T10:18:58.299209323Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/expired-token","Elapsed":0.53}
|
||||
{"Time":"2026-10-18T10:18:58.299222533Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience"}
|
||||
{"Time":"2026-10-18T10:18:58.299227854Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"=== RUN TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience\n"}
|
||||
{"Time":"2026-10-18T10:18:58.504027985Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience (0.20s)\n"}
|
||||
{"Time":"2026-10-18T10:18:58.504085894Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP/wrong-audience","Elapsed":0.2}
|
||||
{"Time":"2026-10-18T10:18:58.50409813Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Output":"--- PASS: TestAccessEngineActualCallbackChecksIdentityBeforePDP (3.88s)\n"}
|
||||
{"Time":"2026-10-18T10:18:58.504109979Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineActualCallbackChecksIdentityBeforePDP","Elapsed":3.88}
|
||||
{"Time":"2026-10-18T10:18:58.504135029Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision"}
|
||||
{"Time":"2026-10-18T10:18:58.5041419Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision\n"}
|
||||
{"Time":"2026-10-18T10:18:58.504148897Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing"}
|
||||
{"Time":"2026-10-18T10:18:58.504154723Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing\n"}
|
||||
{"Time":"2026-10-18T10:18:58.746895605Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing (0.24s)\n"}
|
||||
{"Time":"2026-10-18T10:18:58.746953694Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing","Elapsed":0.24}
|
||||
{"Time":"2026-10-18T10:18:58.746966445Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access"}
|
||||
{"Time":"2026-10-18T10:18:58.746974028Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access\n"}
|
||||
{"Time":"2026-10-18T10:18:59.493844845Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access (0.75s)\n"}
|
||||
{"Time":"2026-10-18T10:18:59.494402237Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/id-as-access","Elapsed":0.75}
|
||||
{"Time":"2026-10-18T10:18:59.494421209Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject"}
|
||||
{"Time":"2026-10-18T10:18:59.494427467Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/subject\n"}
|
||||
{"Time":"2026-10-18T10:18:59.649238391Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/subject (0.16s)\n"}
|
||||
{"Time":"2026-10-18T10:18:59.64929621Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/subject","Elapsed":0.16}
|
||||
{"Time":"2026-10-18T10:18:59.649309555Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant"}
|
||||
{"Time":"2026-10-18T10:18:59.649317202Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant\n"}
|
||||
{"Time":"2026-10-18T10:19:00.366996003Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant (0.72s)\n"}
|
||||
{"Time":"2026-10-18T10:19:00.367067906Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant","Elapsed":0.72}
|
||||
{"Time":"2026-10-18T10:19:00.367172047Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal"}
|
||||
{"Time":"2026-10-18T10:19:00.367184022Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/principal\n"}
|
||||
{"Time":"2026-10-18T10:19:00.664993465Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/principal (0.30s)\n"}
|
||||
{"Time":"2026-10-18T10:19:00.665053926Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/principal","Elapsed":0.3}
|
||||
{"Time":"2026-10-18T10:19:00.665067236Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance"}
|
||||
{"Time":"2026-10-18T10:19:00.665072357Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance\n"}
|
||||
{"Time":"2026-10-18T10:19:00.966157108Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance (0.30s)\n"}
|
||||
{"Time":"2026-10-18T10:19:00.966199634Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/assurance","Elapsed":0.3}
|
||||
{"Time":"2026-10-18T10:19:00.966209814Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time"}
|
||||
{"Time":"2026-10-18T10:19:00.966213385Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time\n"}
|
||||
{"Time":"2026-10-18T10:19:01.356327497Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time (0.39s)\n"}
|
||||
{"Time":"2026-10-18T10:19:01.356375739Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/authentication-time","Elapsed":0.39}
|
||||
{"Time":"2026-10-18T10:19:01.356392223Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source"}
|
||||
{"Time":"2026-10-18T10:19:01.356398247Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source\n"}
|
||||
{"Time":"2026-10-18T10:19:01.478532824Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source (0.12s)\n"}
|
||||
{"Time":"2026-10-18T10:19:01.478586954Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/tenant-source","Elapsed":0.12}
|
||||
{"Time":"2026-10-18T10:19:01.478598325Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at"}
|
||||
{"Time":"2026-10-18T10:19:01.478605557Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at\n"}
|
||||
{"Time":"2026-10-18T10:19:02.093913306Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at (0.62s)\n"}
|
||||
{"Time":"2026-10-18T10:19:02.093957752Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/issued-at","Elapsed":0.62}
|
||||
{"Time":"2026-10-18T10:19:02.093968149Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive"}
|
||||
{"Time":"2026-10-18T10:19:02.094025562Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive\n"}
|
||||
{"Time":"2026-10-18T10:19:02.606703212Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive (0.51s)\n"}
|
||||
{"Time":"2026-10-18T10:19:02.606755539Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/inactive","Elapsed":0.51}
|
||||
{"Time":"2026-10-18T10:19:02.606766017Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable"}
|
||||
{"Time":"2026-10-18T10:19:02.606771418Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable\n"}
|
||||
{"Time":"2026-10-18T10:19:02.71368809Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable (0.11s)\n"}
|
||||
{"Time":"2026-10-18T10:19:02.713736766Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/unavailable","Elapsed":0.11}
|
||||
{"Time":"2026-10-18T10:19:02.713745711Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status"}
|
||||
{"Time":"2026-10-18T10:19:02.713754385Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status\n"}
|
||||
{"Time":"2026-10-18T10:19:02.94057398Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status (0.23s)\n"}
|
||||
{"Time":"2026-10-18T10:19:02.940616407Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-status","Elapsed":0.23}
|
||||
{"Time":"2026-10-18T10:19:02.940626317Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission"}
|
||||
{"Time":"2026-10-18T10:19:02.940629815Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission\n"}
|
||||
{"Time":"2026-10-18T10:19:03.060902113Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission (0.12s)\n"}
|
||||
{"Time":"2026-10-18T10:19:03.060977533Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-current-admission","Elapsed":0.12}
|
||||
{"Time":"2026-10-18T10:19:03.060995234Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client"}
|
||||
{"Time":"2026-10-18T10:19:03.061002394Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client\n"}
|
||||
{"Time":"2026-10-18T10:19:03.143078475Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client (0.08s)\n"}
|
||||
{"Time":"2026-10-18T10:19:03.143126708Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/wrong-client","Elapsed":0.08}
|
||||
{"Time":"2026-10-18T10:19:03.143134923Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key"}
|
||||
{"Time":"2026-10-18T10:19:03.143139557Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key\n"}
|
||||
{"Time":"2026-10-18T10:19:03.574148238Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key (0.43s)\n"}
|
||||
{"Time":"2026-10-18T10:19:03.574202278Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/withdrawn-key","Elapsed":0.43}
|
||||
{"Time":"2026-10-18T10:19:03.57421382Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant"}
|
||||
{"Time":"2026-10-18T10:19:03.574221016Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant\n"}
|
||||
{"Time":"2026-10-18T10:19:03.705613684Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant (0.13s)\n"}
|
||||
{"Time":"2026-10-18T10:19:03.705662116Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/mismatched-grant","Elapsed":0.13}
|
||||
{"Time":"2026-10-18T10:19:03.705676733Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash"}
|
||||
{"Time":"2026-10-18T10:19:03.70568245Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"=== RUN TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash\n"}
|
||||
{"Time":"2026-10-18T10:19:04.169381464Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash (0.46s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.169434594Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision/missing-at-hash","Elapsed":0.46}
|
||||
{"Time":"2026-10-18T10:19:04.169446893Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Output":"--- PASS: TestAccessEngineCurrentTokenRejectsBeforeDecision (5.67s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.169472484Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRejectsBeforeDecision","Elapsed":5.67}
|
||||
{"Time":"2026-10-18T10:19:04.169481646Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn"}
|
||||
{"Time":"2026-10-18T10:19:04.169487976Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"=== RUN TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn\n"}
|
||||
{"Time":"2026-10-18T10:19:04.487920753Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Output":"--- PASS: TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn (0.32s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.487971764Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn","Elapsed":0.32}
|
||||
{"Time":"2026-10-18T10:19:04.487981683Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims"}
|
||||
{"Time":"2026-10-18T10:19:04.487987336Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims\n"}
|
||||
{"Time":"2026-10-18T10:19:04.487996759Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti"}
|
||||
{"Time":"2026-10-18T10:19:04.488001656Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti\n"}
|
||||
{"Time":"2026-10-18T10:19:04.48837029Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.488398766Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/jti","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.48840525Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope"}
|
||||
{"Time":"2026-10-18T10:19:04.488409903Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"=== RUN TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope\n"}
|
||||
{"Time":"2026-10-18T10:19:04.488661529Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.488679933Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims/scope","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.488686588Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Output":"--- PASS: TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.488691683Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.488699086Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant"}
|
||||
{"Time":"2026-10-18T10:19:04.488722549Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant","Output":"=== RUN TestVerifiedApplicationIdentityCannotChooseApplicationTenant\n"}
|
||||
{"Time":"2026-10-18T10:19:04.48923985Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant","Output":"--- PASS: TestVerifiedApplicationIdentityCannotChooseApplicationTenant (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.489263475Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityCannotChooseApplicationTenant","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.48927004Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE"}
|
||||
{"Time":"2026-10-18T10:19:04.489277606Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE\n"}
|
||||
{"Time":"2026-10-18T10:19:04.489558105Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired"}
|
||||
{"Time":"2026-10-18T10:19:04.489570414Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/expired\n"}
|
||||
{"Time":"2026-10-18T10:19:04.489805115Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/expired (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.489817865Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/expired","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.489823681Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued"}
|
||||
{"Time":"2026-10-18T10:19:04.489828208Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/future-issued\n"}
|
||||
{"Time":"2026-10-18T10:19:04.490059455Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/future-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.490072602Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/future-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.490078274Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued"}
|
||||
{"Time":"2026-10-18T10:19:04.49008281Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/old-issued\n"}
|
||||
{"Time":"2026-10-18T10:19:04.490305139Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/old-issued (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.490323381Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/old-issued","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.490329711Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp"}
|
||||
{"Time":"2026-10-18T10:19:04.49033431Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-exp\n"}
|
||||
{"Time":"2026-10-18T10:19:04.49055287Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-exp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.490565602Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-exp","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.490571292Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance"}
|
||||
{"Time":"2026-10-18T10:19:04.490576657Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-assurance\n"}
|
||||
{"Time":"2026-10-18T10:19:04.490792539Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-assurance (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.490805768Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-assurance","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.490811512Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp"}
|
||||
{"Time":"2026-10-18T10:19:04.490815948Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp\n"}
|
||||
{"Time":"2026-10-18T10:19:04.49109957Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.491129706Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/multi-audience-no-azp","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.491139003Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat"}
|
||||
{"Time":"2026-10-18T10:19:04.491147506Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-iat\n"}
|
||||
{"Time":"2026-10-18T10:19:04.49140787Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-iat (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.491434796Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-iat","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.491443236Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant"}
|
||||
{"Time":"2026-10-18T10:19:04.491449395Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-tenant\n"}
|
||||
{"Time":"2026-10-18T10:19:04.491765251Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-tenant (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.491790409Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-tenant","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.491798218Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal"}
|
||||
{"Time":"2026-10-18T10:19:04.491823962Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/no-principal\n"}
|
||||
{"Time":"2026-10-18T10:19:04.492085156Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/no-principal (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.492102766Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/no-principal","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.49211035Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject"}
|
||||
{"Time":"2026-10-18T10:19:04.49211576Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/empty-subject\n"}
|
||||
{"Time":"2026-10-18T10:19:04.492455984Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/empty-subject (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.492500664Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/empty-subject","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.492512423Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer"}
|
||||
{"Time":"2026-10-18T10:19:04.492518699Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/issuer\n"}
|
||||
{"Time":"2026-10-18T10:19:04.492793526Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/issuer (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.492825447Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/issuer","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.492850795Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience"}
|
||||
{"Time":"2026-10-18T10:19:04.492860155Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/audience\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493240761Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/audience (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493270446Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/audience","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.493277488Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key"}
|
||||
{"Time":"2026-10-18T10:19:04.493282141Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-key\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493573524Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493586798Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.493610107Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key"}
|
||||
{"Time":"2026-10-18T10:19:04.49361522Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/unknown-key\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493712958Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/unknown-key (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493727458Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/unknown-key","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.493734771Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type"}
|
||||
{"Time":"2026-10-18T10:19:04.49373927Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/wrong-type\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493900481Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/wrong-type (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493913123Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/wrong-type","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.493918984Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion"}
|
||||
{"Time":"2026-10-18T10:19:04.493924043Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion\n"}
|
||||
{"Time":"2026-10-18T10:19:04.493955152Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494027182Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/algorithm-confusion","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.494039833Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none"}
|
||||
{"Time":"2026-10-18T10:19:04.494044441Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"=== RUN TestVerifiedApplicationIdentityActualJOSE/none\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494142513Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE/none (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494154794Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE/none","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.494162738Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Output":"--- PASS: TestVerifiedApplicationIdentityActualJOSE (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494168095Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestVerifiedApplicationIdentityActualJOSE","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.494244985Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation"}
|
||||
{"Time":"2026-10-18T10:19:04.49426659Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"=== RUN TestRequiredClaimExactAnyOfAndNoMutation\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494305193Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Output":"--- PASS: TestRequiredClaimExactAnyOfAndNoMutation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494378197Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimExactAnyOfAndNoMutation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.494390676Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation"}
|
||||
{"Time":"2026-10-18T10:19:04.494395257Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"=== RUN TestRequiredClaimConfigurationRefusalsAndLegacyIsolation\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494470867Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Output":"--- PASS: TestRequiredClaimConfigurationRefusalsAndLegacyIsolation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.494485772Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestRequiredClaimConfigurationRefusalsAndLegacyIsolation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.494493599Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable"}
|
||||
{"Time":"2026-10-18T10:19:04.494498324Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"=== RUN TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable\n"}
|
||||
{"Time":"2026-10-18T10:19:04.497538617Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Output":"--- PASS: TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.497593892Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.497634443Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval"}
|
||||
{"Time":"2026-10-18T10:19:04.497649502Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"=== RUN TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval\n"}
|
||||
{"Time":"2026-10-18T10:19:04.497664164Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Output":"--- PASS: TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.49767099Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.497677554Z","Action":"run","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse"}
|
||||
{"Time":"2026-10-18T10:19:04.497683091Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"=== RUN TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse\n"}
|
||||
{"Time":"2026-10-18T10:19:04.497690052Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Output":"--- PASS: TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:04.497696094Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Test":"TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:04.497732893Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:19:04.505037302Z","Action":"output","Package":"forgejo.org/services/auth/source/oauth2","Output":"ok \tforgejo.org/services/auth/source/oauth2\t12.515s\n"}
|
||||
{"Time":"2026-10-18T10:19:04.505080937Z","Action":"pass","Package":"forgejo.org/services/auth/source/oauth2","Elapsed":12.515}
|
||||
{"Time":"2026-10-18T10:19:15.231969207Z","Action":"start","Package":"forgejo.org/cmd"}
|
||||
{"Time":"2026-10-18T10:19:15.411140265Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:15.41127546Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata246684370/attachments\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411285983Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411298409Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata246684370/avatars\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411305578Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:15.41131244Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata246684370/repo-avatars\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411319185Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411325632Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata246684370/repo-archive\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411334Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411340556Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata246684370/packages\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411347102Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411353532Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata246684370/actions_log\n"}
|
||||
{"Time":"2026-10-18T10:19:15.41136006Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:15.411369339Z","Action":"output","Package":"forgejo.org/cmd","Output":"2026/10/18 10:19:15 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build2948449686/b1036/data/actions_artifacts\n"}
|
||||
{"Time":"2026-10-18T10:19:15.53127327Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata"}
|
||||
{"Time":"2026-10-18T10:19:15.531317337Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"=== RUN TestPrivateSecretMetadata\n"}
|
||||
{"Time":"2026-10-18T10:19:15.531364488Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Output":"--- PASS: TestPrivateSecretMetadata (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:15.531381224Z","Action":"pass","Package":"forgejo.org/cmd","Test":"TestPrivateSecretMetadata","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:15.531389412Z","Action":"run","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement"}
|
||||
{"Time":"2026-10-18T10:19:15.531394299Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"=== RUN TestPrivateSecretOpenedFileReplacement\n"}
|
||||
{"Time":"2026-10-18T10:19:15.531806144Z","Action":"output","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Output":"--- PASS: TestPrivateSecretOpenedFileReplacement (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:15.531834386Z","Action":"pass","Package":"forgejo.org/cmd","Test":"TestPrivateSecretOpenedFileReplacement","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:15.532110034Z","Action":"output","Package":"forgejo.org/cmd","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:19:15.561953003Z","Action":"output","Package":"forgejo.org/cmd","Output":"ok \tforgejo.org/cmd\t0.330s\n"}
|
||||
{"Time":"2026-10-18T10:19:15.56200964Z","Action":"pass","Package":"forgejo.org/cmd","Elapsed":0.33}
|
||||
{"Time":"2026-10-18T10:19:18.5199517Z","Action":"start","Package":"forgejo.org/models/auth"}
|
||||
{"Time":"2026-10-18T10:19:18.674772195Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674867092Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2133799640/attachments\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674875082Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674882692Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2133799640/avatars\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674889059Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674895271Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2133799640/repo-avatars\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674901502Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674907634Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2133799640/repo-archive\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674913775Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674919907Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2133799640/packages\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674926237Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674932287Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata2133799640/actions_log\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674982757Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:18.674991233Z","Action":"output","Package":"forgejo.org/models/auth","Output":"2026/10/18 10:19:18 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build2948449686/b1316/data/actions_artifacts\n"}
|
||||
{"Time":"2026-10-18T10:19:18.781028584Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion"}
|
||||
{"Time":"2026-10-18T10:19:18.781094933Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"=== RUN TestSourceOperationDatabaseWriterExclusion\n"}
|
||||
{"Time":"2026-10-18T10:19:18.882373172Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Output":"--- PASS: TestSourceOperationDatabaseWriterExclusion (0.10s)\n"}
|
||||
{"Time":"2026-10-18T10:19:18.882449377Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationDatabaseWriterExclusion","Elapsed":0.1}
|
||||
{"Time":"2026-10-18T10:19:18.882473714Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging"}
|
||||
{"Time":"2026-10-18T10:19:18.882483976Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"=== RUN TestSourceOperationRefusesActualXORMArgumentLogging\n"}
|
||||
{"Time":"2026-10-18T10:19:18.882498286Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 10:19:18.882449 [SQL] BEGIN TRANSACTION [] - 58.387µs\n"}
|
||||
{"Time":"2026-10-18T10:19:18.882507322Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"[xorm] [info] 2026/10/18 10:19:18.882480 [SQL] ROLLBACK [] - 16.041µs\n"}
|
||||
{"Time":"2026-10-18T10:19:18.88251789Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Output":"--- PASS: TestSourceOperationRefusesActualXORMArgumentLogging (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:18.88252853Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesActualXORMArgumentLogging","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:18.883928788Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery"}
|
||||
{"Time":"2026-10-18T10:19:18.883977472Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"=== RUN TestSourceOperationProcessRecovery\n"}
|
||||
{"Time":"2026-10-18T10:19:21.178981559Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Output":"--- PASS: TestSourceOperationProcessRecovery (2.29s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.179041632Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationProcessRecovery","Elapsed":2.29}
|
||||
{"Time":"2026-10-18T10:19:21.179110389Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse"}
|
||||
{"Time":"2026-10-18T10:19:21.17912286Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"=== RUN TestSourceOperationCreateAndInverse\n"}
|
||||
{"Time":"2026-10-18T10:19:21.208394173Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Output":"--- PASS: TestSourceOperationCreateAndInverse (0.03s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.208463534Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationCreateAndInverse","Elapsed":0.03}
|
||||
{"Time":"2026-10-18T10:19:21.208492903Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting"}
|
||||
{"Time":"2026-10-18T10:19:21.208497935Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"=== RUN TestSourceOperationRestoresExactRawPreexisting\n"}
|
||||
{"Time":"2026-10-18T10:19:21.228044834Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Output":"--- PASS: TestSourceOperationRestoresExactRawPreexisting (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.228098487Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRestoresExactRawPreexisting","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.228110083Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting"}
|
||||
{"Time":"2026-10-18T10:19:21.228114871Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"=== RUN TestSourceOperationUnchangedNeverDeletesPreexisting\n"}
|
||||
{"Time":"2026-10-18T10:19:21.251268443Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Output":"--- PASS: TestSourceOperationUnchangedNeverDeletesPreexisting (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.251343737Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationUnchangedNeverDeletesPreexisting","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.251357633Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses"}
|
||||
{"Time":"2026-10-18T10:19:21.251362196Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"=== RUN TestSourceOperationPrivateDriftRefuses\n"}
|
||||
{"Time":"2026-10-18T10:19:21.251378715Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply"}
|
||||
{"Time":"2026-10-18T10:19:21.251384748Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-apply\n"}
|
||||
{"Time":"2026-10-18T10:19:21.275801314Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-apply (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.275848041Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-apply","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.275858096Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse"}
|
||||
{"Time":"2026-10-18T10:19:21.275861982Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"=== RUN TestSourceOperationPrivateDriftRefuses/before-inverse\n"}
|
||||
{"Time":"2026-10-18T10:19:21.296072315Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses/before-inverse (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.2961376Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses/before-inverse","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.296154165Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Output":"--- PASS: TestSourceOperationPrivateDriftRefuses (0.04s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.296162975Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPrivateDriftRefuses","Elapsed":0.04}
|
||||
{"Time":"2026-10-18T10:19:21.296180288Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses"}
|
||||
{"Time":"2026-10-18T10:19:21.296192777Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"=== RUN TestSourceOperationStockCRUDDriftRefuses\n"}
|
||||
{"Time":"2026-10-18T10:19:21.314059542Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Output":"--- PASS: TestSourceOperationStockCRUDDriftRefuses (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.314109579Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationStockCRUDDriftRefuses","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.314117965Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion"}
|
||||
{"Time":"2026-10-18T10:19:21.314122194Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion\n"}
|
||||
{"Time":"2026-10-18T10:19:21.314132573Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user"}
|
||||
{"Time":"2026-10-18T10:19:21.314137388Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/user\n"}
|
||||
{"Time":"2026-10-18T10:19:21.335619279Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/user (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.335674807Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/user","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.335689397Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external"}
|
||||
{"Time":"2026-10-18T10:19:21.335695439Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"=== RUN TestSourceOperationLinkedSourceRefusesDeletion/external\n"}
|
||||
{"Time":"2026-10-18T10:19:21.356168119Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion/external (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.35624665Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion/external","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.356269563Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Output":"--- PASS: TestSourceOperationLinkedSourceRefusesDeletion (0.04s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.356278796Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationLinkedSourceRefusesDeletion","Elapsed":0.04}
|
||||
{"Time":"2026-10-18T10:19:21.35629207Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse"}
|
||||
{"Time":"2026-10-18T10:19:21.356299239Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"=== RUN TestSourceOperationPreparedCancellationAndIDReuse\n"}
|
||||
{"Time":"2026-10-18T10:19:21.377314655Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Output":"--- PASS: TestSourceOperationPreparedCancellationAndIDReuse (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.377380977Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationPreparedCancellationAndIDReuse","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.377395026Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime"}
|
||||
{"Time":"2026-10-18T10:19:21.377400364Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"=== RUN TestSourceOperationRejectsUnsafeRuntime\n"}
|
||||
{"Time":"2026-10-18T10:19:21.395148303Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Output":"--- PASS: TestSourceOperationRejectsUnsafeRuntime (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.3952365Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRejectsUnsafeRuntime","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.395251712Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource"}
|
||||
{"Time":"2026-10-18T10:19:21.39525651Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"=== RUN TestSourceOperationRefusesSharedManagedSource\n"}
|
||||
{"Time":"2026-10-18T10:19:21.415651921Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Output":"--- PASS: TestSourceOperationRefusesSharedManagedSource (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.415709478Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRefusesSharedManagedSource","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.415721534Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody"}
|
||||
{"Time":"2026-10-18T10:19:21.415726845Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"=== RUN TestSourceOperationBoundsPrivateJournalCustody\n"}
|
||||
{"Time":"2026-10-18T10:19:21.436854212Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Output":"--- PASS: TestSourceOperationBoundsPrivateJournalCustody (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.436914023Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationBoundsPrivateJournalCustody","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.436927197Z","Action":"run","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState"}
|
||||
{"Time":"2026-10-18T10:19:21.436933951Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"=== RUN TestSourceOperationRequiresExactPostState\n"}
|
||||
{"Time":"2026-10-18T10:19:21.454743707Z","Action":"output","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Output":"--- PASS: TestSourceOperationRequiresExactPostState (0.02s)\n"}
|
||||
{"Time":"2026-10-18T10:19:21.454797098Z","Action":"pass","Package":"forgejo.org/models/auth","Test":"TestSourceOperationRequiresExactPostState","Elapsed":0.02}
|
||||
{"Time":"2026-10-18T10:19:21.455056263Z","Action":"output","Package":"forgejo.org/models/auth","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:19:21.482025516Z","Action":"output","Package":"forgejo.org/models/auth","Output":"ok \tforgejo.org/models/auth\t2.962s\n"}
|
||||
{"Time":"2026-10-18T10:19:21.48209045Z","Action":"pass","Package":"forgejo.org/models/auth","Elapsed":2.9619999999999997}
|
||||
{"Time":"2026-10-18T10:19:27.132546853Z","Action":"start","Package":"forgejo.org/models/db"}
|
||||
{"Time":"2026-10-18T10:19:27.331217623Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331320329Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata275439258/attachments\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331334712Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...s/storage/storage.go:147:initAvatars() [I] Initialising Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331347318Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata275439258/avatars\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331359157Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...s/storage/storage.go:173:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331389203Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata275439258/repo-avatars\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331405993Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...s/storage/storage.go:179:initRepoArchives() [I] Initialising Repository Archive storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331589142Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata275439258/repo-archive\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331632533Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...s/storage/storage.go:189:initPackages() [I] Initialising Packages storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331650252Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata275439258/packages\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331662534Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...s/storage/storage.go:200:initActions() [I] Initialising Actions storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331674896Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/appdata275439258/actions_log\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331697034Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...s/storage/storage.go:204:initActions() [I] Initialising ActionsArtifacts storage with type: local\n"}
|
||||
{"Time":"2026-10-18T10:19:27.331708125Z","Action":"output","Package":"forgejo.org/models/db","Output":"2026/10/18 10:19:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /tmp/go-build2948449686/b1337/data/actions_artifacts\n"}
|
||||
{"Time":"2026-10-18T10:19:27.450304092Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine"}
|
||||
{"Time":"2026-10-18T10:19:27.450364625Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine\n"}
|
||||
{"Time":"2026-10-18T10:19:27.450392254Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine"}
|
||||
{"Time":"2026-10-18T10:19:27.450407764Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/engine\n"}
|
||||
{"Time":"2026-10-18T10:19:27.450420694Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/engine (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:27.450426258Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/engine","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:27.450435131Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session"}
|
||||
{"Time":"2026-10-18T10:19:27.450439378Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"=== RUN TestSensitiveSQLLoggingChecksUnderlyingEngine/session\n"}
|
||||
{"Time":"2026-10-18T10:19:27.450444734Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine/session (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:27.450449982Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine/session","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:27.450470659Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Output":"--- PASS: TestSensitiveSQLLoggingChecksUnderlyingEngine (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:27.450483527Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLLoggingChecksUnderlyingEngine","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:27.450498324Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks"}
|
||||
{"Time":"2026-10-18T10:19:27.450504861Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"=== RUN TestSensitiveSQLHooks\n"}
|
||||
{"Time":"2026-10-18T10:19:27.454616311Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Output":"--- PASS: TestSensitiveSQLHooks (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:27.454674274Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLHooks","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:27.454689955Z","Action":"run","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation"}
|
||||
{"Time":"2026-10-18T10:19:27.454702931Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"=== RUN TestSensitiveSQLActualContextPropagation\n"}
|
||||
{"Time":"2026-10-18T10:19:27.458389623Z","Action":"output","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Output":"--- PASS: TestSensitiveSQLActualContextPropagation (0.00s)\n"}
|
||||
{"Time":"2026-10-18T10:19:27.458442915Z","Action":"pass","Package":"forgejo.org/models/db","Test":"TestSensitiveSQLActualContextPropagation","Elapsed":0}
|
||||
{"Time":"2026-10-18T10:19:27.458786012Z","Action":"output","Package":"forgejo.org/models/db","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:19:27.485385964Z","Action":"output","Package":"forgejo.org/models/db","Output":"ok \tforgejo.org/models/db\t0.353s\n"}
|
||||
{"Time":"2026-10-18T10:19:27.485434865Z","Action":"pass","Package":"forgejo.org/models/db","Elapsed":0.353}
|
||||
|
|
@ -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,114 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build linux
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var errOAuthSecretFile = errors.New("secret-file input refused")
|
||||
|
||||
// This checks DAC and mount properties, not namespace, Secret provenance or
|
||||
// operation authority. Admission must bind the exact workload/mount/reader set.
|
||||
// Kubernetes Secret subPath leaves are root-owned; fsGroup can provide 0440
|
||||
// access. Requiring euid ownership would incorrectly refuse that supported case.
|
||||
func readOAuthSecretFile(path string) (string, error) {
|
||||
if len(path) > 4096 || !filepath.IsAbs(path) || filepath.Clean(path) != path || strings.ContainsRune(path, 0) {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
|
||||
if len(parts) < 2 {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
dir, err := unix.Open("/", unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
defer func() { unix.Close(dir) }()
|
||||
for _, part := range parts[:len(parts)-1] {
|
||||
var st unix.Stat_t
|
||||
if unix.Fstat(dir, &st) != nil || !safeOAuthSecretParent(st) {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
next, err := unix.Openat(dir, part, unix.O_PATH|unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
unix.Close(dir)
|
||||
dir = next
|
||||
}
|
||||
var parent unix.Stat_t
|
||||
if unix.Fstat(dir, &parent) != nil || !safeOAuthSecretParent(parent) {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
// Nonblocking is necessary: reject a FIFO without waiting for a writer.
|
||||
fd, err := unix.Openat(dir, parts[len(parts)-1], unix.O_RDONLY|unix.O_NOFOLLOW|unix.O_NONBLOCK|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
f := os.NewFile(uintptr(fd), "private OAuth input")
|
||||
defer f.Close()
|
||||
return readOAuthSecretFD(f, dir, parts[len(parts)-1])
|
||||
}
|
||||
|
||||
func readOAuthSecretFD(f *os.File, dir int, leaf string) (string, error) {
|
||||
fd := int(f.Fd())
|
||||
var before, after, named unix.Stat_t
|
||||
var mount unix.Statfs_t
|
||||
if unix.Fstat(fd, &before) != nil || !safeOAuthSecretLeaf(before) || unix.Fstatfs(fd, &mount) != nil || mount.Flags&unix.ST_RDONLY == 0 {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
// mint-secret uses secrets.token_urlsafe(48): exactly 64 ASCII characters,
|
||||
// no padding/newline. Bounded read still detects a concurrent size increase.
|
||||
data, err := io.ReadAll(io.LimitReader(f, 65))
|
||||
defer clear(data)
|
||||
if err != nil || len(data) != 64 {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
for _, b := range data {
|
||||
if !(b >= 'A' && b <= 'Z' || b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b == '_') {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
}
|
||||
if unix.Fstat(fd, &after) != nil || !sameOAuthSecretFile(before, after) || unix.Fstatat(dir, leaf, &named, unix.AT_SYMLINK_NOFOLLOW) != nil || !sameOAuthSecretFile(before, named) {
|
||||
return "", errOAuthSecretFile
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func safeOAuthSecretParent(st unix.Stat_t) bool {
|
||||
return st.Mode&unix.S_IFMT == unix.S_IFDIR && (st.Uid == 0 || st.Uid == uint32(os.Geteuid())) && st.Mode&0022 == 0
|
||||
}
|
||||
|
||||
func safeOAuthSecretLeaf(st unix.Stat_t) bool {
|
||||
if st.Mode&unix.S_IFMT != unix.S_IFREG || st.Nlink != 1 || st.Size != 64 || st.Mode&07337 != 0 || (st.Uid != 0 && st.Uid != uint32(os.Geteuid())) {
|
||||
return false
|
||||
}
|
||||
if st.Mode&0040 != 0 {
|
||||
groups, err := os.Getgroups()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
member := st.Gid == uint32(os.Getegid())
|
||||
for _, gid := range groups {
|
||||
member = member || st.Gid == uint32(gid)
|
||||
}
|
||||
if !member {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func sameOAuthSecretFile(a, b unix.Stat_t) bool {
|
||||
return a.Dev == b.Dev && a.Ino == b.Ino && a.Mode == b.Mode && a.Nlink == b.Nlink && a.Uid == b.Uid && a.Gid == b.Gid && a.Size == b.Size && a.Mtim == b.Mtim && a.Ctim == b.Ctim
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build linux
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrivateSecretMetadata(t *testing.T) {
|
||||
st := unix.Stat_t{Mode: unix.S_IFREG | 0440, Uid: 0, Gid: uint32(os.Getegid()), Nlink: 1, Size: 64}
|
||||
if !safeOAuthSecretLeaf(st) {
|
||||
t.Fatal("root-owned fsGroup-readable Secret subPath refused")
|
||||
}
|
||||
for _, mode := range []uint32{0444, 0640, 0540, 0460, 0441, 0440 | unix.S_ISUID, 0440 | unix.S_ISGID, 0440 | unix.S_ISVTX} {
|
||||
x := st
|
||||
x.Mode = unix.S_IFREG | mode
|
||||
if safeOAuthSecretLeaf(x) {
|
||||
t.Fatalf("unsafe mode accepted: %o", mode)
|
||||
}
|
||||
}
|
||||
for _, mutate := range []func(*unix.Stat_t){func(x *unix.Stat_t) { x.Uid = 123456 }, func(x *unix.Stat_t) { x.Nlink = 2 }, func(x *unix.Stat_t) { x.Size = 63 }, func(x *unix.Stat_t) { x.Mode = unix.S_IFIFO | 0400 }, func(x *unix.Stat_t) { x.Gid = 123456 }} {
|
||||
x := st
|
||||
mutate(&x)
|
||||
if safeOAuthSecretLeaf(x) {
|
||||
t.Fatal("unsafe metadata accepted")
|
||||
}
|
||||
}
|
||||
if !safeOAuthSecretParent(unix.Stat_t{Mode: unix.S_IFDIR | 0755, Uid: 0}) {
|
||||
t.Fatal("root parent refused")
|
||||
}
|
||||
if safeOAuthSecretParent(unix.Stat_t{Mode: unix.S_IFDIR | 0775, Uid: 0}) {
|
||||
t.Fatal("group writable parent accepted")
|
||||
}
|
||||
for _, mutate := range []func(*unix.Stat_t){func(x *unix.Stat_t) { x.Dev++ }, func(x *unix.Stat_t) { x.Ino++ }, func(x *unix.Stat_t) { x.Ctim.Nsec++ }, func(x *unix.Stat_t) { x.Mtim.Nsec++ }, func(x *unix.Stat_t) { x.Size++ }, func(x *unix.Stat_t) { x.Uid++ }, func(x *unix.Stat_t) { x.Gid++ }, func(x *unix.Stat_t) { x.Nlink++ }, func(x *unix.Stat_t) { x.Mode++ }} {
|
||||
x := st
|
||||
mutate(&x)
|
||||
if sameOAuthSecretFile(st, x) {
|
||||
t.Fatal("changed file considered stable")
|
||||
}
|
||||
}
|
||||
x := st
|
||||
x.Atim.Nsec++
|
||||
if !sameOAuthSecretFile(st, x) {
|
||||
t.Fatal("read-induced atime change refused")
|
||||
}
|
||||
}
|
||||
|
||||
// Actual Linux descriptors and namespace read-only bind mounts, not mocked stat
|
||||
// metadata. A writable alias lets the fixture simulate replacement after open.
|
||||
func TestPrivateSecretOpenedFileReplacement(t *testing.T) {
|
||||
if _, err := os.Stat("/run/forgejo-oidc/race"); os.IsNotExist(err) {
|
||||
t.Skip("requires isolated read-only bind fixture")
|
||||
}
|
||||
dir, err := unix.Open("/run/forgejo-oidc", unix.O_PATH|unix.O_DIRECTORY, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer unix.Close(dir)
|
||||
f, err := os.Open("/run/forgejo-oidc/race")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
// Preserve the opened inode with one link; replace only the named leaf.
|
||||
if err := os.Rename("/run/writable-input/race", "/run/writable-input/race-backup"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Rename("/run/writable-input/race-replacement", "/run/writable-input/race"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if value, err := readOAuthSecretFD(f, dir, "race"); err != errOAuthSecretFile || value != "" {
|
||||
t.Fatal("replaced leaf accepted")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !linux
|
||||
|
||||
package cmd
|
||||
|
||||
import "errors"
|
||||
|
||||
func readOAuthSecretFile(string) (string, error) {
|
||||
return "", errors.New("secret-file input refused")
|
||||
}
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
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
|
||||
|
||||
require keycape v0.0.0
|
||||
|
||||
replace keycape => ./third_party/keycape-tokenstatus
|
||||
|
|
@ -0,0 +1,430 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package auth
|
||||
|
||||
// This owner-confined database primitive is NOT an authorization boundary.
|
||||
// No CLI/web/API route exposes it. The admitted runtime must separately obtain
|
||||
// fresh authenticated apply/inverse sessions and retain signed public evidence.
|
||||
// Configurations and private journal rows must never leave database custody.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/timeutil"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSourceOperationConflict = errors.New("source operation requires owner reconciliation")
|
||||
ErrSourceOperationStorage = errors.New("source operation storage failure; reconcile before retry")
|
||||
ErrSourceOperationUnsupported = errors.New("source operation requires standalone SQLite/PostgreSQL transaction with SQL logging disabled")
|
||||
operationIDPattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._:-]{7,127}$`)
|
||||
)
|
||||
|
||||
// Private source rows deliberately bypass Config decoding: decoding and
|
||||
// reserializing can erase unknown fields, including a gate on an older image.
|
||||
type sourceOperationRow struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type Type
|
||||
Name string
|
||||
IsActive bool
|
||||
IsSyncEnabled bool
|
||||
Cfg string `xorm:"TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp
|
||||
UpdatedUnix timeutil.TimeStamp
|
||||
}
|
||||
|
||||
func (sourceOperationRow) TableName() string { return "login_source" }
|
||||
|
||||
// The whole inventory, including private configs, stays in the owner's database.
|
||||
// Neither journal payload nor its digest is a public result.
|
||||
type sourceOperationJournal struct {
|
||||
OperationID string `xorm:"pk VARCHAR(128)"`
|
||||
CaseID string `xorm:"VARCHAR(128)"`
|
||||
TargetName string
|
||||
TargetID int64
|
||||
Phase string
|
||||
BeforeState string `xorm:"TEXT"`
|
||||
DesiredState string `xorm:"TEXT"`
|
||||
PostState string `xorm:"TEXT"`
|
||||
}
|
||||
|
||||
func (sourceOperationJournal) TableName() string { return "login_source_operation" }
|
||||
func init() { db.RegisterModel(new(sourceOperationJournal)) }
|
||||
|
||||
// SourceOperationResult contains only public outcome metadata. An authenticated
|
||||
// session/evidence producer must bind this result to its actual principal,
|
||||
// admitted source revision, exact parameters and current operation session.
|
||||
type SourceOperationResult struct {
|
||||
OperationID string
|
||||
Phase string
|
||||
SourceID int64
|
||||
}
|
||||
|
||||
// sourceOperationTx acquires a database-enforced lock before any source read.
|
||||
// SQLite's first write serializes every database writer, including stock admin
|
||||
// CRUD. PostgreSQL table locks exclude ordinary SQL source and user-link writes.
|
||||
// Privileged DDL, process/provider refresh and release writers are outside this
|
||||
// bounded primitive; they still require owner runtime/release exclusion.
|
||||
func sourceOperationTx(ctx context.Context, f func(context.Context) error) error {
|
||||
if db.InTransaction(ctx) || setting.Database.LogSQL || (!setting.Database.Type.IsSQLite3() && !setting.Database.Type.IsPostgreSQL()) {
|
||||
return ErrSourceOperationUnsupported
|
||||
}
|
||||
err := db.WithTx(db.WithSensitiveSQL(ctx), func(tx context.Context) error {
|
||||
session, ok := db.GetEngine(tx).(*xorm.Session)
|
||||
if !ok || session.Engine().Logger().IsShowSQL() {
|
||||
return ErrSourceOperationUnsupported
|
||||
}
|
||||
if setting.Database.Type.IsPostgreSQL() {
|
||||
if _, err := db.GetEngine(tx).Exec(`LOCK TABLE login_source_operation, login_source, "user", external_login_user IN EXCLUSIVE MODE`); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Even with no matching row, UPDATE starts SQLite's write transaction.
|
||||
if _, err := db.GetEngine(tx).Exec(`UPDATE login_source_operation SET phase = phase WHERE operation_id = ''`); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return f(tx)
|
||||
})
|
||||
if err == nil || errors.Is(err, ErrSourceOperationConflict) || errors.Is(err, ErrSourceOperationUnsupported) || IsErrSourceInUse(err) {
|
||||
return err
|
||||
}
|
||||
return ErrSourceOperationStorage
|
||||
}
|
||||
|
||||
func sourceInventory(ctx context.Context) ([]sourceOperationRow, error) {
|
||||
rows := make([]sourceOperationRow, 0)
|
||||
err := db.GetEngine(ctx).OrderBy("id").Find(&rows)
|
||||
return rows, err
|
||||
}
|
||||
func encodeSourceInventory(rows []sourceOperationRow) string {
|
||||
b, _ := json.Marshal(rows)
|
||||
return string(b)
|
||||
}
|
||||
func decodeSourceInventory(s string) ([]sourceOperationRow, error) {
|
||||
var rows []sourceOperationRow
|
||||
if err := json.Unmarshal([]byte(s), &rows); err != nil {
|
||||
return nil, ErrSourceOperationConflict
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
func sourceJournal(ctx context.Context, operationID string) (*sourceOperationJournal, error) {
|
||||
row := new(sourceOperationJournal)
|
||||
found, err := db.GetEngine(ctx).ID(operationID).Get(row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, ErrSourceOperationConflict
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
func journalResult(j *sourceOperationJournal) SourceOperationResult {
|
||||
return SourceOperationResult{OperationID: j.OperationID, Phase: j.Phase, SourceID: j.TargetID}
|
||||
}
|
||||
|
||||
// PrepareSourceOperation durably captures exact source identity and complete
|
||||
// original inventory before mutation. desired is private owner input, not an
|
||||
// authorization token. targetID=0 requires authoritative name absence. Existing
|
||||
// sources require both exact ID and name. Reuse of an operation ID is refused.
|
||||
func PrepareSourceOperation(ctx context.Context, operationID, caseID string, targetID int64, desired *Source) (SourceOperationResult, error) {
|
||||
var result SourceOperationResult
|
||||
if !operationIDPattern.MatchString(operationID) || !operationIDPattern.MatchString(caseID) || desired == nil || desired.Type != OAuth2 || desired.Name == "" || desired.Cfg == nil || desired.IsSyncEnabled || desired.ID != targetID {
|
||||
return result, ErrSourceOperationConflict
|
||||
}
|
||||
cfg, err := desired.Cfg.ToDB()
|
||||
if err != nil || len(cfg) > 64*1024 || !json.Valid(cfg) {
|
||||
return result, ErrSourceOperationConflict
|
||||
}
|
||||
candidate := sourceOperationRow{ID: targetID, Type: OAuth2, Name: desired.Name, IsActive: desired.IsActive, Cfg: string(cfg)}
|
||||
candidateBytes, _ := json.Marshal(candidate)
|
||||
err = sourceOperationTx(ctx, func(tx context.Context) error {
|
||||
// Bound confidential duplicate custody. Refuse rather than erase journals
|
||||
// with pending recovery or silently expire terminal private snapshots.
|
||||
count, err := db.GetEngine(tx).Count(new(sourceOperationJournal))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count >= 256 {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
active, err := db.GetEngine(tx).Where("phase IN (?, ?, ?)", "prepared", "applied", "unchanged").Count(new(sourceOperationJournal))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if active >= 64 {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
found, err := db.GetEngine(tx).ID(operationID).Exist(new(sourceOperationJournal))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
shared, err := db.GetEngine(tx).Where("target_name = ? AND phase IN (?, ?, ?)", desired.Name, "prepared", "applied", "unchanged").Exist(new(sourceOperationJournal))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if shared {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
rows, err := sourceInventory(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(encodeSourceInventory(rows)) > 1024*1024 {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
matched := false
|
||||
for _, row := range rows {
|
||||
if row.Name == desired.Name || row.ID == targetID {
|
||||
if targetID == 0 || row.ID != targetID || row.Name != desired.Name || row.Type != OAuth2 {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
if targetID != 0 && !matched {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
j := &sourceOperationJournal{OperationID: operationID, CaseID: caseID, TargetName: desired.Name, TargetID: targetID, Phase: "prepared", BeforeState: encodeSourceInventory(rows), DesiredState: string(candidateBytes)}
|
||||
if _, err := db.GetEngine(tx).Insert(j); err != nil {
|
||||
return err
|
||||
}
|
||||
result = journalResult(j)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return SourceOperationResult{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ApplySourceOperation atomically compares every original raw source row,
|
||||
// mutates exactly the target and persists confirmed post-state. A lost commit
|
||||
// acknowledgement is reconciled using the same operation ID, never a fresh ID.
|
||||
// No source registration/network call occurs inside the database transaction.
|
||||
func ApplySourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) {
|
||||
var result SourceOperationResult
|
||||
err := sourceOperationTx(ctx, func(tx context.Context) error {
|
||||
j, err := sourceJournal(tx, operationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if j.CaseID != caseID {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
rows, err := sourceInventory(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if j.Phase == "applied" || j.Phase == "unchanged" {
|
||||
if encodeSourceInventory(rows) != j.PostState {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
result = journalResult(j)
|
||||
return nil
|
||||
}
|
||||
if j.Phase != "prepared" || encodeSourceInventory(rows) != j.BeforeState {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
var desired sourceOperationRow
|
||||
if json.Unmarshal([]byte(j.DesiredState), &desired) != nil {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
unchanged := false
|
||||
for _, row := range rows {
|
||||
if row.ID == j.TargetID {
|
||||
desired.CreatedUnix = row.CreatedUnix
|
||||
desired.UpdatedUnix = row.UpdatedUnix
|
||||
unchanged = reflect.DeepEqual(row, desired)
|
||||
}
|
||||
}
|
||||
if !unchanged {
|
||||
desired.UpdatedUnix = timeutil.TimeStampNow()
|
||||
if j.TargetID == 0 {
|
||||
desired.CreatedUnix = desired.UpdatedUnix
|
||||
if _, err := db.GetEngine(tx).NoAutoTime().Insert(&desired); err != nil {
|
||||
return err
|
||||
}
|
||||
j.TargetID = desired.ID
|
||||
} else {
|
||||
n, err := db.GetEngine(tx).ID(j.TargetID).NoAutoTime().AllCols().Update(&desired)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
}
|
||||
}
|
||||
post, err := sourceInventory(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expected := append([]sourceOperationRow{}, rows...)
|
||||
if !unchanged {
|
||||
replaced := false
|
||||
for i := range expected {
|
||||
if expected[i].ID == desired.ID {
|
||||
expected[i] = desired
|
||||
replaced = true
|
||||
}
|
||||
}
|
||||
if !replaced {
|
||||
expected = append(expected, desired)
|
||||
}
|
||||
sort.Slice(expected, func(i, k int) bool { return expected[i].ID < expected[k].ID })
|
||||
}
|
||||
if !reflect.DeepEqual(post, expected) {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
j.PostState = encodeSourceInventory(post)
|
||||
j.Phase = "applied"
|
||||
if unchanged {
|
||||
j.Phase = "unchanged"
|
||||
}
|
||||
if _, err := db.GetEngine(tx).ID(j.OperationID).AllCols().Update(j); err != nil {
|
||||
return err
|
||||
}
|
||||
result = journalResult(j)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return SourceOperationResult{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// InverseSourceOperation requires fresh external inverse authority on EACH call.
|
||||
// It restores a preexisting source in place; only confirmed case creation can
|
||||
// delete. Any linked local/external account refuses deletion without alteration.
|
||||
func InverseSourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) {
|
||||
var result SourceOperationResult
|
||||
err := sourceOperationTx(ctx, func(tx context.Context) error {
|
||||
j, err := sourceJournal(tx, operationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if j.CaseID != caseID {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
rows, err := sourceInventory(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if j.Phase == "reverted" || j.Phase == "cancelled" {
|
||||
if encodeSourceInventory(rows) != j.BeforeState {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
result = journalResult(j)
|
||||
return nil
|
||||
}
|
||||
if j.Phase == "prepared" {
|
||||
if encodeSourceInventory(rows) != j.BeforeState {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
j.Phase = "cancelled"
|
||||
} else {
|
||||
if (j.Phase != "applied" && j.Phase != "unchanged") || encodeSourceInventory(rows) != j.PostState {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
before, err := decodeSourceInventory(j.BeforeState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var original *sourceOperationRow
|
||||
for i := range before {
|
||||
if before[i].ID == j.TargetID {
|
||||
original = &before[i]
|
||||
}
|
||||
}
|
||||
if original != nil {
|
||||
if _, err := db.GetEngine(tx).ID(j.TargetID).NoAutoTime().AllCols().Update(original); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
for _, table := range []struct{ name, column string }{{"user", "login_source"}, {"external_login_user", "login_source_id"}} {
|
||||
count, err := db.GetEngine(tx).Table(table.name).Where(table.column+" = ?", j.TargetID).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count != 0 {
|
||||
return ErrSourceInUse{ID: j.TargetID}
|
||||
}
|
||||
}
|
||||
n, err := db.GetEngine(tx).ID(j.TargetID).Delete(new(sourceOperationRow))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
}
|
||||
restored, err := sourceInventory(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if encodeSourceInventory(restored) != j.BeforeState {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
j.Phase = "reverted"
|
||||
}
|
||||
if _, err := db.GetEngine(tx).ID(j.OperationID).AllCols().Update(j); err != nil {
|
||||
return err
|
||||
}
|
||||
result = journalResult(j)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return SourceOperationResult{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ReconcileSourceOperation only checks committed journal/current equality. It
|
||||
// never infers that a prepared intent ran and never retries the mutation. The
|
||||
// separately authenticated runtime must call this after an ambiguous result.
|
||||
func ReconcileSourceOperation(ctx context.Context, operationID, caseID string) (SourceOperationResult, error) {
|
||||
var result SourceOperationResult
|
||||
err := sourceOperationTx(ctx, func(tx context.Context) error {
|
||||
j, err := sourceJournal(tx, operationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if j.CaseID != caseID {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
rows, err := sourceInventory(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expected := j.BeforeState
|
||||
switch j.Phase {
|
||||
case "prepared", "reverted", "cancelled":
|
||||
case "applied", "unchanged":
|
||||
expected = j.PostState
|
||||
default:
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
if encodeSourceInventory(rows) != expected {
|
||||
return ErrSourceOperationConflict
|
||||
}
|
||||
result = journalResult(j)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return SourceOperationResult{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func TestSourceOperationDatabaseWriterExclusion(t *testing.T) {
|
||||
// Use DEFERRED transaction mode in the managed engine as well, so this
|
||||
// test detects removal of the primitive's explicit first write.
|
||||
previousContext := db.DefaultContext
|
||||
previousEngine, ok := db.GetEngine(previousContext).(*xorm.Engine)
|
||||
if !ok {
|
||||
t.Fatal("missing actual fixture engine")
|
||||
}
|
||||
managed, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=deferred")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db.SetDefaultEngine(context.Background(), managed)
|
||||
defer func() { db.SetDefaultEngine(previousContext, previousEngine); managed.Close() }()
|
||||
// A second actual connection executes uncooperative raw SQL, so this checks
|
||||
// database exclusion rather than an advisory-lock convention.
|
||||
raw, err := xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=deferred")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer raw.Close()
|
||||
locked := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- sourceOperationTx(db.DefaultContext, func(ctx context.Context) error { close(locked); <-release; return nil })
|
||||
}()
|
||||
select {
|
||||
case <-locked:
|
||||
case err := <-done:
|
||||
t.Fatalf("lock failed: %v", err)
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("lock timed out")
|
||||
}
|
||||
writeDone := make(chan error, 1)
|
||||
go func() { _, err := raw.Exec("UPDATE login_source SET name = name WHERE id = -123"); writeDone <- err }()
|
||||
select {
|
||||
case err := <-writeDone:
|
||||
close(release)
|
||||
<-done
|
||||
t.Fatalf("uncooperative write escaped exclusion: %v", err)
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
}
|
||||
close(release)
|
||||
select {
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("transaction did not finish")
|
||||
}
|
||||
select {
|
||||
case err := <-writeDone:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("writer did not resume")
|
||||
}
|
||||
}
|
||||
func TestSourceOperationRefusesActualXORMArgumentLogging(t *testing.T) {
|
||||
engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine)
|
||||
if !ok {
|
||||
t.Fatal("missing actual engine")
|
||||
}
|
||||
engine.ShowSQL(true)
|
||||
defer engine.ShowSQL(false)
|
||||
called := false
|
||||
err := sourceOperationTx(db.DefaultContext, func(context.Context) error { called = true; return nil })
|
||||
if err != ErrSourceOperationUnsupported || called {
|
||||
t.Fatal("actual SQL argument logger did not fail closed")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
//go:build source_operation_postgres
|
||||
|
||||
package auth
|
||||
|
||||
// Test-only seam: the production transaction body still obtains its real locks
|
||||
// and private query context. No runtime API or authority callback is added.
|
||||
var SourceOperationPostgresTransactionForTest = sourceOperationTx
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
//go:build source_operation_postgres
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/trace"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
auth "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/setting"
|
||||
oauth2 "forgejo.org/services/auth/source/oauth2"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type postgresOperationLog struct {
|
||||
log.Logger
|
||||
mu sync.Mutex
|
||||
entries []string
|
||||
}
|
||||
|
||||
func (l *postgresOperationLog) Log(_ int, _ log.Level, format string, values ...any) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.entries = append(l.entries, fmt.Sprintf(format, values...))
|
||||
}
|
||||
func (l *postgresOperationLog) take() string {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
result := strings.Join(l.entries, "\n")
|
||||
l.entries = nil
|
||||
return result
|
||||
}
|
||||
|
||||
// The real Forgejo engine/driver, registered model schemas and public operation
|
||||
// functions run. Only the database, application configuration and actors are
|
||||
// synthetic. No deployed owner database or authenticated operation is claimed.
|
||||
func postgresOperationDB(t *testing.T, schema string) (context.Context, *xorm.Engine, *xorm.Engine) {
|
||||
t.Helper()
|
||||
require.Equal(t, "/run/source-operation-postgres", os.Getenv("CUSTODIAN_PG_SOCKET"), "explicit isolated socket required")
|
||||
oldSettings, oldContext := setting.Database, db.DefaultContext
|
||||
oldEngine, ok := db.GetEngine(oldContext).(*xorm.Engine)
|
||||
require.True(t, ok)
|
||||
expectedSchema := schema
|
||||
if expectedSchema == "" {
|
||||
expectedSchema = "public"
|
||||
}
|
||||
raw, err := xorm.NewEngine("postgres", "host=/run/source-operation-postgres user=custodian_test dbname=custodian_source sslmode=disable search_path="+expectedSchema)
|
||||
require.NoError(t, err)
|
||||
_, err = raw.Exec(`CREATE SCHEMA IF NOT EXISTS "` + expectedSchema + `"`)
|
||||
require.NoError(t, err)
|
||||
setting.Database.Type = "postgres"
|
||||
setting.Database.Host = "/run/source-operation-postgres"
|
||||
setting.Database.Name = "custodian_source"
|
||||
setting.Database.User = "custodian_test"
|
||||
setting.Database.Passwd = ""
|
||||
setting.Database.Schema = schema
|
||||
setting.Database.SSLMode = "disable"
|
||||
setting.Database.LogSQL = false
|
||||
setting.Database.MaxOpenConns, setting.Database.MaxIdleConns = 8, 2
|
||||
setting.Database.SlowQueryThreshold = 0
|
||||
require.NoError(t, db.InitEngine(context.Background()))
|
||||
engine, ok := db.GetEngine(db.DefaultContext).(*xorm.Engine)
|
||||
require.True(t, ok)
|
||||
t.Cleanup(func() {
|
||||
_ = engine.Close()
|
||||
_ = raw.Close()
|
||||
setting.Database = oldSettings
|
||||
db.SetDefaultEngine(oldContext, oldEngine)
|
||||
})
|
||||
version, err := engine.Query("SELECT current_setting('server_version_num')::integer / 10000 AS major, current_schema() AS schema")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "16", string(version[0]["major"]))
|
||||
require.Equal(t, expectedSchema, string(version[0]["schema"]))
|
||||
require.NoError(t, db.SyncAllTables())
|
||||
auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source))
|
||||
return db.DefaultContext, engine, raw
|
||||
}
|
||||
|
||||
func resetPostgresOperations(t *testing.T, engine *xorm.Engine) {
|
||||
t.Helper()
|
||||
_, err := engine.Exec(`DROP TRIGGER IF EXISTS custodian_source_rewrite ON login_source`)
|
||||
require.NoError(t, err)
|
||||
_, err = engine.Exec(`TRUNCATE login_source_operation, login_source, "user", external_login_user RESTART IDENTITY CASCADE`)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestSourceOperationPostgres(t *testing.T) {
|
||||
for _, profile := range []struct{ name, schema string }{{"default", ""}, {"public", "public"}, {"named", "custodian_named"}} {
|
||||
t.Run(profile.name, func(t *testing.T) {
|
||||
ctx, engine, raw := postgresOperationDB(t, profile.schema)
|
||||
run := func(name string, f func(*testing.T)) {
|
||||
t.Run(name, func(t *testing.T) { resetPostgresOperations(t, engine); f(t) })
|
||||
}
|
||||
run("create-reconcile-inverse", func(t *testing.T) {
|
||||
before := operationRows(t, ctx)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "postgres-create-operation", "postgres-create-case", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
r, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "applied", r.Phase)
|
||||
require.Positive(t, r.SourceID)
|
||||
post := operationRows(t, ctx)
|
||||
again, err := auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, r, again)
|
||||
require.Equal(t, post, operationRows(t, ctx))
|
||||
_, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-wrong-case")
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
r, err = auth.ReconcileSourceOperation(ctx, "postgres-create-operation", "postgres-create-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "applied", r.Phase)
|
||||
r, err = auth.InverseSourceOperation(ctx, "postgres-create-operation", "postgres-create-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "reverted", r.Phase)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
_, err = auth.ApplySourceOperation(ctx, "postgres-create-operation", "postgres-create-case")
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
})
|
||||
run("exact-raw-preexisting-restoration", func(t *testing.T) {
|
||||
s := operationSource()
|
||||
require.NoError(t, auth.CreateSource(ctx, s))
|
||||
_, err := engine.Exec(`UPDATE login_source SET cfg = ? WHERE id = ?`, `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}`, s.ID)
|
||||
require.NoError(t, err)
|
||||
before := operationRows(t, ctx)
|
||||
s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new"
|
||||
_, err = auth.PrepareSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case", s.ID, s)
|
||||
require.NoError(t, err)
|
||||
_, err = auth.ApplySourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case")
|
||||
require.NoError(t, err)
|
||||
_, err = auth.InverseSourceOperation(ctx, "postgres-restore-operation", "postgres-restore-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
})
|
||||
run("unchanged-preexisting-survives", func(t *testing.T) {
|
||||
s := operationSource()
|
||||
require.NoError(t, auth.CreateSource(ctx, s))
|
||||
before := operationRows(t, ctx)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case", s.ID, s)
|
||||
require.NoError(t, err)
|
||||
r, err := auth.ApplySourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "unchanged", r.Phase)
|
||||
_, err = auth.InverseSourceOperation(ctx, "postgres-unchanged-operation", "postgres-unchanged-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
})
|
||||
for _, stage := range []string{"before-apply", "before-inverse"} {
|
||||
run("private-drift-"+stage, func(t *testing.T) {
|
||||
s := operationSource()
|
||||
require.NoError(t, auth.CreateSource(ctx, s))
|
||||
_, err := auth.PrepareSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case", s.ID, s)
|
||||
require.NoError(t, err)
|
||||
if stage == "before-inverse" {
|
||||
_, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
_, err = engine.Exec(`UPDATE login_source SET cfg = cfg || ' ' WHERE id = ?`, s.ID)
|
||||
require.NoError(t, err)
|
||||
before := operationRows(t, ctx)
|
||||
if stage == "before-apply" {
|
||||
_, err = auth.ApplySourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case")
|
||||
} else {
|
||||
_, err = auth.InverseSourceOperation(ctx, "postgres-drift-operation", "postgres-drift-case")
|
||||
}
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
})
|
||||
}
|
||||
for _, kind := range []string{"user", "external"} {
|
||||
run("linked-"+kind+"-refuses-deletion", func(t *testing.T) {
|
||||
_, err := auth.PrepareSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
r, err := auth.ApplySourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case")
|
||||
require.NoError(t, err)
|
||||
if kind == "user" {
|
||||
_, err = engine.Insert(&user_model.User{ID: 1, LowerName: "synthetic", Name: "synthetic", Email: "synthetic@invalid.test", LoginSource: r.SourceID})
|
||||
} else {
|
||||
_, err = engine.Exec(`INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES ('synthetic-external', 1, ?)`, r.SourceID)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
before := operationRows(t, ctx)
|
||||
_, err = auth.InverseSourceOperation(ctx, "postgres-linked-operation", "postgres-linked-case")
|
||||
require.True(t, auth.IsErrSourceInUse(err))
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
var count int64
|
||||
if kind == "user" {
|
||||
count, err = engine.Table("user").Where("login_source = ?", r.SourceID).Count()
|
||||
} else {
|
||||
count, err = engine.Table("external_login_user").Where("login_source_id = ?", r.SourceID).Count()
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, count)
|
||||
})
|
||||
}
|
||||
run("unexpected-trigger-post-state-rolls-back", func(t *testing.T) {
|
||||
before := operationRows(t, ctx)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
_, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_rewrite_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.name := 'unexpected-rewrite'; RETURN NEW; END $$`)
|
||||
require.NoError(t, err)
|
||||
_, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_rewrite_source()`)
|
||||
require.NoError(t, err)
|
||||
_, err = auth.ApplySourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case")
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "postgres-trigger-operation", "postgres-trigger-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "prepared", r.Phase)
|
||||
})
|
||||
run("driver-error-rollback-and-private-diagnostics", func(t *testing.T) {
|
||||
captured := new(postgresOperationLog)
|
||||
engine.AddHook(&db.SlowQueryHook{Logger: captured})
|
||||
engine.AddHook(&db.ErrorQueryHook{Logger: captured})
|
||||
const control = "synthetic-postgres-unmarked-control"
|
||||
_, err := db.GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", control)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, captured.take(), control, "actual unmarked diagnostic control must be observable")
|
||||
_, err = engine.Exec(`CREATE OR REPLACE FUNCTION custodian_fail_source() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION '%', NEW.cfg; END $$`)
|
||||
require.NoError(t, err)
|
||||
_, err = engine.Exec(`CREATE TRIGGER custodian_source_rewrite BEFORE INSERT ON login_source FOR EACH ROW EXECUTE FUNCTION custodian_fail_source()`)
|
||||
require.NoError(t, err)
|
||||
before := operationRows(t, ctx)
|
||||
_, err = auth.PrepareSourceOperation(ctx, "postgres-error-operation", "postgres-error-case", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
captured.take()
|
||||
var tracing bytes.Buffer
|
||||
require.NoError(t, trace.Start(&tracing))
|
||||
_, err = auth.ApplySourceOperation(ctx, "postgres-error-operation", "postgres-error-case")
|
||||
trace.Stop()
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationStorage)
|
||||
messages := captured.take()
|
||||
require.Contains(t, messages, "[Error SQL Query] [owner-confined query]")
|
||||
require.Contains(t, messages, "[Slow SQL Query] [owner-confined query]")
|
||||
require.NotContains(t, messages, "synthetic-private-original")
|
||||
require.False(t, bytes.Contains(tracing.Bytes(), []byte("synthetic-private-original")), "actual PostgreSQL trace disclosed private cfg")
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "postgres-error-operation", "postgres-error-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "prepared", r.Phase)
|
||||
})
|
||||
run("uncooperative-writers-excluded", func(t *testing.T) {
|
||||
locked, release, done := make(chan struct{}), make(chan struct{}), make(chan error, 1)
|
||||
var once sync.Once
|
||||
releaseLock := func() { once.Do(func() { close(release) }) }
|
||||
defer releaseLock()
|
||||
go func() {
|
||||
done <- auth.SourceOperationPostgresTransactionForTest(ctx, func(context.Context) error { close(locked); <-release; return nil })
|
||||
}()
|
||||
select {
|
||||
case <-locked:
|
||||
case err := <-done:
|
||||
t.Fatalf("lock acquisition refused: %v", err)
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("lock acquisition timed out")
|
||||
}
|
||||
statements := []string{`UPDATE login_source_operation SET phase=phase WHERE operation_id='absent'`, `UPDATE login_source SET name=name WHERE id=-1`, `UPDATE "user" SET login_source=login_source WHERE id=-1`, `UPDATE external_login_user SET login_source_id=login_source_id WHERE user_id=-1`}
|
||||
for _, statement := range statements {
|
||||
session := raw.NewSession()
|
||||
require.NoError(t, session.Begin())
|
||||
_, err := session.Exec(`SET LOCAL lock_timeout='150ms'`)
|
||||
require.NoError(t, err)
|
||||
_, err = session.Exec(statement)
|
||||
var pgerr *pq.Error
|
||||
blocked := errors.As(err, &pgerr) && pgerr.Code == "55P03"
|
||||
_ = session.Rollback()
|
||||
_ = session.Close()
|
||||
require.True(t, blocked, "an uncooperative writer escaped the actual table lock")
|
||||
}
|
||||
releaseLock()
|
||||
select {
|
||||
case err := <-done:
|
||||
require.NoError(t, err)
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("transaction did not finish")
|
||||
}
|
||||
for _, statement := range statements {
|
||||
_, err := raw.Exec(statement)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
run("concurrent-preparation-has-one-owner", func(t *testing.T) {
|
||||
start, done := make(chan struct{}), make(chan error, 2)
|
||||
for _, id := range []string{"postgres-concurrent-first", "postgres-concurrent-second"} {
|
||||
go func(id string) {
|
||||
<-start
|
||||
_, err := auth.PrepareSourceOperation(ctx, id, "postgres-concurrent-case", 0, operationSource())
|
||||
done <- err
|
||||
}(id)
|
||||
}
|
||||
close(start)
|
||||
ok, conflict := 0, 0
|
||||
for range 2 {
|
||||
select {
|
||||
case err := <-done:
|
||||
if err == nil {
|
||||
ok++
|
||||
} else if errors.Is(err, auth.ErrSourceOperationConflict) {
|
||||
conflict++
|
||||
} else {
|
||||
t.Fatal("unexpected concurrent preparation error")
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("concurrent preparation timed out")
|
||||
}
|
||||
}
|
||||
require.Equal(t, 1, ok)
|
||||
require.Equal(t, 1, conflict)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceOperationPostgresProcessRecovery(t *testing.T) {
|
||||
step := os.Getenv("CUSTODIAN_PG_STEP")
|
||||
require.NotEmpty(t, step, "explicit synthetic recovery phase required")
|
||||
ctx, engine, _ := postgresOperationDB(t, "custodian_recovery")
|
||||
switch step {
|
||||
case "prepare":
|
||||
resetPostgresOperations(t, engine)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "postgres-process-operation", "postgres-process-case", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
case "crash":
|
||||
engine.AddHook(operationCrashAfterSourceInsert{})
|
||||
_, _ = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case")
|
||||
t.Fatal("actual source insert did not reach the process-death hook")
|
||||
case "recover-prepared":
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "prepared", r.Phase)
|
||||
n, err := engine.Table("login_source").Count()
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, n)
|
||||
_, err = auth.ApplySourceOperation(ctx, "postgres-process-operation", "postgres-process-case")
|
||||
require.NoError(t, err)
|
||||
case "recover-applied":
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "applied", r.Phase)
|
||||
n, err := engine.Table("login_source").Count()
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 1, n)
|
||||
_, err = auth.InverseSourceOperation(ctx, "postgres-process-operation", "postgres-process-case")
|
||||
require.NoError(t, err)
|
||||
case "recover-inverse":
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "postgres-process-operation", "postgres-process-case")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "reverted", r.Phase)
|
||||
n, err := engine.Table("login_source").Count()
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, n)
|
||||
default:
|
||||
t.Fatal("unknown synthetic recovery phase")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
auth "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
oauth2 "forgejo.org/services/auth/source/oauth2"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/contexts"
|
||||
"xorm.io/xorm/names"
|
||||
)
|
||||
|
||||
type operationCrashAfterSourceInsert struct{}
|
||||
|
||||
func (operationCrashAfterSourceInsert) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||
return c.Ctx, nil
|
||||
}
|
||||
func (operationCrashAfterSourceInsert) AfterProcess(c *contexts.ContextHook) error {
|
||||
if c.Err == nil && strings.Contains(c.SQL, "INSERT INTO") && strings.Contains(c.SQL, "login_source") && !strings.Contains(c.SQL, "login_source_operation") {
|
||||
// Process death after the real INSERT, before actual ApplySourceOperation's
|
||||
// post-read/journal update/commit. No artificial operation is substituted.
|
||||
os.Exit(73)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func TestSourceOperationProcessRecovery(t *testing.T) {
|
||||
if step := os.Getenv("SOURCE_OPERATION_CHILD_STEP"); step != "" {
|
||||
e, err := xorm.NewEngine("sqlite3", os.Getenv("SOURCE_OPERATION_CHILD_DB")+"?_txlock=deferred")
|
||||
require.NoError(t, err)
|
||||
e.SetMapper(names.GonicMapper{})
|
||||
db.SetDefaultEngine(context.Background(), e)
|
||||
require.NoError(t, db.SyncAllTables())
|
||||
auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source))
|
||||
ctx := db.DefaultContext
|
||||
switch step {
|
||||
case "prepare":
|
||||
_, err = auth.PrepareSourceOperation(ctx, "operation-process-recovery", "case-process-recovery", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
case "crash":
|
||||
e.AddHook(operationCrashAfterSourceInsert{})
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-process-recovery", "case-process-recovery")
|
||||
require.NoError(t, err)
|
||||
t.Fatal("real insert did not reach crash hook")
|
||||
case "recover-prepared":
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "prepared", r.Phase)
|
||||
n, err := db.GetEngine(ctx).Table("login_source").Count()
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, n)
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-process-recovery", "case-process-recovery")
|
||||
require.NoError(t, err)
|
||||
case "recover-applied":
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "applied", r.Phase)
|
||||
_, err = auth.InverseSourceOperation(ctx, "operation-process-recovery", "case-process-recovery")
|
||||
require.NoError(t, err)
|
||||
case "recover-inverse":
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "operation-process-recovery", "case-process-recovery")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "reverted", r.Phase)
|
||||
n, err := db.GetEngine(ctx).Table("login_source").Count()
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, n)
|
||||
default:
|
||||
t.Fatal("unknown synthetic child step")
|
||||
}
|
||||
require.NoError(t, e.Close())
|
||||
return
|
||||
}
|
||||
exe, err := os.Executable()
|
||||
require.NoError(t, err)
|
||||
path := filepath.Join(t.TempDir(), "owner-private.sqlite")
|
||||
for _, step := range []string{"prepare", "crash", "recover-prepared", "recover-applied", "recover-inverse"} {
|
||||
child := exec.Command(exe, "-test.run=^TestSourceOperationProcessRecovery$", "-test.v")
|
||||
child.Env = append(os.Environ(), "SOURCE_OPERATION_CHILD_STEP="+step, "SOURCE_OPERATION_CHILD_DB="+path)
|
||||
output, err := child.CombinedOutput()
|
||||
if step == "crash" {
|
||||
exit, ok := err.(*exec.ExitError)
|
||||
require.True(t, ok, string(output))
|
||||
require.Equal(t, 73, exit.ExitCode(), string(output))
|
||||
} else {
|
||||
require.NoError(t, err, string(output))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
auth "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/setting"
|
||||
oauth2 "forgejo.org/services/auth/source/oauth2"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func operationDB(t *testing.T) context.Context {
|
||||
t.Helper()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
_, err := db.GetEngine(db.DefaultContext).Exec("DELETE FROM login_source_operation")
|
||||
require.NoError(t, err)
|
||||
auth.RegisterTypeConfig(auth.OAuth2, new(oauth2.Source))
|
||||
return db.DefaultContext
|
||||
}
|
||||
func operationSource() *auth.Source {
|
||||
return &auth.Source{Type: auth.OAuth2, Name: "operation-test", IsActive: false, Cfg: &oauth2.Source{Provider: "openidConnect", ClientID: "synthetic-client", ClientSecret: "synthetic-private-original", RequiredClaimName: "groups", RequiredClaimValues: []string{"g1", "g2", "g3"}, Scopes: []string{"openid", "profile", "groups"}}}
|
||||
}
|
||||
func operationRows(t *testing.T, ctx context.Context) string {
|
||||
t.Helper()
|
||||
rows, err := db.GetEngine(ctx).Query("SELECT * FROM login_source ORDER BY id")
|
||||
require.NoError(t, err)
|
||||
b, err := json.Marshal(rows)
|
||||
require.NoError(t, err)
|
||||
return string(b)
|
||||
}
|
||||
func TestSourceOperationCreateAndInverse(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
before := operationRows(t, ctx)
|
||||
r, err := auth.PrepareSourceOperation(ctx, "operation-create", "case-create", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "prepared", r.Phase)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
r, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "applied", r.Phase)
|
||||
require.Positive(t, r.SourceID)
|
||||
id := r.SourceID
|
||||
post := operationRows(t, ctx)
|
||||
r, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, id, r.SourceID)
|
||||
require.Equal(t, post, operationRows(t, ctx))
|
||||
_, err = auth.InverseSourceOperation(ctx, "operation-create", "wrong-case")
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
r, err = auth.InverseSourceOperation(ctx, "operation-create", "case-create")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "reverted", r.Phase)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
_, err = auth.InverseSourceOperation(ctx, "operation-create", "case-create")
|
||||
require.NoError(t, err)
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-create", "case-create")
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
}
|
||||
func TestSourceOperationRestoresExactRawPreexisting(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
s := operationSource()
|
||||
require.NoError(t, auth.CreateSource(ctx, s))
|
||||
// The old/unknown field must survive restoration byte for byte, including the
|
||||
// private synthetic secret. A typed OAuth2 decoder would silently lose it.
|
||||
raw := `{"Provider":"openidConnect","ClientID":"synthetic-client","ClientSecret":"synthetic-private-original","FutureGuard":{"x":true}}`
|
||||
_, err := db.GetEngine(ctx).Exec("UPDATE login_source SET cfg = ? WHERE id = ?", raw, s.ID)
|
||||
require.NoError(t, err)
|
||||
before := operationRows(t, ctx)
|
||||
s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-new"
|
||||
_, err = auth.PrepareSourceOperation(ctx, "operation-restore", "case-restore", s.ID, s)
|
||||
require.NoError(t, err)
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-restore", "case-restore")
|
||||
require.NoError(t, err)
|
||||
_, err = auth.InverseSourceOperation(ctx, "operation-restore", "case-restore")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
source, err := auth.GetSourceByID(ctx, s.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, s.ID, source.ID)
|
||||
}
|
||||
func TestSourceOperationUnchangedNeverDeletesPreexisting(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
s := operationSource()
|
||||
require.NoError(t, auth.CreateSource(ctx, s))
|
||||
before := operationRows(t, ctx)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-unchanged", "case-unchanged", s.ID, s)
|
||||
require.NoError(t, err)
|
||||
r, err := auth.ApplySourceOperation(ctx, "operation-unchanged", "case-unchanged")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "unchanged", r.Phase)
|
||||
_, err = auth.InverseSourceOperation(ctx, "operation-unchanged", "case-unchanged")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
}
|
||||
func TestSourceOperationPrivateDriftRefuses(t *testing.T) {
|
||||
for _, stage := range []string{"before-apply", "before-inverse"} {
|
||||
t.Run(stage, func(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
s := operationSource()
|
||||
require.NoError(t, auth.CreateSource(ctx, s))
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-private-drift", "case-private-drift", s.ID, s)
|
||||
require.NoError(t, err)
|
||||
if stage == "before-inverse" {
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-private-drift", "case-private-drift")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
s.Cfg.(*oauth2.Source).ClientSecret = "synthetic-private-drift"
|
||||
require.NoError(t, auth.UpdateSource(ctx, s))
|
||||
drift := operationRows(t, ctx)
|
||||
if stage == "before-apply" {
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-private-drift", "case-private-drift")
|
||||
} else {
|
||||
_, err = auth.InverseSourceOperation(ctx, "operation-private-drift", "case-private-drift")
|
||||
}
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
require.NotContains(t, err.Error(), "synthetic-private")
|
||||
require.Equal(t, drift, operationRows(t, ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestSourceOperationStockCRUDDriftRefuses(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-other-writer", "case-other-writer", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
other := operationSource()
|
||||
other.Name = "unrelated-source"
|
||||
require.NoError(t, auth.CreateSource(ctx, other))
|
||||
drift := operationRows(t, ctx)
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-other-writer", "case-other-writer")
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
require.Equal(t, drift, operationRows(t, ctx))
|
||||
}
|
||||
func TestSourceOperationLinkedSourceRefusesDeletion(t *testing.T) {
|
||||
for _, kind := range []string{"user", "external"} {
|
||||
t.Run(kind, func(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-linked", "case-linked", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
r, err := auth.ApplySourceOperation(ctx, "operation-linked", "case-linked")
|
||||
require.NoError(t, err)
|
||||
if kind == "user" {
|
||||
_, err = db.GetEngine(ctx).Exec("UPDATE `user` SET login_source = ? WHERE id = 1", r.SourceID)
|
||||
} else {
|
||||
_, err = db.GetEngine(ctx).Exec("INSERT INTO external_login_user (external_id, user_id, login_source_id) VALUES (?, ?, ?)", "synthetic-external", 1, r.SourceID)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
before := operationRows(t, ctx)
|
||||
_, err = auth.InverseSourceOperation(ctx, "operation-linked", "case-linked")
|
||||
require.True(t, auth.IsErrSourceInUse(err))
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
if kind == "user" {
|
||||
rows, e := db.GetEngine(ctx).Query("SELECT login_source FROM `user` WHERE id = 1")
|
||||
require.NoError(t, e)
|
||||
require.NotEqual(t, []byte("0"), rows[0]["login_source"])
|
||||
} else {
|
||||
n, e := db.GetEngine(ctx).Table("external_login_user").Count()
|
||||
require.NoError(t, e)
|
||||
require.EqualValues(t, 1, n)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestSourceOperationPreparedCancellationAndIDReuse(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
before := operationRows(t, ctx)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-cancelled", "case-cancelled", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
_, err = auth.PrepareSourceOperation(ctx, "operation-cancelled", "case-cancelled", 0, operationSource())
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
r, err := auth.InverseSourceOperation(ctx, "operation-cancelled", "case-cancelled")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "cancelled", r.Phase)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
}
|
||||
func TestSourceOperationRejectsUnsafeRuntime(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
setting.Database.LogSQL = true
|
||||
t.Cleanup(func() { setting.Database.LogSQL = false })
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-no-logging", "case-no-logging", 0, operationSource())
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationUnsupported)
|
||||
setting.Database.LogSQL = false
|
||||
err = db.WithTx(ctx, func(tx context.Context) error {
|
||||
_, err := auth.PrepareSourceOperation(tx, "operation-nested", "case-nested", 0, operationSource())
|
||||
return err
|
||||
})
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationUnsupported)
|
||||
}
|
||||
func TestSourceOperationRefusesSharedManagedSource(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-first-case", "case-first-case", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
r, err := auth.ApplySourceOperation(ctx, "operation-first-case", "case-first-case")
|
||||
require.NoError(t, err)
|
||||
shared := operationSource()
|
||||
shared.ID = r.SourceID
|
||||
_, err = auth.PrepareSourceOperation(ctx, "operation-second-case", "case-second-case", r.SourceID, shared)
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
}
|
||||
func TestSourceOperationBoundsPrivateJournalCustody(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
// A full unresolved-journal budget refuses new preparation, without deleting
|
||||
// any intent or replacing private recovery data with an expiry assumption.
|
||||
for i := 0; i < 64; i++ {
|
||||
_, err := db.GetEngine(ctx).Exec("INSERT INTO login_source_operation (operation_id, case_id, target_name, phase) VALUES (?, ?, ?, ?)", fmt.Sprintf("bounded-operation-%03d", i), "bounded-case", fmt.Sprintf("bounded-target-%03d", i), "prepared")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-budget-full", "case-budget-full", 0, operationSource())
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
n, err := db.GetEngine(ctx).Table("login_source_operation").Count()
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 64, n)
|
||||
}
|
||||
func TestSourceOperationRequiresExactPostState(t *testing.T) {
|
||||
ctx := operationDB(t)
|
||||
before := operationRows(t, ctx)
|
||||
_, err := auth.PrepareSourceOperation(ctx, "operation-post-exact", "case-post-exact", 0, operationSource())
|
||||
require.NoError(t, err)
|
||||
_, err = db.GetEngine(ctx).Exec("CREATE TRIGGER source_operation_test_drift AFTER INSERT ON login_source BEGIN UPDATE login_source SET name = 'unexpected-rewrite' WHERE id = NEW.id; END")
|
||||
require.NoError(t, err)
|
||||
defer db.GetEngine(ctx).Exec("DROP TRIGGER source_operation_test_drift")
|
||||
_, err = auth.ApplySourceOperation(ctx, "operation-post-exact", "case-post-exact")
|
||||
require.ErrorIs(t, err, auth.ErrSourceOperationConflict)
|
||||
require.Equal(t, before, operationRows(t, ctx))
|
||||
r, err := auth.ReconcileSourceOperation(ctx, "operation-post-exact", "case-post-exact")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "prepared", r.Phase)
|
||||
}
|
||||
|
|
@ -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,391 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"runtime/trace"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/contexts"
|
||||
"xorm.io/xorm/names"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver
|
||||
_ "github.com/lib/pq" // Needed for the Postgresql driver
|
||||
)
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []any
|
||||
initFuncs []func() error
|
||||
)
|
||||
|
||||
// Engine represents a xorm engine or session.
|
||||
type Engine interface {
|
||||
Table(tableNameOrBean any) *xorm.Session
|
||||
Count(...any) (int64, error)
|
||||
Decr(column string, arg ...any) *xorm.Session
|
||||
Delete(...any) (int64, error)
|
||||
Truncate(...any) (int64, error)
|
||||
Exec(...any) (sql.Result, error)
|
||||
Find(any, ...any) error
|
||||
Get(beans ...any) (bool, error)
|
||||
ID(any) *xorm.Session
|
||||
In(string, ...any) *xorm.Session
|
||||
Incr(column string, arg ...any) *xorm.Session
|
||||
Insert(...any) (int64, error)
|
||||
Iterate(any, xorm.IterFunc) error
|
||||
IsTableExist(any) (bool, error)
|
||||
Join(joinOperator string, tablename, condition any, args ...any) *xorm.Session
|
||||
SQL(any, ...any) *xorm.Session
|
||||
Where(any, ...any) *xorm.Session
|
||||
Asc(colNames ...string) *xorm.Session
|
||||
Desc(colNames ...string) *xorm.Session
|
||||
Limit(limit int, start ...int) *xorm.Session
|
||||
NoAutoTime() *xorm.Session
|
||||
SumInt(bean any, columnName string) (res int64, err error)
|
||||
Sync(...any) error
|
||||
Select(string) *xorm.Session
|
||||
SetExpr(string, any) *xorm.Session
|
||||
NotIn(string, ...any) *xorm.Session
|
||||
OrderBy(any, ...any) *xorm.Session
|
||||
Exist(...any) (bool, error)
|
||||
Distinct(...string) *xorm.Session
|
||||
Query(...any) ([]map[string][]byte, error)
|
||||
Cols(...string) *xorm.Session
|
||||
Context(ctx context.Context) *xorm.Session
|
||||
Ping() error
|
||||
}
|
||||
|
||||
// TableInfo returns table's information via an object
|
||||
func TableInfo(v any) (*schemas.Table, error) {
|
||||
return x.TableInfo(v)
|
||||
}
|
||||
|
||||
// DumpTables dump tables information
|
||||
func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error {
|
||||
return x.DumpTables(tables, w, tp...)
|
||||
}
|
||||
|
||||
// RegisterModel registers model, if initfunc provided, it will be invoked after data model sync
|
||||
func RegisterModel(bean any, initFunc ...func() error) {
|
||||
tables = append(tables, bean)
|
||||
if len(initFuncs) > 0 && initFunc[0] != nil {
|
||||
initFuncs = append(initFuncs, initFunc[0])
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
gonicNames := []string{"SSL", "UID"}
|
||||
for _, name := range gonicNames {
|
||||
names.LintGonicMapper[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
// newXORMEngine returns a new XORM engine from the configuration
|
||||
func newXORMEngine() (*xorm.Engine, error) {
|
||||
connStr, err := setting.DBConnStr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var engine *xorm.Engine
|
||||
|
||||
if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 {
|
||||
// OK whilst we sort out our schema issues - create a schema aware postgres
|
||||
registerPostgresSchemaDriver()
|
||||
engine, err = xorm.NewEngine("postgresschema", connStr)
|
||||
} else {
|
||||
engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setting.Database.Type.IsMySQL() {
|
||||
engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
|
||||
}
|
||||
engine.SetSchema(setting.Database.Schema)
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
// SyncAllTables sync the schemas of all tables, is required by unit test code
|
||||
func SyncAllTables() error {
|
||||
_, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
|
||||
WarnIfDatabaseColumnMissed: true,
|
||||
}, tables...)
|
||||
return err
|
||||
}
|
||||
|
||||
// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
|
||||
func InitEngine(ctx context.Context) error {
|
||||
xormEngine, err := newXORMEngine()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to database: %w", err)
|
||||
}
|
||||
|
||||
xormEngine.SetMapper(names.GonicMapper{})
|
||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL))
|
||||
xormEngine.ShowSQL(setting.Database.LogSQL)
|
||||
xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns)
|
||||
xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns)
|
||||
xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
|
||||
xormEngine.SetConnMaxIdleTime(setting.Database.ConnMaxIdleTime)
|
||||
xormEngine.SetDefaultContext(ctx)
|
||||
|
||||
if setting.Database.SlowQueryThreshold > 0 {
|
||||
xormEngine.AddHook(&SlowQueryHook{
|
||||
Treshold: setting.Database.SlowQueryThreshold,
|
||||
Logger: log.GetLogger("xorm"),
|
||||
})
|
||||
}
|
||||
|
||||
errorLogger := log.GetLogger("xorm")
|
||||
if setting.IsInTesting {
|
||||
errorLogger = log.GetLogger(log.DEFAULT)
|
||||
}
|
||||
|
||||
xormEngine.AddHook(&ErrorQueryHook{
|
||||
Logger: errorLogger,
|
||||
})
|
||||
|
||||
xormEngine.AddHook(&TracingHook{})
|
||||
|
||||
SetDefaultEngine(ctx, xormEngine)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefaultEngine sets the default engine for db
|
||||
func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
|
||||
x = eng
|
||||
DefaultContext = &Context{
|
||||
Context: ctx,
|
||||
e: x,
|
||||
}
|
||||
}
|
||||
|
||||
// UnsetDefaultEngine closes and unsets the default engine
|
||||
// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now,
|
||||
// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `x` and DefaultContext without close
|
||||
// Global database engine related functions are all racy and there is no graceful close right now.
|
||||
func UnsetDefaultEngine() {
|
||||
if x != nil {
|
||||
_ = x.Close()
|
||||
x = nil
|
||||
}
|
||||
DefaultContext = nil
|
||||
}
|
||||
|
||||
// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
|
||||
// This function must never call .Sync() if the provided migration function fails.
|
||||
// When called from the "doctor" command, the migration function is a version check
|
||||
// that prevents the doctor from fixing anything in the database if the migration level
|
||||
// is different from the expected value.
|
||||
func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
|
||||
if err = InitEngine(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = x.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preprocessDatabaseCollation(x)
|
||||
|
||||
// We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
|
||||
// If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
|
||||
//
|
||||
// Installation should only be being re-run if users want to recover an old database.
|
||||
// However, we should think carefully about should we support re-install on an installed instance,
|
||||
// as there may be other problems due to secret reinitialization.
|
||||
if err = migrateFunc(x); err != nil {
|
||||
return fmt.Errorf("migrate: %w", err)
|
||||
}
|
||||
|
||||
if err = SyncAllTables(); err != nil {
|
||||
return fmt.Errorf("sync database struct error: %w", err)
|
||||
}
|
||||
|
||||
for _, initFunc := range initFuncs {
|
||||
if err := initFunc(); err != nil {
|
||||
return fmt.Errorf("initFunc failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NamesToBean return a list of beans or an error
|
||||
func NamesToBean(names ...string) ([]any, error) {
|
||||
beans := []any{}
|
||||
if len(names) == 0 {
|
||||
beans = append(beans, tables...)
|
||||
return beans, nil
|
||||
}
|
||||
// Need to map provided names to beans...
|
||||
beanMap := make(map[string]any)
|
||||
for _, bean := range tables {
|
||||
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
|
||||
beanMap[strings.ToLower(x.TableName(bean))] = bean
|
||||
beanMap[strings.ToLower(x.TableName(bean, true))] = bean
|
||||
}
|
||||
|
||||
gotBean := make(map[any]bool)
|
||||
for _, name := range names {
|
||||
bean, ok := beanMap[strings.ToLower(strings.TrimSpace(name))]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no table found that matches: %s", name)
|
||||
}
|
||||
if !gotBean[bean] {
|
||||
beans = append(beans, bean)
|
||||
gotBean[bean] = true
|
||||
}
|
||||
}
|
||||
return beans, nil
|
||||
}
|
||||
|
||||
// DumpDatabase dumps all data from database according the special database SQL syntax to file system.
|
||||
func DumpDatabase(filePath, dbType string) error {
|
||||
var tbs []*schemas.Table
|
||||
for _, t := range tables {
|
||||
t, err := x.TableInfo(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tbs = append(tbs, t)
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Version int64
|
||||
}
|
||||
t, err := x.TableInfo(&Version{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tbs = append(tbs, t)
|
||||
|
||||
if len(dbType) > 0 {
|
||||
return x.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
|
||||
}
|
||||
return x.DumpTablesToFile(tbs, filePath)
|
||||
}
|
||||
|
||||
// MaxBatchInsertSize returns the table's max batch insert size
|
||||
func MaxBatchInsertSize(bean any) int {
|
||||
t, err := x.TableInfo(bean)
|
||||
if err != nil {
|
||||
return 50
|
||||
}
|
||||
return 999 / len(t.ColumnsSeq())
|
||||
}
|
||||
|
||||
// IsTableNotEmpty returns true if table has at least one record
|
||||
func IsTableNotEmpty(beanOrTableName any) (bool, error) {
|
||||
return x.Table(beanOrTableName).Exist()
|
||||
}
|
||||
|
||||
// DeleteAllRecords will delete all the records of this table
|
||||
func DeleteAllRecords(tableName string) error {
|
||||
_, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetMaxID will return max id of the table
|
||||
func GetMaxID(beanOrTableName any) (maxID int64, err error) {
|
||||
_, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
|
||||
return maxID, err
|
||||
}
|
||||
|
||||
func SetLogSQL(ctx context.Context, on bool) {
|
||||
e := GetEngine(ctx)
|
||||
if x, ok := e.(*xorm.Engine); ok {
|
||||
x.ShowSQL(on)
|
||||
} else if sess, ok := e.(*xorm.Session); ok {
|
||||
sess.Engine().ShowSQL(on)
|
||||
}
|
||||
}
|
||||
|
||||
type TracingHook struct{}
|
||||
|
||||
var _ contexts.Hook = &TracingHook{}
|
||||
|
||||
type sqlTask struct{}
|
||||
|
||||
func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||
ctx, task := trace.NewTask(c.Ctx, "sql")
|
||||
ctx = context.WithValue(ctx, sqlTask{}, task)
|
||||
// XORM passes the same ContextHook to every BeforeProcess hook. Publish
|
||||
// the task there too, so a later hook returning c.Ctx retains it.
|
||||
c.Ctx = ctx
|
||||
if isSensitiveSQL(c.Ctx) {
|
||||
trace.Log(ctx, "query", "[owner-confined query]")
|
||||
} else {
|
||||
trace.Log(ctx, "query", c.SQL)
|
||||
trace.Logf(ctx, "args", "%v", c.Args)
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (TracingHook) AfterProcess(c *contexts.ContextHook) error {
|
||||
c.Ctx.Value(sqlTask{}).(*trace.Task).End()
|
||||
return nil
|
||||
}
|
||||
|
||||
type SlowQueryHook struct {
|
||||
Treshold time.Duration
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
var _ contexts.Hook = &SlowQueryHook{}
|
||||
|
||||
func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||
return c.Ctx, nil
|
||||
}
|
||||
|
||||
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
|
||||
if c.ExecuteTime >= h.Treshold {
|
||||
if isSensitiveSQL(c.Ctx) {
|
||||
h.Logger.Log(8, log.WARN, "[Slow SQL Query] [owner-confined query] - %v", c.ExecuteTime)
|
||||
} else {
|
||||
h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ErrorQueryHook struct {
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
var _ contexts.Hook = &ErrorQueryHook{}
|
||||
|
||||
func (ErrorQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||
return c.Ctx, nil
|
||||
}
|
||||
|
||||
func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error {
|
||||
if c.Err != nil && !errors.Is(c.Err, context.Canceled) {
|
||||
if isSensitiveSQL(c.Ctx) {
|
||||
h.Logger.Log(8, log.ERROR, "[Error SQL Query] [owner-confined query]")
|
||||
} else {
|
||||
h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/trace"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"forgejo.org/modules/log"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/contexts"
|
||||
)
|
||||
|
||||
type sensitiveSQLLog struct {
|
||||
log.Logger
|
||||
entries []string
|
||||
}
|
||||
|
||||
func (l *sensitiveSQLLog) Log(_ int, _ log.Level, f string, v ...any) {
|
||||
l.entries = append(l.entries, fmt.Sprintf(f, v...))
|
||||
}
|
||||
func TestSensitiveSQLHooks(t *testing.T) {
|
||||
const secret = "synthetic-query-private-sentinel"
|
||||
for _, sensitive := range []bool{false, true} {
|
||||
ctx := context.Background()
|
||||
if sensitive {
|
||||
ctx = WithSensitiveSQL(ctx)
|
||||
}
|
||||
h := &contexts.ContextHook{Ctx: ctx, SQL: "SELECT '" + secret + "'", Args: []any{secret}, Err: errors.New(secret), ExecuteTime: time.Second}
|
||||
logger := new(sensitiveSQLLog)
|
||||
if err := (&SlowQueryHook{Logger: logger}).AfterProcess(h); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := (&ErrorQueryHook{Logger: logger}).AfterProcess(h); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found := strings.Contains(strings.Join(logger.entries, "\n"), secret)
|
||||
if found == sensitive {
|
||||
t.Fatal("query hook disclosure/redaction mismatch")
|
||||
}
|
||||
var out bytes.Buffer
|
||||
if err := trace.Start(&out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
next, err := (TracingHook{}).BeforeProcess(h)
|
||||
if err != nil {
|
||||
trace.Stop()
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.Ctx = next
|
||||
err = (TracingHook{}).AfterProcess(h)
|
||||
trace.Stop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive {
|
||||
t.Fatal("trace disclosure/redaction mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSensitiveSQLActualContextPropagation(t *testing.T) {
|
||||
engine, ok := GetEngine(DefaultContext).(*xorm.Engine)
|
||||
if !ok {
|
||||
t.Fatal("missing actual fixture engine")
|
||||
}
|
||||
logger := new(sensitiveSQLLog)
|
||||
engine.AddHook(&SlowQueryHook{Logger: logger})
|
||||
engine.AddHook(&ErrorQueryHook{Logger: logger})
|
||||
engine.AddHook(TracingHook{})
|
||||
const secret = "synthetic-actual-xorm-private-sentinel"
|
||||
for _, sensitive := range []bool{false, true} {
|
||||
logger.entries = nil
|
||||
ctx := context.Context(DefaultContext)
|
||||
if sensitive {
|
||||
ctx = WithSensitiveSQL(ctx)
|
||||
}
|
||||
var out bytes.Buffer
|
||||
if err := trace.Start(&out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", secret)
|
||||
trace.Stop()
|
||||
if err == nil {
|
||||
t.Fatal("expected actual SQLite function refusal")
|
||||
}
|
||||
if len(logger.entries) < 2 {
|
||||
t.Fatal("actual query hooks did not execute")
|
||||
}
|
||||
if strings.Contains(strings.Join(logger.entries, "\n"), secret) == sensitive {
|
||||
t.Fatal("actual XORM query context did not preserve redaction")
|
||||
}
|
||||
if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive {
|
||||
t.Fatal("actual XORM trace context did not preserve redaction")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"runtime/trace"
|
||||
"testing"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/contexts"
|
||||
)
|
||||
|
||||
type followingQueryHook struct{}
|
||||
|
||||
func (followingQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||
return c.Ctx, nil
|
||||
}
|
||||
func (followingQueryHook) AfterProcess(*contexts.ContextHook) error { return nil }
|
||||
|
||||
func TestTracingContextSurvivesFollowingHooks(t *testing.T) {
|
||||
for _, sensitive := range []bool{false, true} {
|
||||
for _, fail := range []bool{false, true} {
|
||||
engine, err := xorm.NewEngine("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
engine.AddHook(TracingHook{})
|
||||
engine.AddHook(followingQueryHook{})
|
||||
ctx := context.Background()
|
||||
if sensitive {
|
||||
ctx = WithSensitiveSQL(ctx)
|
||||
}
|
||||
var output bytes.Buffer
|
||||
if err := trace.Start(&output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
func() {
|
||||
defer trace.Stop()
|
||||
query := "SELECT ?"
|
||||
if fail {
|
||||
query = "SELECT missing_synthetic_function(?)"
|
||||
}
|
||||
_, err = engine.Context(ctx).Exec(query, "synthetic-following-hook-sentinel")
|
||||
}()
|
||||
_ = engine.Close()
|
||||
if (err != nil) != fail {
|
||||
t.Fatal("unexpected real SQLite query outcome")
|
||||
}
|
||||
if bytes.Contains(output.Bytes(), []byte("synthetic-following-hook-sentinel")) == sensitive {
|
||||
t.Fatal("following hook changed private or ordinary trace disclosure")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/modules/auth/pam"
|
||||
"forgejo.org/modules/base"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/web"
|
||||
auth_service "forgejo.org/services/auth"
|
||||
"forgejo.org/services/auth/source/ldap"
|
||||
"forgejo.org/services/auth/source/oauth2"
|
||||
pam_service "forgejo.org/services/auth/source/pam"
|
||||
"forgejo.org/services/auth/source/smtp"
|
||||
"forgejo.org/services/context"
|
||||
"forgejo.org/services/forms"
|
||||
|
||||
"xorm.io/xorm/convert"
|
||||
)
|
||||
|
||||
const (
|
||||
tplAuths base.TplName = "admin/auth/list"
|
||||
tplAuthNew base.TplName = "admin/auth/new"
|
||||
tplAuthEdit base.TplName = "admin/auth/edit"
|
||||
)
|
||||
|
||||
// Authentications show authentication config page
|
||||
func Authentications(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.authentication")
|
||||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
var err error
|
||||
ctx.Data["Sources"], ctx.Data["Total"], err = db.FindAndCount[auth.Source](ctx, auth.FindSourcesOptions{})
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.Sources", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, tplAuths)
|
||||
}
|
||||
|
||||
type dropdownItem struct {
|
||||
Name string
|
||||
Type any
|
||||
}
|
||||
|
||||
var (
|
||||
authSources = func() []dropdownItem {
|
||||
items := []dropdownItem{
|
||||
{auth.LDAP.String(), auth.LDAP},
|
||||
{auth.DLDAP.String(), auth.DLDAP},
|
||||
{auth.SMTP.String(), auth.SMTP},
|
||||
{auth.OAuth2.String(), auth.OAuth2},
|
||||
}
|
||||
if pam.Supported {
|
||||
items = append(items, dropdownItem{auth.Names[auth.PAM], auth.PAM})
|
||||
}
|
||||
return items
|
||||
}()
|
||||
|
||||
securityProtocols = []dropdownItem{
|
||||
{ldap.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted], ldap.SecurityProtocolUnencrypted},
|
||||
{ldap.SecurityProtocolNames[ldap.SecurityProtocolLDAPS], ldap.SecurityProtocolLDAPS},
|
||||
{ldap.SecurityProtocolNames[ldap.SecurityProtocolStartTLS], ldap.SecurityProtocolStartTLS},
|
||||
}
|
||||
)
|
||||
|
||||
// NewAuthSource render adding a new auth source page
|
||||
func NewAuthSource(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.auths.new")
|
||||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
ctx.Data["type"] = auth.LDAP.Int()
|
||||
ctx.Data["CurrentTypeName"] = auth.Names[auth.LDAP]
|
||||
ctx.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted]
|
||||
ctx.Data["smtp_auth"] = "PLAIN"
|
||||
ctx.Data["is_active"] = true
|
||||
ctx.Data["is_sync_enabled"] = true
|
||||
ctx.Data["AuthSources"] = authSources
|
||||
ctx.Data["SecurityProtocols"] = securityProtocols
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
// only the first as default
|
||||
ctx.Data["oauth2_provider"] = oauth2providers[0].Name()
|
||||
|
||||
ctx.HTML(http.StatusOK, tplAuthNew)
|
||||
}
|
||||
|
||||
func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
|
||||
var pageSize uint32
|
||||
if form.UsePagedSearch {
|
||||
pageSize = uint32(form.SearchPageSize)
|
||||
}
|
||||
return &ldap.Source{
|
||||
Name: form.Name,
|
||||
Host: form.Host,
|
||||
Port: form.Port,
|
||||
SecurityProtocol: ldap.SecurityProtocol(form.SecurityProtocol),
|
||||
SkipVerify: form.SkipVerify,
|
||||
BindDN: form.BindDN,
|
||||
UserDN: form.UserDN,
|
||||
BindPassword: form.BindPassword,
|
||||
UserBase: form.UserBase,
|
||||
DefaultDomainName: form.DefaultDomainName,
|
||||
AttributeUsername: form.AttributeUsername,
|
||||
AttributeName: form.AttributeName,
|
||||
AttributeSurname: form.AttributeSurname,
|
||||
AttributeMail: form.AttributeMail,
|
||||
AttributesInBind: form.AttributesInBind,
|
||||
AttributeSSHPublicKey: form.AttributeSSHPublicKey,
|
||||
AttributeAvatar: form.AttributeAvatar,
|
||||
SearchPageSize: pageSize,
|
||||
Filter: form.Filter,
|
||||
GroupsEnabled: form.GroupsEnabled,
|
||||
GroupDN: form.GroupDN,
|
||||
GroupFilter: form.GroupFilter,
|
||||
GroupMemberUID: form.GroupMemberUID,
|
||||
GroupTeamMap: form.GroupTeamMap,
|
||||
GroupTeamMapRemoval: form.GroupTeamMapRemoval,
|
||||
UserUID: form.UserUID,
|
||||
AdminFilter: form.AdminFilter,
|
||||
RestrictedFilter: form.RestrictedFilter,
|
||||
AllowDeactivateAll: form.AllowDeactivateAll,
|
||||
Enabled: true,
|
||||
SkipLocalTwoFA: form.SkipLocalTwoFA,
|
||||
}
|
||||
}
|
||||
|
||||
func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source {
|
||||
return &smtp.Source{
|
||||
Auth: form.SMTPAuth,
|
||||
Host: form.SMTPHost,
|
||||
Port: form.SMTPPort,
|
||||
AllowedDomains: form.AllowedDomains,
|
||||
ForceSMTPS: form.ForceSMTPS,
|
||||
SkipVerify: form.SkipVerify,
|
||||
HeloHostname: form.HeloHostname,
|
||||
DisableHelo: form.DisableHelo,
|
||||
SkipLocalTwoFA: form.SkipLocalTwoFA,
|
||||
}
|
||||
}
|
||||
|
||||
func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
|
||||
var customURLMapping *oauth2.CustomURLMapping
|
||||
if form.Oauth2UseCustomURL {
|
||||
customURLMapping = &oauth2.CustomURLMapping{
|
||||
TokenURL: form.Oauth2TokenURL,
|
||||
AuthURL: form.Oauth2AuthURL,
|
||||
ProfileURL: form.Oauth2ProfileURL,
|
||||
EmailURL: form.Oauth2EmailURL,
|
||||
Tenant: form.Oauth2Tenant,
|
||||
}
|
||||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
var scopes []string
|
||||
for _, s := range strings.Split(form.Oauth2Scopes, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if s != "" {
|
||||
scopes = append(scopes, s)
|
||||
}
|
||||
}
|
||||
|
||||
return &oauth2.Source{
|
||||
Provider: form.Oauth2Provider,
|
||||
ClientID: form.Oauth2Key,
|
||||
ClientSecret: form.Oauth2Secret,
|
||||
OpenIDConnectAutoDiscoveryURL: form.OpenIDConnectAutoDiscoveryURL,
|
||||
CustomURLMapping: customURLMapping,
|
||||
IconURL: form.Oauth2IconURL,
|
||||
Scopes: scopes,
|
||||
AttributeSSHPublicKey: form.Oauth2AttributeSSHPublicKey,
|
||||
RequiredClaimName: form.Oauth2RequiredClaimName,
|
||||
RequiredClaimValue: form.Oauth2RequiredClaimValue,
|
||||
SkipLocalTwoFA: form.SkipLocalTwoFA,
|
||||
GroupClaimName: form.Oauth2GroupClaimName,
|
||||
RestrictedGroup: form.Oauth2RestrictedGroup,
|
||||
AdminGroup: form.Oauth2AdminGroup,
|
||||
GroupTeamMap: form.Oauth2GroupTeamMap,
|
||||
GroupTeamMapRemoval: form.Oauth2GroupTeamMapRemoval,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAuthSourcePost response for adding an auth source
|
||||
func NewAuthSourcePost(ctx *context.Context) {
|
||||
form := *web.GetForm(ctx).(*forms.AuthenticationForm)
|
||||
ctx.Data["Title"] = ctx.Tr("admin.auths.new")
|
||||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
ctx.Data["CurrentTypeName"] = auth.Type(form.Type).String()
|
||||
ctx.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolNames[ldap.SecurityProtocol(form.SecurityProtocol)]
|
||||
ctx.Data["AuthSources"] = authSources
|
||||
ctx.Data["SecurityProtocols"] = securityProtocols
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
hasTLS := false
|
||||
var config convert.Conversion
|
||||
switch auth.Type(form.Type) {
|
||||
case auth.LDAP, auth.DLDAP:
|
||||
config = parseLDAPConfig(form)
|
||||
hasTLS = ldap.SecurityProtocol(form.SecurityProtocol) > ldap.SecurityProtocolUnencrypted
|
||||
case auth.SMTP:
|
||||
config = parseSMTPConfig(form)
|
||||
hasTLS = true
|
||||
case auth.PAM:
|
||||
config = &pam_service.Source{
|
||||
ServiceName: form.PAMServiceName,
|
||||
EmailDomain: form.PAMEmailDomain,
|
||||
SkipLocalTwoFA: form.SkipLocalTwoFA,
|
||||
}
|
||||
case auth.OAuth2:
|
||||
config = parseOAuth2Config(form)
|
||||
oauth2Config := config.(*oauth2.Source)
|
||||
if oauth2Config.Provider == "openidConnect" {
|
||||
discoveryURL, err := url.Parse(oauth2Config.OpenIDConnectAutoDiscoveryURL)
|
||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthNew, form)
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
ctx.Error(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
ctx.Data["HasTLS"] = hasTLS
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplAuthNew)
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.CreateSource(ctx, &auth.Source{
|
||||
Type: auth.Type(form.Type),
|
||||
Name: form.Name,
|
||||
IsActive: form.IsActive,
|
||||
IsSyncEnabled: form.IsSyncEnabled,
|
||||
Cfg: config,
|
||||
}); err != nil {
|
||||
if auth.IsErrSourceAlreadyExist(err) {
|
||||
ctx.Data["Err_Name"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(auth.ErrSourceAlreadyExist).Name), tplAuthNew, form)
|
||||
} else if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
unwrapped := err.(oauth2.ErrOpenIDConnectInitialize).Unwrap()
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.unable_to_initialize_openid", unwrapped), tplAuthNew, form)
|
||||
} else {
|
||||
ctx.ServerError("auth.CreateSource", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("Authentication created by admin(%s): %s", ctx.Doer.Name, form.Name)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.auths.new_success", form.Name))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/auths")
|
||||
}
|
||||
|
||||
// EditAuthSource render editing auth source page
|
||||
func EditAuthSource(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.auths.edit")
|
||||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
ctx.Data["SecurityProtocols"] = securityProtocols
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid"))
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.GetSourceByID", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["Source"] = source
|
||||
ctx.Data["HasTLS"] = source.HasTLS()
|
||||
|
||||
if source.IsOAuth2() {
|
||||
type Named interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
for _, provider := range oauth2providers {
|
||||
if provider.Name() == source.Cfg.(Named).Name() {
|
||||
ctx.Data["CurrentOAuth2Provider"] = provider
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, tplAuthEdit)
|
||||
}
|
||||
|
||||
// EditAuthSourcePost response for editing auth source
|
||||
func EditAuthSourcePost(ctx *context.Context) {
|
||||
form := *web.GetForm(ctx).(*forms.AuthenticationForm)
|
||||
ctx.Data["Title"] = ctx.Tr("admin.auths.edit")
|
||||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
ctx.Data["SMTPAuths"] = smtp.Authenticators
|
||||
oauth2providers := oauth2.GetSupportedOAuth2Providers()
|
||||
ctx.Data["OAuth2Providers"] = oauth2providers
|
||||
|
||||
source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid"))
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.GetSourceByID", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["Source"] = source
|
||||
ctx.Data["HasTLS"] = source.HasTLS()
|
||||
|
||||
// The legacy form has no representation for the opt-in current-UserInfo gate.
|
||||
// Refuse rather than silently dropping it during source reconstruction.
|
||||
if current, ok := source.Cfg.(*oauth2.Source); ok && current.RequiredClaimValues != nil {
|
||||
ctx.RenderWithErr("This OAuth source uses required-claim-values; update it through the reviewed CLI.", tplAuthEdit, form)
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplAuthEdit)
|
||||
return
|
||||
}
|
||||
|
||||
var config convert.Conversion
|
||||
switch auth.Type(form.Type) {
|
||||
case auth.LDAP, auth.DLDAP:
|
||||
config = parseLDAPConfig(form)
|
||||
case auth.SMTP:
|
||||
config = parseSMTPConfig(form)
|
||||
case auth.PAM:
|
||||
config = &pam_service.Source{
|
||||
ServiceName: form.PAMServiceName,
|
||||
EmailDomain: form.PAMEmailDomain,
|
||||
}
|
||||
case auth.OAuth2:
|
||||
config = parseOAuth2Config(form)
|
||||
oauth2Config := config.(*oauth2.Source)
|
||||
if oauth2Config.Provider == "openidConnect" {
|
||||
discoveryURL, err := url.Parse(oauth2Config.OpenIDConnectAutoDiscoveryURL)
|
||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthEdit, form)
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
ctx.Error(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
source.Name = form.Name
|
||||
source.IsActive = form.IsActive
|
||||
source.IsSyncEnabled = form.IsSyncEnabled
|
||||
source.Cfg = config
|
||||
if err := auth.UpdateSource(ctx, source); err != nil {
|
||||
if auth.IsErrSourceAlreadyExist(err) {
|
||||
ctx.Data["Err_Name"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(auth.ErrSourceAlreadyExist).Name), tplAuthEdit, form)
|
||||
} else if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||
ctx.Flash.Error(err.Error(), true)
|
||||
ctx.Data["Err_DiscoveryURL"] = true
|
||||
ctx.HTML(http.StatusOK, tplAuthEdit)
|
||||
} else {
|
||||
ctx.ServerError("UpdateSource", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Trace("Authentication changed by admin(%s): %d", ctx.Doer.Name, source.ID)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.auths.update_success"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/auths/" + strconv.FormatInt(form.ID, 10))
|
||||
}
|
||||
|
||||
// DeleteAuthSource response for deleting an auth source
|
||||
func DeleteAuthSource(ctx *context.Context) {
|
||||
source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid"))
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.GetSourceByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = auth_service.DeleteSource(ctx, source); err != nil {
|
||||
if auth.IsErrSourceInUse(err) {
|
||||
ctx.Flash.Error(ctx.Tr("admin.auths.still_in_used"))
|
||||
} else {
|
||||
ctx.Flash.Error(fmt.Sprintf("auth_service.DeleteSource: %v", err))
|
||||
}
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.Params(":authid")))
|
||||
return
|
||||
}
|
||||
log.Trace("Authentication deleted by admin(%s): %d", ctx.Doer.Name, source.ID)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.auths.deletion_success"))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/auths")
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,247 @@
|
|||
// 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"
|
||||
"keycape/pkg/tokenstatus"
|
||||
)
|
||||
|
||||
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"`
|
||||
ApplicationTenant string `json:"application_tenant"`
|
||||
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"`
|
||||
TenantSourceClaim string `json:"tenant_source_claim"`
|
||||
MaxAuthenticationAge string `json:"max_authentication_age"`
|
||||
MaxIdentityAge string `json:"max_identity_age"`
|
||||
Decision decisionpep.Admission `json:"decision"`
|
||||
CurrentToken tokenstatus.OnlineAdmission `json:"current_token"`
|
||||
}
|
||||
|
||||
// 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
|
||||
TenantSource string
|
||||
AssuranceData map[string]any
|
||||
AuthenticatedAt time.Time
|
||||
IssuedAt time.Time
|
||||
SigningAlgorithm string
|
||||
AccessTokenHash 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)
|
||||
authAge, authErr := time.ParseDuration(a.MaxAuthenticationAge)
|
||||
if e != nil || authErr != nil || authAge <= 0 || age <= 0 || a.TenantSourceClaim == "" || 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
|
||||
}
|
||||
// The admitted KeyCape ID-token profile never carries access-grant fields.
|
||||
// Both producer tokens use typ JWT, so the header alone cannot separate them.
|
||||
for _, field := range []string{"jti", "scope"} {
|
||||
if _, exists := claims[field]; exists {
|
||||
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 || a.ApplicationTenant == "" || tenant != a.ApplicationTenant {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
principal, ok := value(a.PrincipalTypeClaim)
|
||||
if !ok {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
tenantSource, ok := value(a.TenantSourceClaim)
|
||||
if !ok || (tenantSource != "directory" && tenantSource != "registration" && tenantSource != "default") {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
assurance, assuranceData, authenticatedAt, authenticationEnd, err := verifiedKeyCapeAssurance(claims[a.AssuranceClaim], issued.Time, now, authAge)
|
||||
if err != nil {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
validUntil := end.Time
|
||||
for _, bound := range []time.Time{issued.Time.Add(age), authenticationEnd} {
|
||||
if bound.Before(validUntil) {
|
||||
validUntil = bound
|
||||
}
|
||||
}
|
||||
if !now.Before(validUntil) {
|
||||
return out, errApplicationDecision
|
||||
}
|
||||
accessHash, _ := claims["at_hash"].(string)
|
||||
return VerifiedApplicationIdentity{Issuer: a.Issuer, Subject: subject, Tenant: tenant, PrincipalType: api.SubjectType(principal), Assurance: assurance, TenantSource: tenantSource, AssuranceData: assuranceData, AuthenticatedAt: authenticatedAt, IssuedAt: issued.Time, SigningAlgorithm: token.Method.Alg(), AccessTokenHash: accessHash, NotAfter: validUntil}, 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
|
||||
}
|
||||
current, e := verifyCurrentApplicationToken(ctx, user.AccessToken, a, identity)
|
||||
if e != nil {
|
||||
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_assurance_claim": identity.AssuranceData, "identity_authenticated_at": identity.AuthenticatedAt.UTC().Format(time.RFC3339Nano), "identity_tenant_source": identity.TenantSource, "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
|
||||
}
|
||||
// A slow decision must not turn a revoked authentication into a sign-in.
|
||||
current, e = verifyCurrentApplicationToken(ctx, user.AccessToken, a, identity)
|
||||
if e != nil {
|
||||
return errApplicationDecision
|
||||
}
|
||||
if !time.Now().UTC().Before(identity.NotAfter) || !time.Now().UTC().Before(current.NotAfter) {
|
||||
return errApplicationDecision
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Validate the actual IAM v0.3 object only after issuer JOSE verification. These
|
||||
// are identity facts, not an access verdict: aal1/service facts still reach the
|
||||
// PDP, which owns the application's assurance and principal policy. Preserve the
|
||||
// full object and its integer timestamp in the signed decision request.
|
||||
func verifiedKeyCapeAssurance(raw any, issued, now time.Time, maxAge time.Duration) (string, map[string]any, time.Time, time.Time, error) {
|
||||
refuse := func() (string, map[string]any, time.Time, time.Time, error) {
|
||||
return "", nil, time.Time{}, time.Time{}, errApplicationDecision
|
||||
}
|
||||
claim, ok := raw.(map[string]any)
|
||||
if !ok || len(claim) != 5 {
|
||||
return refuse()
|
||||
}
|
||||
level, ok := claim["level"].(string)
|
||||
if !ok || (level != "aal1" && level != "aal2" && level != "service") {
|
||||
return refuse()
|
||||
}
|
||||
mfa, ok := claim["mfa"].(bool)
|
||||
if !ok || mfa != (level == "aal2") || claim["source"] != "key-cape" {
|
||||
return refuse()
|
||||
}
|
||||
methods, ok := claim["methods"].([]any)
|
||||
if !ok || len(methods) == 0 {
|
||||
return refuse()
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, method := range methods {
|
||||
value, ok := method.(string)
|
||||
if !ok || !validClaimLiteral(value) || seen[value] {
|
||||
return refuse()
|
||||
}
|
||||
seen[value] = true
|
||||
}
|
||||
number, ok := claim["at"].(json.Number)
|
||||
if !ok {
|
||||
return refuse()
|
||||
}
|
||||
seconds, err := number.Int64()
|
||||
if err != nil || seconds <= 0 || seconds > issued.Unix() || seconds > now.Unix() {
|
||||
return refuse()
|
||||
}
|
||||
authenticated := time.Unix(seconds, 0).UTC()
|
||||
end := authenticated.Add(maxAge)
|
||||
if maxAge <= 0 || !now.Before(end) {
|
||||
return refuse()
|
||||
}
|
||||
return level, claim, authenticated, end, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAccessEngineKeyCapeAssuranceRefusals(t *testing.T) {
|
||||
cases := map[string]func(map[string]any, time.Time){
|
||||
"stale-authentication": func(c map[string]any, now time.Time) { c["at"] = now.Add(-10 * time.Minute).Unix() },
|
||||
"future-authentication": func(c map[string]any, now time.Time) { c["at"] = now.Add(time.Minute).Unix() },
|
||||
"missing-at": func(c map[string]any, _ time.Time) { delete(c, "at") },
|
||||
"string-at": func(c map[string]any, _ time.Time) { c["at"] = "recent" },
|
||||
"fractional-at": func(c map[string]any, now time.Time) { c["at"] = float64(now.Unix()) - 1.5 },
|
||||
"integer-mfa": func(c map[string]any, _ time.Time) { c["mfa"] = 1 },
|
||||
"inconsistent-mfa": func(c map[string]any, _ time.Time) { c["mfa"] = false },
|
||||
"wrong-source": func(c map[string]any, _ time.Time) { c["source"] = "userinfo" },
|
||||
"wrong-level": func(c map[string]any, _ time.Time) { c["level"] = "verified" },
|
||||
"object-methods": func(c map[string]any, _ time.Time) { c["methods"] = map[string]any{"pwd": true} },
|
||||
"empty-methods": func(c map[string]any, _ time.Time) { c["methods"] = []string{} },
|
||||
"mixed-methods": func(c map[string]any, _ time.Time) { c["methods"] = []any{"pwd", 1} },
|
||||
"duplicate-methods": func(c map[string]any, _ time.Time) { c["methods"] = []string{"pwd", "pwd"} },
|
||||
"unknown-object-field": func(c map[string]any, _ time.Time) { c["verified"] = true },
|
||||
}
|
||||
for name, mutate := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
admission, key, claims, now := identityFixture(t)
|
||||
mutate(claims["assurance"].(map[string]any), now)
|
||||
if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err == nil {
|
||||
t.Fatal("malformed or stale issuer assurance accepted")
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, source := range []string{"", "assumed"} {
|
||||
admission, key, claims, now := identityFixture(t)
|
||||
claims["tenant_source"] = source
|
||||
if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now); err == nil {
|
||||
t.Fatal("missing or unknown tenant provenance accepted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessEngineKeyCapeIdentityFactsAndFreshnessBounds(t *testing.T) {
|
||||
admission, key, claims, now := identityFixture(t)
|
||||
admission.MaxAuthenticationAge = "5s"
|
||||
claims["assurance"].(map[string]any)["at"] = now.Add(-3 * time.Second).Unix()
|
||||
identity, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !identity.NotAfter.Equal(now.Add(2*time.Second)) || !identity.AuthenticatedAt.Equal(now.Add(-3*time.Second)) || identity.TenantSource != "directory" {
|
||||
t.Fatal("snapshot overstated authentication freshness or tenant provenance")
|
||||
}
|
||||
if _, ok := identity.AssuranceData["at"].(json.Number); !ok || identity.AssuranceData["mfa"] != true || identity.AssuranceData["source"] != "key-cape" {
|
||||
t.Fatal("source assurance object lost its actual wire types")
|
||||
}
|
||||
admission.MaxIdentityAge = "2s"
|
||||
identity, err = VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now)
|
||||
if err != nil || !identity.NotAfter.Equal(now.Add(time.Second)) {
|
||||
t.Fatal("snapshot overstated token-age validity", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessEngineKeyCapeLowerAssuranceIsAnIdentityFact(t *testing.T) {
|
||||
admission, key, claims, now := identityFixture(t)
|
||||
claim := claims["assurance"].(map[string]any)
|
||||
claim["level"] = "aal1"
|
||||
claim["mfa"] = false
|
||||
claim["methods"] = []string{"pwd"}
|
||||
identity, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), admission, now)
|
||||
if err != nil || identity.Assurance != "aal1" || identity.AssuranceData["mfa"] != false {
|
||||
t.Fatal("identity parser substituted an application authorization policy", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,207 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"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) {
|
||||
return callbackFixtureWithIdentity(t, group, mode, nil, "")
|
||||
}
|
||||
|
||||
func callbackFixtureWithIdentity(t *testing.T, group, mode string, suppliedAdmission *AccessEngineAdmission, suppliedToken string, suppliedAccess ...string) (*Source, *goth.User, *atomic.Int64) {
|
||||
t.Helper()
|
||||
admission, identityKey, claims, _ := identityFixture(t)
|
||||
var accessToken string
|
||||
var currentState *callbackCurrentState
|
||||
if suppliedAdmission == nil {
|
||||
currentState = newCallbackCurrentFixture(t, &admission, claims)
|
||||
accessToken = currentState.token
|
||||
} else if len(suppliedAccess) == 1 {
|
||||
accessToken = suppliedAccess[0]
|
||||
}
|
||||
identityToken := signedIdentity(t, identityKey, claims, nil)
|
||||
if currentState != nil {
|
||||
identityToken = currentState.signIdentity(t, claims)
|
||||
}
|
||||
if suppliedAdmission != nil {
|
||||
admission, identityToken = *suppliedAdmission, suppliedToken
|
||||
}
|
||||
verified, err := VerifyApplicationIdentity(identityToken, admission, time.Now().UTC())
|
||||
if err != nil {
|
||||
t.Fatal("callback fixture identity invalid")
|
||||
}
|
||||
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 != verified.Subject || request.Subject.Tenant != verified.Tenant || request.Subject.Type != verified.PrincipalType || request.Action != "application.access" || request.Resource.ID != "application:forgejo" || c["identity_issuer"] != admission.Issuer || c["identity_assurance"] != verified.Assurance || c["identity_tenant"] != verified.Tenant || c["source_id"] != json.Number("17") || !reflect.DeepEqual(c["identity_assurance_claim"], verified.AssuranceData) || c["identity_authenticated_at"] != verified.AuthenticatedAt.UTC().Format(time.RFC3339Nano) || c["identity_tenant_source"] != verified.TenantSource {
|
||||
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 "revoke-current":
|
||||
currentState.mode.Store("inactive")
|
||||
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: verified.Subject, IDToken: identityToken, AccessToken: accessToken, RawData: map[string]any{"sub": verified.Subject, "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["assurance"] = map[string]any{"level": "aal1", "source": "attacker"}
|
||||
user.RawData["tenant_source"] = "attacker"
|
||||
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,46 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"keycape/pkg/tokenstatus"
|
||||
)
|
||||
|
||||
// This consumer joins its independently verified ID token to the producer's
|
||||
// current access-grant observation. It supplies no token registry or evaluator.
|
||||
// Native credential/session continuation retains its separate owner profile.
|
||||
func verifyCurrentApplicationToken(ctx context.Context, raw string, a AccessEngineAdmission, identity VerifiedApplicationIdentity) (tokenstatus.CurrentToken, error) {
|
||||
var refused tokenstatus.CurrentToken
|
||||
digest := sha256.Sum256([]byte(raw))
|
||||
if identity.SigningAlgorithm != "RS256" || identity.AccessTokenHash != base64.RawURLEncoding.EncodeToString(digest[:16]) {
|
||||
return refused, errApplicationDecision
|
||||
}
|
||||
if a.CurrentToken.Issuer != identity.Issuer || a.CurrentToken.ClientID != a.Audience {
|
||||
return refused, errApplicationDecision
|
||||
}
|
||||
client, err := tokenstatus.NewOnlineClient(a.CurrentToken)
|
||||
if err != nil {
|
||||
return refused, errApplicationDecision
|
||||
}
|
||||
current, err := client.Check(ctx, raw, tokenstatus.ExpectedIdentity{
|
||||
Subject: identity.Subject, Tenant: identity.Tenant, PrincipalType: string(identity.PrincipalType),
|
||||
})
|
||||
if err != nil || !current.IssuedAt.Equal(identity.IssuedAt) {
|
||||
return refused, errApplicationDecision
|
||||
}
|
||||
facts := current.Claims()
|
||||
actual, err := json.Marshal(facts[a.AssuranceClaim])
|
||||
expected, expectedErr := json.Marshal(identity.AssuranceData)
|
||||
if err != nil || expectedErr != nil || !bytes.Equal(actual, expected) || facts[a.TenantSourceClaim] != identity.TenantSource {
|
||||
return refused, errApplicationDecision
|
||||
}
|
||||
if !time.Now().UTC().Before(identity.NotAfter) {
|
||||
return refused, errApplicationDecision
|
||||
}
|
||||
return current, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"keycape/pkg/tokenstatus"
|
||||
)
|
||||
|
||||
// This unit status responder is synthetic. Separate actual-issuer integration
|
||||
// tests exercise the producer's real PKCE, introspection and revocation handlers.
|
||||
type callbackCurrentState struct {
|
||||
key *rsa.PrivateKey
|
||||
claims jwt.MapClaims
|
||||
token string
|
||||
mode atomic.Value
|
||||
calls atomic.Int64
|
||||
}
|
||||
|
||||
func (s *callbackCurrentState) sign(t *testing.T) string {
|
||||
t.Helper()
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, s.claims)
|
||||
token.Header["kid"] = "access-fixture"
|
||||
raw, err := token.SignedString(s.key)
|
||||
if err != nil {
|
||||
t.Fatal("synthetic access signing failed")
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func (s *callbackCurrentState) signIdentity(t *testing.T, claims jwt.MapClaims) string {
|
||||
t.Helper()
|
||||
id := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
id.Header["kid"] = "access-fixture"
|
||||
raw, err := id.SignedString(s.key)
|
||||
if err != nil {
|
||||
t.Fatal("synthetic ID signing failed")
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func newCallbackCurrentFixture(t *testing.T, a *AccessEngineAdmission, idClaims jwt.MapClaims) *callbackCurrentState {
|
||||
t.Helper()
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatal("synthetic access key failed")
|
||||
}
|
||||
f := &callbackCurrentState{key: key}
|
||||
f.mode.Store("")
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
f.calls.Add(1)
|
||||
client, secret, ok := r.BasicAuth()
|
||||
if !ok || client != a.Audience || secret != "synthetic-current" || r.URL.Path != "/introspect" || r.Method != http.MethodPost || r.ParseForm() != nil || r.Form.Get("token") != f.token {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if f.mode.Load().(string) == "unavailable" {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
if f.mode.Load().(string) == "inactive" {
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"active": false})
|
||||
return
|
||||
}
|
||||
status := map[string]any{"active": true, "client_id": a.Audience, "token_type": "Bearer", "iss": a.Issuer, "sub": f.claims["sub"], "aud": a.CurrentToken.Audience, "jti": f.claims["jti"], "scope": f.claims["scope"], "iat": f.claims["iat"], "exp": f.claims["exp"]}
|
||||
if f.mode.Load().(string) == "wrong-status" {
|
||||
status["jti"] = "other"
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(status)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
a.Issuer = server.URL
|
||||
idClaims["iss"] = a.Issuer
|
||||
raw, _ := json.Marshal(idClaims)
|
||||
if json.Unmarshal(raw, &f.claims) != nil {
|
||||
t.Fatal("synthetic claims failed")
|
||||
}
|
||||
f.claims["jti"] = base64.RawURLEncoding.EncodeToString(make([]byte, 32))
|
||||
f.claims["scope"] = "openid profile groups"
|
||||
f.token = f.sign(t)
|
||||
digest := sha256.Sum256([]byte(f.token))
|
||||
idClaims["at_hash"] = base64.RawURLEncoding.EncodeToString(digest[:16])
|
||||
der, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
|
||||
if err != nil {
|
||||
t.Fatal("synthetic identity public key failed")
|
||||
}
|
||||
a.Algorithms = []string{"RS256"}
|
||||
a.IdentityKeys = map[string]string{"access-fixture": string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))}
|
||||
dir := t.TempDir()
|
||||
ca, keys, secret := filepath.Join(dir, "current-ca.pem"), filepath.Join(dir, "current-jwks.json"), filepath.Join(dir, "current-client")
|
||||
jwks := map[string]any{"keys": []any{map[string]any{"kty": "RSA", "use": "sig", "alg": "RS256", "kid": "access-fixture", "n": base64.RawURLEncoding.EncodeToString(key.N.Bytes()), "e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(key.E)).Bytes())}}}
|
||||
public, _ := json.Marshal(jwks)
|
||||
if os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0644) != nil || os.WriteFile(keys, public, 0644) != nil || os.WriteFile(secret, []byte("synthetic-current"), 0600) != nil {
|
||||
t.Fatal("synthetic admission failed")
|
||||
}
|
||||
a.CurrentToken = tokenstatus.OnlineAdmission{Issuer: a.Issuer, Audience: a.Audience, ClientID: a.Audience, CAFile: ca, JWKSFile: keys, SecretFile: secret, MaxTokenAge: "5m"}
|
||||
return f
|
||||
}
|
||||
|
||||
func TestAccessEngineCurrentTokenRejectsBeforeDecision(t *testing.T) {
|
||||
for _, mode := range []string{"missing", "id-as-access", "subject", "tenant", "principal", "assurance", "authentication-time", "tenant-source", "issued-at", "inactive", "unavailable", "wrong-status", "missing-current-admission", "wrong-client", "withdrawn-key", "mismatched-grant", "missing-at-hash"} {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
a, _, claims, _ := identityFixture(t)
|
||||
state := newCallbackCurrentFixture(t, &a, claims)
|
||||
switch mode {
|
||||
case "mismatched-grant":
|
||||
state.claims["jti"] = base64.RawURLEncoding.EncodeToString([]byte("12345678901234567890123456789012"))
|
||||
case "subject":
|
||||
state.claims["sub"] = "other"
|
||||
case "tenant":
|
||||
state.claims["tenant"] = "other"
|
||||
case "principal":
|
||||
state.claims["principal_type"] = "service"
|
||||
case "assurance":
|
||||
state.claims["assurance"].(map[string]any)["level"] = "aal1"
|
||||
case "authentication-time":
|
||||
state.claims["assurance"].(map[string]any)["at"] = state.claims["iat"].(float64) - 1
|
||||
case "tenant-source":
|
||||
state.claims["tenant_source"] = "default"
|
||||
case "issued-at":
|
||||
state.claims["iat"] = state.claims["iat"].(float64) - 1
|
||||
case "inactive", "unavailable", "wrong-status":
|
||||
state.mode.Store(mode)
|
||||
case "missing-current-admission":
|
||||
a.CurrentToken = tokenstatus.OnlineAdmission{}
|
||||
case "wrong-client":
|
||||
a.CurrentToken.ClientID = "other"
|
||||
case "withdrawn-key":
|
||||
if os.WriteFile(a.CurrentToken.JWKSFile, []byte(`{"keys":[]}`), 0644) != nil {
|
||||
t.Fatal("fixture withdrawal failed")
|
||||
}
|
||||
}
|
||||
state.token = state.sign(t)
|
||||
if mode != "mismatched-grant" {
|
||||
digest := sha256.Sum256([]byte(state.token))
|
||||
claims["at_hash"] = base64.RawURLEncoding.EncodeToString(digest[:16])
|
||||
}
|
||||
if mode == "missing-at-hash" {
|
||||
delete(claims, "at_hash")
|
||||
}
|
||||
id := state.signIdentity(t, claims)
|
||||
access := state.token
|
||||
if mode == "missing" {
|
||||
access = ""
|
||||
}
|
||||
if mode == "id-as-access" {
|
||||
access = id
|
||||
}
|
||||
source, _, calls := callbackFixtureWithIdentity(t, "forge-admins", "allow", &a, id, access)
|
||||
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
|
||||
if err == nil || got.UserID != "" || calls.Load() != 0 {
|
||||
t.Fatal("invalid current authentication reached the decision or callback")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessEngineCurrentTokenRevokedDuringDecisionRefusesSignIn(t *testing.T) {
|
||||
source, _, calls := callbackFixture(t, "forge-admins", "revoke-current")
|
||||
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
|
||||
if err == nil || got.UserID != "" || calls.Load() != 1 {
|
||||
t.Fatal("revocation during decision escaped the final current check")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package oauth2
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestVerifiedApplicationIdentityRefusesAccessTokenGrantClaims(t *testing.T) {
|
||||
for _, field := range []string{"jti", "scope"} {
|
||||
t.Run(field, func(t *testing.T) {
|
||||
a, key, claims, now := identityFixture(t)
|
||||
claims[field] = "synthetic-access-grant"
|
||||
if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), a, now); err == nil {
|
||||
t.Fatal("access-token grant fields were accepted as an ID token")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedApplicationIdentityCannotChooseApplicationTenant(t *testing.T) {
|
||||
a, key, claims, now := identityFixture(t)
|
||||
claims["tenant"] = "tenant:another-valid-issuer-tenant"
|
||||
if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), a, now); err == nil {
|
||||
t.Fatal("a valid signature let the caller select the application's tenant")
|
||||
}
|
||||
a.ApplicationTenant = ""
|
||||
claims["tenant"] = "tenant:synthetic"
|
||||
if _, err := VerifyApplicationIdentity(signedIdentity(t, key, claims, nil), a, now); err == nil {
|
||||
t.Fatal("missing owner application tenant became a permissive default")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
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: "assurance", TenantSourceClaim: "tenant_source", MaxAuthenticationAge: "5m", 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", "tenant_source": "directory", "assurance": map[string]any{"level": "aal2", "mfa": true, "source": "key-cape", "methods": []string{"pwd", "otp"}, "at": now.Add(-time.Second).Unix()}}
|
||||
a.ApplicationTenant = "tenant:synthetic"
|
||||
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, "assurance") }, "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,68 @@
|
|||
//go:build current_issuer_fixture
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/gothic"
|
||||
"github.com/markbates/goth/providers/openidConnect"
|
||||
"keycape/pkg/tokenstatus"
|
||||
)
|
||||
|
||||
// Explicit isolated integration input only; never run against a deployed issuer.
|
||||
func TestAccessEngineLiveFixtureCurrentIssuer(t *testing.T) {
|
||||
var fixture struct {
|
||||
IDToken string `json:"id_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
Key string `json:"public_key_pem"`
|
||||
Tenant string `json:"tenant"`
|
||||
Phase string `json:"phase"`
|
||||
Current tokenstatus.OnlineAdmission `json:"current"`
|
||||
}
|
||||
raw, err := os.ReadFile(os.Getenv("FORGEJO_CURRENT_ISSUER_FIXTURE"))
|
||||
if err != nil || json.Unmarshal(raw, &fixture) != nil {
|
||||
t.Fatal("private isolated issuer fixture missing")
|
||||
}
|
||||
a, _, _, _ := identityFixture(t)
|
||||
a.Issuer, a.Audience, a.ApplicationTenant = fixture.Current.Issuer, fixture.Current.ClientID, fixture.Tenant
|
||||
a.Algorithms, a.IdentityKeys = []string{"RS256"}, map[string]string{"key-1": fixture.Key}
|
||||
a.CurrentToken = fixture.Current
|
||||
source, _, calls := callbackFixtureWithIdentity(t, "forge-admins", "allow", &a, fixture.IDToken, fixture.AccessToken)
|
||||
if fixture.Phase == "active" {
|
||||
ca, err := os.ReadFile(fixture.Current.CAFile)
|
||||
roots := x509.NewCertPool()
|
||||
if err != nil || !roots.AppendCertsFromPEM(ca) {
|
||||
t.Fatal("private fixture trust missing")
|
||||
}
|
||||
transport := &http.Transport{Proxy: nil, TLSClientConfig: &tls.Config{RootCAs: roots, MinVersion: tls.VersionTLS12}}
|
||||
t.Cleanup(transport.CloseIdleConnections)
|
||||
oldClient := http.DefaultClient
|
||||
http.DefaultClient = &http.Client{Transport: transport, Timeout: 5 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return errApplicationDecision }}
|
||||
t.Cleanup(func() { http.DefaultClient = oldClient })
|
||||
source.OpenIDConnectAutoDiscoveryURL = fixture.Current.Issuer + "/.well-known/openid-configuration"
|
||||
provider, err := (&OpenIDProvider{}).CreateGothProvider("synthetic-source", "https://forgejo.invalid/callback", source)
|
||||
if err != nil {
|
||||
t.Fatal("actual provider discovery failed")
|
||||
}
|
||||
gothic.CompleteUserAuth = func(http.ResponseWriter, *http.Request) (goth.User, error) {
|
||||
return provider.FetchUser(&openidConnect.Session{IDToken: fixture.IDToken, AccessToken: fixture.AccessToken, ExpiresAt: time.Now().Add(time.Minute)})
|
||||
}
|
||||
}
|
||||
got, err := source.Callback(httptest.NewRequest(http.MethodGet, "https://forgejo.invalid/callback", nil), httptest.NewRecorder(), "")
|
||||
if fixture.Phase == "active" {
|
||||
if err != nil || got.UserID == "" || calls.Load() != 1 {
|
||||
t.Fatal("actual issuer/provider/current-token/decision callback did not pass")
|
||||
}
|
||||
} else if err == nil || got.UserID != "" || got.IDToken != "" || calls.Load() != 0 {
|
||||
t.Fatal("revoked, suspended or substituted grant escaped current callback check")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"html/template"
|
||||
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/svg"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/openidConnect"
|
||||
)
|
||||
|
||||
// OpenIDProvider is a GothProvider for OpenID
|
||||
type OpenIDProvider struct{}
|
||||
|
||||
// Name provides the technical name for this provider
|
||||
func (o *OpenIDProvider) Name() string {
|
||||
return "openidConnect"
|
||||
}
|
||||
|
||||
// DisplayName returns the friendly name for this provider
|
||||
func (o *OpenIDProvider) DisplayName() string {
|
||||
return "OpenID Connect"
|
||||
}
|
||||
|
||||
// IconHTML returns icon HTML for this provider
|
||||
func (o *OpenIDProvider) IconHTML(size int) template.HTML {
|
||||
return svg.RenderHTML("gitea-openid", size, "tw-mr-2")
|
||||
}
|
||||
|
||||
// CreateGothProvider creates a GothProvider from this Provider
|
||||
func (o *OpenIDProvider) CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) {
|
||||
scopes := setting.OAuth2Client.OpenIDConnectScopes
|
||||
if len(scopes) == 0 {
|
||||
scopes = append(scopes, source.Scopes...)
|
||||
}
|
||||
|
||||
provider, err := openidConnect.New(source.ClientID, source.ClientSecret, callbackURL, source.OpenIDConnectAutoDiscoveryURL, scopes...)
|
||||
if err != nil {
|
||||
log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, source.OpenIDConnectAutoDiscoveryURL, err)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if source.RequiredClaimValues != nil {
|
||||
if err := ValidateRequiredClaimValues(source.RequiredClaimName, source.RequiredClaimValue, source.RequiredClaimValues); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if provider.OpenIDConfig == nil || provider.SkipUserInfoRequest {
|
||||
return nil, errors.New("required-claim-values needs current UserInfo")
|
||||
}
|
||||
guarded, err := RequireCurrentUserInfoClaim(provider.Client(), provider.OpenIDConfig.UserInfoEndpoint, source.RequiredClaimName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
provider.HTTPClient = guarded
|
||||
}
|
||||
return provider, err
|
||||
}
|
||||
|
||||
// CustomURLSettings returns the custom url settings for this provider
|
||||
func (o *OpenIDProvider) CustomURLSettings() *CustomURLSettings {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *OpenIDProvider) CanProvideSSHKeys() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var _ GothProvider = &OpenIDProvider{}
|
||||
|
||||
func init() {
|
||||
RegisterGothProvider(&OpenIDProvider{})
|
||||
}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const maxClaimValues = 1024
|
||||
const maxUserInfoBytes = 1 << 20
|
||||
|
||||
// ValidateRequiredClaimValues validates only the opt-in list extension. A nil
|
||||
// list retains every legacy single-value behavior. An explicitly empty list
|
||||
// fails closed rather than turning a configured gate into claim-presence-only.
|
||||
func ValidateRequiredClaimValues(name, legacy string, values []string) error {
|
||||
if values == nil {
|
||||
return nil
|
||||
}
|
||||
if !validClaimLiteral(name) || legacy != "" || len(values) == 0 || len(values) > 64 {
|
||||
return errors.New("invalid or conflicting required-claim-values configuration")
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, value := range values {
|
||||
if !validClaimLiteral(value) || seen[value] {
|
||||
return errors.New("invalid required-claim-values literal")
|
||||
}
|
||||
seen[value] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validClaimLiteral(value string) bool {
|
||||
return value != "" && len(value) <= 256 && utf8.ValidString(value) &&
|
||||
value == strings.TrimSpace(value) && !strings.ContainsAny(value, "\x00\r\n,") &&
|
||||
!strings.ContainsFunc(value, func(r rune) bool { return unicode.IsControl(r) || r == utf8.RuneError })
|
||||
}
|
||||
|
||||
// strictClaimValues accepts only a well-formed claim array. No scalar parsing,
|
||||
// coercion, regex or prefix matching is introduced into the new gate.
|
||||
func strictClaimValues(raw any) ([]string, bool) {
|
||||
var values []string
|
||||
switch v := raw.(type) {
|
||||
case []string:
|
||||
values = v
|
||||
case []any:
|
||||
values = make([]string, len(v))
|
||||
for i, value := range v {
|
||||
s, ok := value.(string)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
values[i] = s
|
||||
}
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
if len(values) > maxClaimValues {
|
||||
return nil, false
|
||||
}
|
||||
for _, value := range values {
|
||||
if !validClaimLiteral(value) {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return values, true
|
||||
}
|
||||
|
||||
// MatchRequiredClaimValues evaluates the new consumer gate against current
|
||||
// authenticated provider data; it does not mutate or manufacture issuer claims.
|
||||
func MatchRequiredClaimValues(claims map[string]any, name, legacy string, required []string) bool {
|
||||
if required == nil || ValidateRequiredClaimValues(name, legacy, required) != nil {
|
||||
return false
|
||||
}
|
||||
raw, present := claims[name]
|
||||
if !present {
|
||||
return false
|
||||
}
|
||||
values, ok := strictClaimValues(raw)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
for _, wanted := range required {
|
||||
if value == wanted {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RequireCurrentUserInfoClaim preserves the existing HTTP client settings and
|
||||
// adds a strict response guard for this opt-in source only. A current UserInfo
|
||||
// array must overwrite any stale ID-token claim. Missing/invalid data and HTTP
|
||||
// failures cannot fall back to the ID-token map in Goth's merging code.
|
||||
func RequireCurrentUserInfoClaim(client *http.Client, endpoint, claim string) (*http.Client, error) {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil || u.Scheme != "https" || u.Host == "" || u.User != nil || u.Fragment != "" || !validClaimLiteral(claim) {
|
||||
return nil, errors.New("required-claim-values needs an HTTPS UserInfo endpoint")
|
||||
}
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
result := *client
|
||||
base := result.Transport
|
||||
if base == nil {
|
||||
base = http.DefaultTransport
|
||||
}
|
||||
result.Transport = ¤tClaimTransport{base: base, endpoint: endpoint, claim: claim}
|
||||
// Redirects could evade endpoint matching or send a bearer to another origin.
|
||||
// They are unsupported for the new guarded provider; legacy clients are intact.
|
||||
result.CheckRedirect = func(*http.Request, []*http.Request) error {
|
||||
return errors.New("guarded OAuth redirects are not supported")
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
type currentClaimTransport struct {
|
||||
base http.RoundTripper
|
||||
endpoint, claim string
|
||||
}
|
||||
|
||||
func (t *currentClaimTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
response, err := t.base.RoundTrip(req)
|
||||
if req.URL.String() != t.endpoint {
|
||||
return response, err
|
||||
}
|
||||
if err != nil {
|
||||
if response != nil && response.Body != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
return nil, errors.New("current UserInfo lookup failed")
|
||||
}
|
||||
if response == nil || response.Body == nil {
|
||||
return nil, errors.New("current UserInfo response missing")
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if req.Method != http.MethodGet || response.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("current UserInfo refused")
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(response.Body, maxUserInfoBytes+1))
|
||||
if err != nil || len(body) > maxUserInfoBytes || !utf8.Valid(body) {
|
||||
return nil, errors.New("current UserInfo response invalid")
|
||||
}
|
||||
// Reject duplicate top-level keys before Unmarshal can hide a conflicting
|
||||
// groups or subject claim. json.RawMessage permits unrelated normal claims.
|
||||
decoder := json.NewDecoder(bytes.NewReader(body))
|
||||
token, err := decoder.Token()
|
||||
if err != nil || token != json.Delim('{') {
|
||||
return nil, errors.New("current UserInfo object required")
|
||||
}
|
||||
claims := map[string]json.RawMessage{}
|
||||
for decoder.More() {
|
||||
keyToken, err := decoder.Token()
|
||||
if err != nil {
|
||||
return nil, errors.New("current UserInfo key invalid")
|
||||
}
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("current UserInfo key invalid")
|
||||
}
|
||||
if _, duplicate := claims[key]; duplicate {
|
||||
return nil, errors.New("current UserInfo duplicate claim")
|
||||
}
|
||||
var value json.RawMessage
|
||||
if decoder.Decode(&value) != nil {
|
||||
return nil, errors.New("current UserInfo value invalid")
|
||||
}
|
||||
claims[key] = value
|
||||
}
|
||||
if token, err = decoder.Token(); err != nil || token != json.Delim('}') {
|
||||
return nil, errors.New("current UserInfo object invalid")
|
||||
}
|
||||
var trailing any
|
||||
if decoder.Decode(&trailing) != io.EOF {
|
||||
return nil, errors.New("current UserInfo trailing data")
|
||||
}
|
||||
raw, present := claims[t.claim]
|
||||
if !present {
|
||||
return nil, errors.New("current UserInfo required claim missing")
|
||||
}
|
||||
var value any
|
||||
if json.Unmarshal(raw, &value) != nil {
|
||||
return nil, errors.New("current UserInfo required claim invalid")
|
||||
}
|
||||
if _, ok := strictClaimValues(value); !ok {
|
||||
return nil, errors.New("current UserInfo required claim array invalid")
|
||||
}
|
||||
// Return the original public response bytes to Goth. Subject equality and
|
||||
// audience/issuer checks remain in its existing authenticated provider path.
|
||||
response.Body = io.NopCloser(bytes.NewReader(body))
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var caseValues = []string{"forge-admins", "forge-coulomb-owners", "forge-coulomb-readers"}
|
||||
|
||||
type roundTripFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
||||
|
||||
func mockUserInfo(body string, status int) *http.Client {
|
||||
return &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return &http.Response{StatusCode: status, Header: http.Header{}, Body: io.NopCloser(strings.NewReader(body)), Request: r}, nil
|
||||
})}
|
||||
}
|
||||
|
||||
func TestRequiredClaimExactAnyOfAndNoMutation(t *testing.T) {
|
||||
for _, group := range caseValues {
|
||||
groups := []any{group, "unrelated"}
|
||||
claims := map[string]any{"groups": groups, "other": "preserved"}
|
||||
if !MatchRequiredClaimValues(claims, "groups", "", caseValues) {
|
||||
t.Fatal("individual allowed group rejected")
|
||||
}
|
||||
if !reflect.DeepEqual(claims, map[string]any{"groups": groups, "other": "preserved"}) {
|
||||
t.Fatal("claims mutated")
|
||||
}
|
||||
}
|
||||
if !MatchRequiredClaimValues(map[string]any{"groups": append([]string(nil), caseValues...)}, "groups", "", caseValues) {
|
||||
t.Fatal("multiple allowed groups rejected")
|
||||
}
|
||||
for _, raw := range []any{nil, true, 1, "forge-admins", "forge-admins,forge-coulomb-owners", []any{"forge-admins", 1}, []any{"forge-admins", nil}, []string{}, []any{}, []string{"forge-admins-lookalike"}, []string{" Forge-admins"}, []string{"forge-admins\n"}, []string{"Forge-admins"}, []string{"forge-admins,other"}, []string{"forge-admins", ""}} {
|
||||
if MatchRequiredClaimValues(map[string]any{"groups": raw}, "groups", "", caseValues) {
|
||||
t.Fatal("malformed/nonmatching claim accepted")
|
||||
}
|
||||
}
|
||||
if MatchRequiredClaimValues(map[string]any{}, "groups", "", caseValues) {
|
||||
t.Fatal("absent claim accepted")
|
||||
}
|
||||
if MatchRequiredClaimValues(map[string]any{"unrelated": []string{"forge-admins"}}, "groups", "", caseValues) {
|
||||
t.Fatal("unrelated claim used")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiredClaimConfigurationRefusalsAndLegacyIsolation(t *testing.T) {
|
||||
if ValidateRequiredClaimValues("", "old arbitrary literal", nil) != nil {
|
||||
t.Fatal("legacy config changed")
|
||||
}
|
||||
for _, cfg := range []struct {
|
||||
name, legacy string
|
||||
values []string
|
||||
}{
|
||||
{"groups", "", []string{}}, {"groups", "", []string{""}}, {"groups", "old", caseValues}, {"", "", caseValues},
|
||||
{"groups", "", []string{"a", "a"}}, {"groups", "", []string{"a,b"}}, {"groups", "", []string{" a"}},
|
||||
} {
|
||||
if ValidateRequiredClaimValues(cfg.name, cfg.legacy, cfg.values) == nil {
|
||||
t.Fatal("invalid config accepted")
|
||||
}
|
||||
}
|
||||
if ValidateRequiredClaimValues("groups", "", caseValues) != nil {
|
||||
t.Fatal("case config invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentUserInfoGuardRejectsMissingMalformedAndUnavailable(t *testing.T) {
|
||||
for _, body := range []string{
|
||||
`{}`, `{"sub":"alice"}`, `{"groups":null}`, `{"groups":true}`, `{"groups":"forge-admins"}`,
|
||||
`{"groups":["forge-admins",null]}`, `{"groups":["forge-admins",1]}`,
|
||||
`{"groups":["forge-admins"],"groups":[]}`, `{"groups":[]} {}`, `[]`, `{"groups":[]`,
|
||||
`{"groups":["forge-admins",""]}`, strings.Repeat(" ", maxUserInfoBytes+1),
|
||||
} {
|
||||
client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response, err := client.Get("https://issuer.example/userinfo")
|
||||
if err == nil {
|
||||
response.Body.Close()
|
||||
t.Fatal("invalid current claim did not fail closed")
|
||||
}
|
||||
}
|
||||
client, _ := RequireCurrentUserInfoClaim(mockUserInfo(`{"groups":[]}`, 503), "https://issuer.example/userinfo", "groups")
|
||||
if response, err := client.Get("https://issuer.example/userinfo"); err == nil {
|
||||
response.Body.Close()
|
||||
t.Fatal("non200 accepted")
|
||||
}
|
||||
broken := &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) { return nil, errors.New("private backend error") })}
|
||||
client, _ = RequireCurrentUserInfoClaim(broken, "https://issuer.example/userinfo", "groups")
|
||||
if _, err := client.Get("https://issuer.example/userinfo"); err == nil || strings.Contains(err.Error(), "private backend") {
|
||||
t.Fatal("lookup failure not contained")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentUserInfoEmptyArrayOverwritesAndGroupRemoval(t *testing.T) {
|
||||
for _, body := range []string{`{"sub":"alice","groups":[]}`, `{"sub":"alice","groups":["forge-coulomb-readers"]}`} {
|
||||
client, err := RequireCurrentUserInfoClaim(mockUserInfo(body, 200), "https://issuer.example/userinfo", "groups")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response, err := client.Get("https://issuer.example/userinfo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, _ := io.ReadAll(response.Body)
|
||||
response.Body.Close()
|
||||
if string(b) != body {
|
||||
t.Fatal("response changed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentUserInfoGuardIsOptInAndRedirectsRefuse(t *testing.T) {
|
||||
original := mockUserInfo(`{"unrelated":true}`, 200)
|
||||
guarded, err := RequireCurrentUserInfoClaim(original, "https://issuer.example/userinfo", "groups")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response, err := original.Get("https://issuer.example/userinfo")
|
||||
if err != nil {
|
||||
t.Fatal("original client mutated")
|
||||
}
|
||||
response.Body.Close()
|
||||
response, err = guarded.Get("https://issuer.example/unrelated")
|
||||
if err != nil {
|
||||
t.Fatal("unrelated endpoint changed")
|
||||
}
|
||||
response.Body.Close()
|
||||
if guarded.CheckRedirect == nil || guarded.CheckRedirect(nil, nil) == nil {
|
||||
t.Fatal("redirect can bypass guard")
|
||||
}
|
||||
for _, endpoint := range []string{"", "http://issuer.example/userinfo", "https://user:pass@issuer.example/userinfo", "https://issuer.example/userinfo#fragment"} {
|
||||
if _, err := RequireCurrentUserInfoClaim(original, endpoint, "groups"); err == nil {
|
||||
t.Fatal("invalid endpoint accepted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"forgejo.org/models/auth"
|
||||
"forgejo.org/modules/json"
|
||||
)
|
||||
|
||||
// Source holds configuration for the OAuth2 login source.
|
||||
type Source struct {
|
||||
Provider string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
OpenIDConnectAutoDiscoveryURL string
|
||||
CustomURLMapping *CustomURLMapping
|
||||
IconURL string
|
||||
|
||||
Scopes []string
|
||||
AttributeSSHPublicKey string
|
||||
RequiredClaimName string
|
||||
RequiredClaimValues []string `json:",omitempty"`
|
||||
RequiredClaimValue string
|
||||
GroupClaimName string
|
||||
AdminGroup string
|
||||
GroupTeamMap string
|
||||
GroupTeamMapRemoval bool
|
||||
RestrictedGroup string
|
||||
SkipLocalTwoFA bool `json:",omitempty"`
|
||||
|
||||
// reference to the authSource
|
||||
authSource *auth.Source
|
||||
}
|
||||
|
||||
// FromDB fills up an OAuth2Config from serialized format.
|
||||
func (source *Source) FromDB(bs []byte) error {
|
||||
return json.UnmarshalHandleDoubleEncode(bs, &source)
|
||||
}
|
||||
|
||||
// ToDB exports an SMTPConfig to a serialized format.
|
||||
func (source *Source) ToDB() ([]byte, error) {
|
||||
return json.Marshal(source)
|
||||
}
|
||||
|
||||
// ProvidesSSHKeys returns if this source provides SSH Keys
|
||||
func (source *Source) ProvidesSSHKeys() bool {
|
||||
return len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0
|
||||
}
|
||||
|
||||
// SetAuthSource sets the related AuthSource
|
||||
func (source *Source) SetAuthSource(authSource *auth.Source) {
|
||||
source.authSource = authSource
|
||||
}
|
||||
|
||||
func init() {
|
||||
auth.RegisterTypeConfig(auth.OAuth2, &Source{})
|
||||
}
|
||||
|
|
@ -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,151 @@
|
|||
# Target Revenue Source License
|
||||
|
||||
**Version 1.0, Candidate 1 (V1C1)**
|
||||
|
||||
---
|
||||
|
||||
> **PRELIMINARY CANDIDATE — SUBJECT TO CHANGE — NOT FINAL**
|
||||
>
|
||||
> This repository is governed by the Target Revenue Source License
|
||||
> (TRSL), Version 1, Candidate 1. This is the framework's first working
|
||||
> candidate, adopted as the org's preliminary operating license across
|
||||
> all repos (maintainer decision, 2026-07-29) during the build/alpha
|
||||
> stage — see `SCOPE.md` §1 and Appendix A of the canonical text (link
|
||||
> below) for the alpha/beta risk-acceptance decision this reflects and
|
||||
> what it does and does not mean. Full specialist legal review is
|
||||
> explicitly deferred until the framework moves out of beta.
|
||||
>
|
||||
> **Canonical source, full candidate-status banner, and Appendix A
|
||||
> (non-normative candidate notes tracking every open item):** the
|
||||
> `coulomb` org's `target-revenue` repository,
|
||||
> `specs/TargetRevenueSourceLicense-V1C1.md` — this file is the operative
|
||||
> legal text (Preamble through Section 11) only; the canonical document
|
||||
> is authoritative if this copy and that document ever diverge.
|
||||
|
||||
---
|
||||
|
||||
## Preamble
|
||||
|
||||
This Target Revenue Source License ("**License**") governs the Software identified in the applicable Phase Manifest. It implements the Target Revenue Framework: a defined development Phase accumulates Development Credit and Remission Credit against an immutable Initial Target until the Milestone Release automatically and irrevocably converts to a declared permissive Future License.
|
||||
|
||||
Commercial beneficiaries fund the creation and early availability of a software improvement; once the declared target is satisfied, the governed release becomes permissively open source.
|
||||
|
||||
## 1. Definitions
|
||||
|
||||
Capitalized terms used in this License have the meanings given below. Where a term is also defined in the Phase Manifest or Target Ledger for a specific Phase, the Phase Manifest and Target Ledger govern the *values* (amounts, dates, identifiers) and this License governs the *legal effect* of those values — the two must not be read as conflicting definitions of the same concept.
|
||||
|
||||
**"Commercial Entitlement"** means a right, purchased or otherwise granted under a Commercial Use Agreement, to make Commercial Use of the Software during a Phase.
|
||||
|
||||
**"Commercial Use"** means billing, invoicing, or otherwise charging any customer a fee, subscription, license fee, or other consideration for or in connection with use of the Software, at any time before the Conversion Event for the applicable Phase, regardless of whether the person or organization billed would otherwise qualify for Noncommercial Use. Commercial Use occurs by virtue of such billing alone, whether or not the resulting payment is registered with the Trust Service; in particular, billing a customer for pre-conversion use of the Software without recording the corresponding payment in the applicable Phase's Target Ledger is Commercial Use without a valid Commercial Entitlement — a violation of Section 3, addressed under Section 7 and, where applicable, the Enforcement Network described in the canonical repository's `specs/EnforcementNetworkConcept.md`.
|
||||
|
||||
**"Commercial Use Agreement"** means the separate agreement, referenced by the applicable Phase Manifest, under which a Commercial Entitlement is purchased or granted. This License does not itself set pricing, metering, or payment terms — those are governed by the Commercial Use Agreement.
|
||||
|
||||
**"Conversion Event"** means the moment the Outstanding Target for a Phase reaches zero, as computed from the Phase Manifest and Target Ledger per the Target Ledger Specification. The Conversion Event occurs automatically and is not conditioned on any declaration, attestation, or other act by the Licensor or any Trust Service.
|
||||
|
||||
**"Development Credit"** means the portion of a collected and settled payment explicitly allocated toward satisfying the Initial Target of a specific Phase, as recorded in that Phase's Target Ledger.
|
||||
|
||||
**"Future License"** means the permissive license identified in the applicable Phase Manifest, being either the MIT License or the Apache License, Version 2.0, which applies to the Milestone Release upon the Conversion Event.
|
||||
|
||||
**"Initial Target"** means the immutable monetary target declared for a Phase in its Phase Manifest.
|
||||
|
||||
**"Licensor"** means **Binky Hedgehog GmbH**, the party that publishes the Phase Manifest and holds the rights necessary to grant this License and the Future License for the Milestone Release.
|
||||
|
||||
**"Milestone Release"** means the precisely identified software release designated in the applicable Phase Manifest, identified by an immutable source revision, release artifact, or cryptographic digest.
|
||||
|
||||
**"Noncommercial Use"** means use of the Software for personal purposes, private study, hobby or amateur projects; use by any charitable organization, educational institution, public research organization, or government institution acting in a non-revenue-generating capacity; or other use of a materially similar character.
|
||||
|
||||
**"Outstanding Target"** means, at any time, `max(0, Initial Target − cumulative Development Credit − cumulative Remission Credit)` for a Phase, as computed from that Phase's Target Ledger.
|
||||
|
||||
**"Phase"** means a bounded development undertaking governed by one Initial Target, one Milestone Release, one degeneration policy, and one Future License declaration, as declared in a Phase Manifest.
|
||||
|
||||
**"Phase Manifest"** means the published, immutable declaration identifying a Phase, its Milestone Release, Initial Target, Future License, degeneration policy, and Target Ledger location, as specified in the Phase Manifest Specification.
|
||||
|
||||
**"Remission Credit"** means a transparent, non-revenue reduction of a Phase's Outstanding Target, generated under that Phase's published degeneration policy and recorded in the Target Ledger.
|
||||
|
||||
**"Settled Payment"** means a payment that has cleared through its payment processor and is no longer subject to reversal in the ordinary course (chargeback, dispute, or equivalent), as further specified by the applicable Commercial Use Agreement or monetization extension.
|
||||
|
||||
**"Software"** means the source code, object code, and associated documentation of the Milestone Release identified in the applicable Phase Manifest.
|
||||
|
||||
**"Target Ledger"** means the append-only record of Development Credit, Remission Credit, and correction entries for a Phase, as specified in the Target Ledger Specification.
|
||||
|
||||
**"You"** or **"Licensee"** means the individual or entity exercising rights under this License.
|
||||
|
||||
## 2. Grant of Rights for Noncommercial Use
|
||||
|
||||
Subject to the terms of this License, the Licensor grants You a worldwide, royalty-free, non-exclusive license, during the applicable Phase, to:
|
||||
|
||||
(a) use, reproduce, and study the Software for any Noncommercial Use;
|
||||
|
||||
(b) modify the Software and create derivative works of it for any Noncommercial Use; and
|
||||
|
||||
(c) redistribute the Software and Your modifications, in source or object form, for any Noncommercial Use, provided that You include this License, unmodified, with any such redistribution, and that You do not remove or alter any copyright, patent, trademark, or attribution notices contained in the Software.
|
||||
|
||||
This grant does not extend to Commercial Use. Commercial Use requires a Commercial Entitlement under Section 3.
|
||||
|
||||
## 3. Commercial Use
|
||||
|
||||
You may not make Commercial Use of the Software during the applicable Phase unless You hold a valid, current Commercial Entitlement under a Commercial Use Agreement with the Licensor covering the applicable Phase. A Commercial Entitlement granted under one Phase's Commercial Use Agreement does not extend to a later Phase's Milestone Release unless the Commercial Use Agreement expressly says so.
|
||||
|
||||
This Section 3 states the existence and boundary of the commercial-use restriction. It does not itself set pricing, invoicing, metering, audit rights, or payment terms — those are governed exclusively by the applicable Commercial Use Agreement.
|
||||
|
||||
## 4. Patent License
|
||||
|
||||
Subject to the terms of this License, each contributor to the Software grants You, during the applicable Phase and solely to the extent of rights granted under Sections 2 and 3, a perpetual (subject to the termination below), worldwide, non-exclusive, no-charge, royalty-free patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Software, limited to those patent claims licensable by that contributor that are necessarily infringed by their contribution(s) alone or by combination of their contribution(s) with the Software.
|
||||
|
||||
If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software or a contribution incorporated within it constitutes direct or contributory patent infringement, then any patent licenses granted to You under this Section 4 for the Software shall terminate as of the date such litigation is filed.
|
||||
|
||||
## 5. Automatic Conversion to the Future License
|
||||
|
||||
**5.1 Automatic effect.** Upon the Conversion Event for a Phase, the rights and restrictions in Sections 3 (Commercial Use) of this License, as they apply to that Phase's Milestone Release, terminate automatically. In their place, the Milestone Release is licensed under the Future License identified in that Phase's Phase Manifest, effective as of the Conversion Event, without any further act, declaration, or attestation required by the Licensor, any Trust Service, or any other party.
|
||||
|
||||
**5.2 Irrevocability.** Once a valid Conversion Event has occurred for a Phase, no subsequent refund, chargeback, accounting correction, dispute, or termination of this License for an unrelated breach shall revoke, suspend, or otherwise impair the Future License grant for that Phase's Milestone Release. Any shortfall or dispute arising after a Conversion Event is a commercial or accounting matter between the relevant parties and does not reinstate a commercial-use restriction over already-converted Software.
|
||||
|
||||
**5.3 Prior freedom preserved.** A later Phase covering subsequent improvements to the Software does not restrict, withdraw, or otherwise affect the rights granted under the Future License for an earlier Phase's already-converted Milestone Release.
|
||||
|
||||
**5.4 Evidence, not cause.** A Trust Service may publish a Conversion Attestation documenting a Conversion Event. Such an attestation is evidence that the Conversion Event occurred; it is not a condition of, and its absence or delay does not postpone, the automatic effect described in Section 5.1. Any person may independently verify whether a Conversion Event has occurred directly from the Phase Manifest and Target Ledger.
|
||||
|
||||
## 6. Successive Phases
|
||||
|
||||
The Licensor may declare a new Phase covering subsequent improvements to the Software following a Milestone Release's Conversion Event. Each Phase is independently governed by its own Phase Manifest, Initial Target, degeneration policy, and Target Ledger. Nothing in a later Phase's Phase Manifest may be construed to reduce or withdraw rights already granted under Section 5 for an earlier Phase's Milestone Release.
|
||||
|
||||
## 7. Term and Termination
|
||||
|
||||
**7.1 Term.** This License applies to the Software for the duration of the applicable Phase, and, for the Milestone Release, indefinitely following that Phase's Conversion Event under the Future License.
|
||||
|
||||
**7.2 Termination for breach.** If You breach Section 3 (Commercial Use) or Section 2(c) (redistribution notice requirement), the Licensor may terminate this License as to You. Before such termination becomes effective, the Licensor shall provide You written notice of the breach; if You cure the breach within thirty (30) days of that notice, this License continues in effect. A second breach of the same provision within twelve (12) months may be terminated immediately without a further cure opportunity.
|
||||
|
||||
**7.3 Effect of termination.** Termination under this Section 7 affects only Your rights under Sections 2 and 3 for the Phase in which the breach occurred. It does not affect any rights already vested under Section 5 (Automatic Conversion) for a Milestone Release whose Conversion Event has already occurred, per Section 5.2.
|
||||
|
||||
**7.4 Public record of breach and resolution.** The Licensor shall cause the Trust Service to publish, as part of the public record for the affected Phase, notice of: (a) any breach notice issued under Section 7.2, stating the general nature of the breach and the date of notice; (b) whether the breach was cured within the applicable cure period, and the date of cure; and (c) any termination determination made under this Section 7, including its effective date and scope. This public record exists to give the ecosystem a transparent, verifiable conformity signal for the Phase, distinct from and in addition to the Development Credit and Remission Credit facts already published under Section 5.4 and the Target Ledger Specification.
|
||||
|
||||
A breach that You dispute, and that has not been finally determined, shall be recorded as **alleged**; it shall be recorded as **determined** only once the cure period has run without cure, or the dispute has been resolved against You under the applicable Commercial Use Agreement's dispute process, if any. The Trust Service shall update the record promptly upon resolution in either direction. Recording an alleged or determined breach under this Section 7.4 is a ministerial act of publishing the Licensor's determination (or a dispute process's outcome); it does not give the Trust Service discretionary authority to decide whether a breach occurred, consistent with Section 5.4's evidence-not-cause principle.
|
||||
|
||||
Whether, and under what conditions, the public record identifies a Commercial Entitlement holder by name is governed exclusively by the applicable Commercial Use Agreement, which the Licensor and that Commercial Entitlement holder negotiate and agree to directly. This License does not itself set a naming default. Where no Commercial Use Agreement addresses the question, or where the affected party has no Commercial Use Agreement at all (for example, a Section 2(c) breach by a Noncommercial Use licensee), the public record states the Phase and breach category only, without naming the party.
|
||||
|
||||
## 8. Disclaimer of Warranty
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. THE LICENSOR DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR-FREE OR THAT ANY PHASE WILL REACH ITS CONVERSION EVENT.
|
||||
|
||||
## 9. Limitation of Liability
|
||||
|
||||
IN NO EVENT SHALL THE LICENSOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EXCEPT TO THE EXTENT SUCH LIMITATION IS PROHIBITED BY APPLICABLE LAW.
|
||||
|
||||
## 10. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary attribution.
|
||||
|
||||
## 11. General Provisions
|
||||
|
||||
**11.1 Governing law and venue.** Adopted for alpha/beta 2026-07-29: disputes arising under this License shall be resolved by binding arbitration, seated at a neutral, arbitration-mature venue (Singapore or London are the two candidates concretely supported by current research), rather than by litigation in a national court. The specific arbitral institution and substantive governing law remain a per-deployment blank pending final selection; they are not fixed by this candidate. See the canonical repository's `history/260729-TRSL-Jurisdiction-Synthesis.md` §2.
|
||||
|
||||
**11.2 Severability.** If any provision of this License is held unenforceable, the remaining provisions remain in full force, and the unenforceable provision shall be reformed to the minimum extent necessary to make it enforceable.
|
||||
|
||||
**11.3 No waiver.** Failure to enforce any provision of this License is not a waiver of future enforcement of that or any other provision.
|
||||
|
||||
**11.4 Entire agreement (as to licensing).** This License, together with the applicable Phase Manifest and, where applicable, the Commercial Use Agreement, constitutes the entire agreement between You and the Licensor regarding the Software's licensing terms. Operations, service, and consulting arrangements are governed by separate agreements, if any, and are not part of this License.
|
||||
|
||||
**11.5 Definitions control.** Marketing materials, documentation, or other non-normative communications about the Software must not describe pre-Conversion-Event Software as "Open Source," "free software," or "open core." Pre-conversion Noncommercial Use is **source-available**; pre-conversion Commercial Use requires a **Commercial Entitlement**; only post-conversion Software may be described as Open Source, under the Future License.
|
||||
|
||||
---
|
||||
|
||||
**No Phase is currently declared for this repository under this License.** Until a Phase Manifest is published and registered with the Trust Service for a Milestone Release in this repository, Sections 2–7 above have no operative subject matter here — this License establishes the governing framework in advance of that declaration, consistent with the org-wide rollout decision recorded in `target-revenue`'s `workplans/TREV-WP-0008-governance-and-pilot-rollout.md`.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/netkingdom/access-engine
|
||||
|
||||
go 1.24
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
// Package sign attaches a detached Ed25519 signature to a decision envelope
|
||||
// (FLEX-WP-0024). The signed material is json.Marshal of the envelope with
|
||||
// the signature field omitted.
|
||||
package sign
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
)
|
||||
|
||||
const AlgorithmEd25519 = "ed25519"
|
||||
|
||||
// CanonicalBytes is the exact payload that is signed and verified: the
|
||||
// envelope as emitted, with signature absent.
|
||||
func CanonicalBytes(envelope api.DecisionEnvelope) ([]byte, error) {
|
||||
envelope.Signature = nil
|
||||
data, err := json.Marshal(envelope)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal canonical envelope: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func Sign(envelope *api.DecisionEnvelope, private ed25519.PrivateKey, kid string) error {
|
||||
if envelope == nil {
|
||||
return fmt.Errorf("envelope is required")
|
||||
}
|
||||
if len(private) != ed25519.PrivateKeySize {
|
||||
return fmt.Errorf("ed25519 private key has length %d", len(private))
|
||||
}
|
||||
if kid == "" {
|
||||
return fmt.Errorf("kid is required")
|
||||
}
|
||||
payload, err := CanonicalBytes(*envelope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sig := ed25519.Sign(private, payload)
|
||||
envelope.Signature = &api.EnvelopeSignature{
|
||||
Mode: api.EnvelopeSignatureSigned,
|
||||
Alg: AlgorithmEd25519,
|
||||
Kid: kid,
|
||||
Value: base64.RawURLEncoding.EncodeToString(sig),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Verify(envelope api.DecisionEnvelope, keys map[string]ed25519.PublicKey) error {
|
||||
sig := envelope.Signature
|
||||
if sig == nil || sig.Mode != api.EnvelopeSignatureSigned {
|
||||
return fmt.Errorf("envelope is not signed")
|
||||
}
|
||||
if sig.Alg != AlgorithmEd25519 {
|
||||
return fmt.Errorf("unsupported signature alg %q", sig.Alg)
|
||||
}
|
||||
pub, ok := keys[sig.Kid]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown signature kid %q", sig.Kid)
|
||||
}
|
||||
raw, err := base64.RawURLEncoding.DecodeString(sig.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode signature: %w", err)
|
||||
}
|
||||
payload, err := CanonicalBytes(envelope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ed25519.Verify(pub, payload, raw) {
|
||||
return fmt.Errorf("signature does not match envelope")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeEnvelope preserves JSON numeric kinds/lexemes emitted by task inputs.
|
||||
// Legacy producer envelopes remain byte-compatible. It does not invent another
|
||||
// signature canonicalization: CanonicalBytes still calls typed json.Marshal.
|
||||
func DecodeEnvelope(raw []byte) (api.DecisionEnvelope, error) {
|
||||
var envelope api.DecisionEnvelope
|
||||
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||||
decoder.UseNumber()
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&envelope); err != nil {
|
||||
return envelope, err
|
||||
}
|
||||
if _, err := decoder.Token(); err != io.EOF {
|
||||
return api.DecisionEnvelope{}, fmt.Errorf("trailing envelope data")
|
||||
}
|
||||
return envelope, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,699 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ProtectedSystemManifest describes a system that delegates authorization to
|
||||
// flex-auth.
|
||||
type ProtectedSystemManifest struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
ClientBinding *ClientBinding `json:"client_binding,omitempty" yaml:"client_binding,omitempty"`
|
||||
ResourceTypes []ResourceType `json:"resource_types,omitempty" yaml:"resource_types,omitempty"`
|
||||
Actions []ActionDefinition `json:"actions,omitempty" yaml:"actions,omitempty"`
|
||||
CaringProfiles []string `json:"caring_profiles,omitempty" yaml:"caring_profiles,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// ClientBinding names the key-cape client a protected system's users sign in
|
||||
// through. It is descriptive only: the readback registry projection lists it,
|
||||
// and it never decides an allow (FLEX-DEC-2026-019).
|
||||
type ClientBinding struct {
|
||||
Issuer string `json:"issuer" yaml:"issuer"`
|
||||
ClientID string `json:"client_id" yaml:"client_id"`
|
||||
}
|
||||
|
||||
// ResourceType describes a resource namespace entry owned by a protected system.
|
||||
type ResourceType struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
ParentTypes []string `json:"parent_types,omitempty" yaml:"parent_types,omitempty"`
|
||||
ScopeLevel ScopeLevel `json:"scope_level,omitempty" yaml:"scope_level,omitempty"`
|
||||
Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// ActionDefinition maps a protected-system action to CARING capabilities.
|
||||
type ActionDefinition struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Capabilities []Capability `json:"capabilities,omitempty" yaml:"capabilities,omitempty"`
|
||||
Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"`
|
||||
ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// SubjectManifest declares subjects, groups, teams, and tenants for local
|
||||
// registry loading.
|
||||
type SubjectManifest struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Subjects []Subject `json:"subjects,omitempty" yaml:"subjects,omitempty"`
|
||||
Groups []Group `json:"groups,omitempty" yaml:"groups,omitempty"`
|
||||
Teams []Team `json:"teams,omitempty" yaml:"teams,omitempty"`
|
||||
Tenants []Tenant `json:"tenants,omitempty" yaml:"tenants,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Subject is a human, service, automation, agent, or other acting identity.
|
||||
type Subject struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Type SubjectType `json:"type" yaml:"type"`
|
||||
DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"`
|
||||
OrganizationRelation OrganizationRelation `json:"organization_relation,omitempty" yaml:"organization_relation,omitempty"`
|
||||
Roles []CanonicalRole `json:"roles,omitempty" yaml:"roles,omitempty"`
|
||||
Groups []string `json:"groups,omitempty" yaml:"groups,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Claims map[string]any `json:"claims,omitempty" yaml:"claims,omitempty"`
|
||||
CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Group is an assignment convenience, not a canonical role.
|
||||
type Group struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"`
|
||||
Members []string `json:"members,omitempty" yaml:"members,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Team is a group-like ownership unit used by protected systems.
|
||||
type Team struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
DisplayName string `json:"display_name,omitempty" yaml:"display_name,omitempty"`
|
||||
Members []string `json:"members,omitempty" yaml:"members,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
CaringDescriptors []CaringAccessDescriptor `json:"caring_descriptors,omitempty" yaml:"caring_descriptors,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Tenant is a structural isolation boundary.
|
||||
type Tenant struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// RelationshipFact records a relation between subjects, groups, teams, tenants,
|
||||
// and resources.
|
||||
type RelationshipFact struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
System string `json:"system,omitempty" yaml:"system,omitempty"`
|
||||
Subject string `json:"subject" yaml:"subject"`
|
||||
Relation string `json:"relation" yaml:"relation"`
|
||||
Object string `json:"object" yaml:"object"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"`
|
||||
Caring *CaringAccessDescriptor `json:"caring,omitempty" yaml:"caring,omitempty"`
|
||||
Provenance map[string]any `json:"provenance,omitempty" yaml:"provenance,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// PolicyPackageMetadata is the frontmatter contract for Rego-in-Markdown
|
||||
// policy packages.
|
||||
type PolicyPackageMetadata struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
Version string `json:"version" yaml:"version"`
|
||||
Status string `json:"status,omitempty" yaml:"status,omitempty"`
|
||||
Package string `json:"package" yaml:"package"`
|
||||
Actions []string `json:"actions,omitempty" yaml:"actions,omitempty"`
|
||||
Owner string `json:"owner,omitempty" yaml:"owner,omitempty"`
|
||||
Fixtures []string `json:"fixtures,omitempty" yaml:"fixtures,omitempty"`
|
||||
Caring CaringPolicyMetadata `json:"caring" yaml:"caring"`
|
||||
Activation map[string]any `json:"activation,omitempty" yaml:"activation,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
// AllowTTL is a Go duration (for example "15m") that bounds every allow
|
||||
// this package produces. Omit to use DefaultAllowTTL. "none" or "0s"
|
||||
// means no stated end; the engine denies those allows (§9.7.1).
|
||||
AllowTTL string `json:"allow_ttl,omitempty" yaml:"allow_ttl,omitempty"`
|
||||
}
|
||||
|
||||
// CaringPolicyMetadata declares the CARING envelope a policy governs.
|
||||
type CaringPolicyMetadata struct {
|
||||
Profile string `json:"profile" yaml:"profile"`
|
||||
Enforce bool `json:"enforce,omitempty" yaml:"enforce,omitempty"`
|
||||
CanonicalRoles []CanonicalRole `json:"canonical_roles,omitempty" yaml:"canonical_roles,omitempty"`
|
||||
OrganizationRelations []OrganizationRelation `json:"organization_relations,omitempty" yaml:"organization_relations,omitempty"`
|
||||
Scopes []CaringScope `json:"scopes,omitempty" yaml:"scopes,omitempty"`
|
||||
Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"`
|
||||
Capabilities []Capability `json:"capabilities,omitempty" yaml:"capabilities,omitempty"`
|
||||
ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"`
|
||||
Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"`
|
||||
Restrictions []Restriction `json:"restrictions,omitempty" yaml:"restrictions,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// PolicyFixture binds a check request to an expected decision.
|
||||
type PolicyFixture struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Request CheckRequest `json:"request" yaml:"request"`
|
||||
Expect DecisionExpectation `json:"expect" yaml:"expect"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// DecisionExpectation is the compact fixture expectation for policy tests.
|
||||
type DecisionExpectation struct {
|
||||
Effect DecisionEffect `json:"effect" yaml:"effect"`
|
||||
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
|
||||
Obligations []Obligation `json:"obligations,omitempty" yaml:"obligations,omitempty"`
|
||||
ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"`
|
||||
}
|
||||
|
||||
// CheckRequest is the stable protected-system-facing decision request.
|
||||
type CheckRequest struct {
|
||||
ID string `json:"id,omitempty" yaml:"id,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Subject SubjectRef `json:"subject" yaml:"subject"`
|
||||
Action string `json:"action" yaml:"action"`
|
||||
Resource ResourceRef `json:"resource" yaml:"resource"`
|
||||
Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"`
|
||||
CaringContext *CaringAccessDescriptor `json:"caring_context,omitempty" yaml:"caring_context,omitempty"`
|
||||
PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"`
|
||||
|
||||
// Purpose is empty on every enforcement request. The readback pin sets it
|
||||
// to PurposeReadback, and it is hashed into every request digest, so an
|
||||
// advisory readback decision can never pass a PEP's §6.4.2 replay test for
|
||||
// an enforcement request (FLEX-DEC-2026-019). /v1/check and /v1/batch_check
|
||||
// refuse a request that carries it.
|
||||
Purpose string `json:"purpose,omitempty" yaml:"purpose,omitempty"`
|
||||
}
|
||||
|
||||
// PurposeReadback marks an advisory application-access readback decision.
|
||||
const PurposeReadback = "readback"
|
||||
|
||||
// BatchCheckRequest evaluates one subject/action against multiple resources.
|
||||
type BatchCheckRequest struct {
|
||||
ID string `json:"id,omitempty" yaml:"id,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Subject SubjectRef `json:"subject" yaml:"subject"`
|
||||
Action string `json:"action" yaml:"action"`
|
||||
Resources []ResourceRef `json:"resources" yaml:"resources"`
|
||||
Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"`
|
||||
PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"`
|
||||
}
|
||||
|
||||
// SubjectRef is a normalized subject reference in request and decision shapes.
|
||||
type SubjectRef struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Type SubjectType `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
// ResourceRef is a normalized resource reference in request and decision shapes.
|
||||
type ResourceRef struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
System string `json:"system,omitempty" yaml:"system,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
// DecisionEffect is the stable decision outcome vocabulary.
|
||||
type DecisionEffect string
|
||||
|
||||
const (
|
||||
DecisionEffectAllow DecisionEffect = "allow"
|
||||
DecisionEffectDeny DecisionEffect = "deny"
|
||||
DecisionEffectRedact DecisionEffect = "redact"
|
||||
DecisionEffectAuditOnly DecisionEffect = "audit_only"
|
||||
DecisionEffectNotApplicable DecisionEffect = "not_applicable"
|
||||
)
|
||||
|
||||
// DecisionEffects returns the complete effect vocabulary. Every effect is an
|
||||
// emission class in cadence.yaml; a test fails if a constant above is missing
|
||||
// here, so a new effect cannot ship unclassified (FLEX-WP-0031-T03).
|
||||
func DecisionEffects() []DecisionEffect {
|
||||
return []DecisionEffect{
|
||||
DecisionEffectAllow,
|
||||
DecisionEffectDeny,
|
||||
DecisionEffectRedact,
|
||||
DecisionEffectAuditOnly,
|
||||
DecisionEffectNotApplicable,
|
||||
}
|
||||
}
|
||||
|
||||
// Restricts reports whether an effect withholds authority. This classification
|
||||
// does not exempt an effect from required durable emission (GH-DEC-2026-024).
|
||||
func (e DecisionEffect) Restricts() bool {
|
||||
switch e {
|
||||
case DecisionEffectDeny, DecisionEffectRedact, DecisionEffectNotApplicable:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DecisionRecordContractV1 is the published decision-record contract identifier.
|
||||
const DecisionRecordContractV1 = "flex-auth.decision-record.v1"
|
||||
|
||||
// DecisionEnvelope is the stable response produced by standalone and delegated
|
||||
// evaluators. It is flex-auth's published decision-record contract (§17).
|
||||
type DecisionEnvelope struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
ContractVersion string `json:"contract_version,omitempty" yaml:"contract_version,omitempty"`
|
||||
RequestID string `json:"request_id,omitempty" yaml:"request_id,omitempty"`
|
||||
Effect DecisionEffect `json:"effect" yaml:"effect"`
|
||||
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
|
||||
MatchedPolicyVersion string `json:"matched_policy_version,omitempty" yaml:"matched_policy_version,omitempty"`
|
||||
MatchedRule string `json:"matched_rule,omitempty" yaml:"matched_rule,omitempty"`
|
||||
Resource ResourceRef `json:"resource" yaml:"resource"`
|
||||
Subject SubjectRef `json:"subject" yaml:"subject"`
|
||||
Binding *DecisionBinding `json:"binding,omitempty" yaml:"binding,omitempty"`
|
||||
Lifetime *DecisionLifetime `json:"lifetime,omitempty" yaml:"lifetime,omitempty"`
|
||||
Obligations []Obligation `json:"obligations,omitempty" yaml:"obligations,omitempty"`
|
||||
Diagnostics map[string]any `json:"diagnostics,omitempty" yaml:"diagnostics,omitempty"`
|
||||
Provenance DecisionProvenance `json:"provenance" yaml:"provenance"`
|
||||
Signature *EnvelopeSignature `json:"signature,omitempty" yaml:"signature,omitempty"`
|
||||
Caring *CaringDecisionMetadata `json:"caring,omitempty" yaml:"caring,omitempty"`
|
||||
}
|
||||
|
||||
// EnvelopeSignatureMode is how the responder authenticates the envelope.
|
||||
type EnvelopeSignatureMode string
|
||||
|
||||
const (
|
||||
EnvelopeSignatureSigned EnvelopeSignatureMode = "signed"
|
||||
EnvelopeSignatureUnsigned EnvelopeSignatureMode = "unsigned"
|
||||
)
|
||||
|
||||
// EnvelopeSignature is a detached signature over the canonical envelope with
|
||||
// this field omitted (FLEX-WP-0024). It is not decision material and is not
|
||||
// part of request_digest.
|
||||
type EnvelopeSignature struct {
|
||||
Mode EnvelopeSignatureMode `json:"mode" yaml:"mode"`
|
||||
Alg string `json:"alg,omitempty" yaml:"alg,omitempty"`
|
||||
Kid string `json:"kid,omitempty" yaml:"kid,omitempty"`
|
||||
Value string `json:"value,omitempty" yaml:"value,omitempty"`
|
||||
}
|
||||
|
||||
func UnsignedEnvelopeSignature() *EnvelopeSignature {
|
||||
return &EnvelopeSignature{Mode: EnvelopeSignatureUnsigned}
|
||||
}
|
||||
|
||||
// DecisionLifetimeKind identifies how an allow ends.
|
||||
type DecisionLifetimeKind string
|
||||
|
||||
const (
|
||||
DecisionLifetimeTTL DecisionLifetimeKind = "ttl"
|
||||
)
|
||||
|
||||
// DefaultAllowTTL is the engine default when a policy package omits allow_ttl.
|
||||
const DefaultAllowTTL = 15 * time.Minute
|
||||
|
||||
// ReasonAllowLifetimeUnstated is the deny reason for an allow with no stated end.
|
||||
const ReasonAllowLifetimeUnstated = "allow_lifetime_unstated"
|
||||
|
||||
// DecisionLifetime bounds an allow (§9.7.1). flex-auth has no session concept,
|
||||
// so the first honest shape is a policy-package-declared TTL.
|
||||
type DecisionLifetime struct {
|
||||
Kind DecisionLifetimeKind `json:"kind" yaml:"kind"`
|
||||
TTL string `json:"ttl,omitempty" yaml:"ttl,omitempty"`
|
||||
NotBefore string `json:"not_before,omitempty" yaml:"not_before,omitempty"`
|
||||
ExpiresAt string `json:"expires_at" yaml:"expires_at"`
|
||||
}
|
||||
|
||||
// DecisionBinding is the exact normalized authorization request evaluated by
|
||||
// a decision. It lets a consumer verify structured action, target, actor, and
|
||||
// context fields without parsing reason or diagnostic prose.
|
||||
type DecisionBinding struct {
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Subject SubjectRef `json:"subject" yaml:"subject"`
|
||||
Action string `json:"action" yaml:"action"`
|
||||
Resource ResourceRef `json:"resource" yaml:"resource"`
|
||||
Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"`
|
||||
// Purpose is "readback" on an advisory readback decision and absent on an
|
||||
// enforcement decision (FLEX-DEC-2026-019).
|
||||
Purpose string `json:"purpose,omitempty" yaml:"purpose,omitempty"`
|
||||
RequestDigest string `json:"request_digest" yaml:"request_digest"`
|
||||
|
||||
// ApprovalBindingDigest is RequestDigest computed with the approval claim
|
||||
// removed from context. It exists to break a circularity: an approval's
|
||||
// pdp_digest is recorded at issue time, but the request that later carries
|
||||
// the claim inside its hashed context has a different RequestDigest by
|
||||
// construction, so a claim can never name the request that carries it.
|
||||
//
|
||||
// This digest is stable across attaching the claim, so it is the value
|
||||
// claim.binding.pdp_digest must equal (GH-DEC-2026-008). It is NOT a replay
|
||||
// identity and must not be used as one: two requests differing only in
|
||||
// which approval was presented share it, and the decision does not.
|
||||
ApprovalBindingDigest string `json:"approval_binding_digest,omitempty" yaml:"approval_binding_digest,omitempty"`
|
||||
|
||||
// SubmittedRequestDigest is RequestDigest over the request exactly as the
|
||||
// consumer sent it, before the evaluator overlays registry facts onto the
|
||||
// subject and resource.
|
||||
//
|
||||
// It exists because RequestDigest is not consumer-computable and was
|
||||
// published as though it were. The evaluator hashes the ENRICHED request —
|
||||
// subject.tenant, subject.attributes, and resource attributes the registry
|
||||
// contributed — and the registry is flex-auth's, so a consumer recomputing
|
||||
// the digest over what it sent gets a different value on every real allow.
|
||||
// secrets-engine found this on its first live request (FLEX-DEC-2026-012).
|
||||
//
|
||||
// This is the digest a PEP compares for §6.4 obligation 2's replay test.
|
||||
// Nothing is lost by hashing the pre-enrichment form: enrichment is a
|
||||
// function of the request and the registry snapshot, and
|
||||
// provenance.registry_snapshot_digest already pins the snapshot, so
|
||||
// SubmittedRequestDigest together with that digest identifies the evaluated
|
||||
// request completely.
|
||||
SubmittedRequestDigest string `json:"submitted_request_digest,omitempty" yaml:"submitted_request_digest,omitempty"`
|
||||
}
|
||||
|
||||
// ApprovalContextKey is the context key carrying an approval-engine
|
||||
// approval-claim. It is excluded from ApprovalBindingDigest and from nothing
|
||||
// else.
|
||||
const ApprovalContextKey = "approval"
|
||||
|
||||
// requestDigestMaterial is the exact tuple hashed for §6.4.2 replay. Request
|
||||
// id, policy version, and caring_context are excluded: id is correlation, the
|
||||
// version is provenance, and caring_context is an input-claim digest.
|
||||
type requestDigestMaterial struct {
|
||||
Tenant string `json:"tenant,omitempty"`
|
||||
Subject SubjectRef `json:"subject"`
|
||||
Action string `json:"action"`
|
||||
Resource ResourceRef `json:"resource"`
|
||||
Context map[string]any `json:"context,omitempty"`
|
||||
Purpose string `json:"purpose,omitempty"`
|
||||
}
|
||||
|
||||
// NewDecisionBinding returns a stable structured binding for the exact request
|
||||
// an evaluator consumed.
|
||||
//
|
||||
// Prefer NewDecisionBindingFor, which also records the digest of the request as
|
||||
// submitted. This form leaves SubmittedRequestDigest empty, which reads as "the
|
||||
// evaluator did not record one" rather than "the two are equal".
|
||||
func NewDecisionBinding(request CheckRequest) *DecisionBinding {
|
||||
contextCopy := make(map[string]any, len(request.Context))
|
||||
for key, value := range request.Context {
|
||||
contextCopy[key] = value
|
||||
}
|
||||
binding := &DecisionBinding{
|
||||
Tenant: request.Tenant,
|
||||
Subject: request.Subject,
|
||||
Action: request.Action,
|
||||
Resource: request.Resource,
|
||||
Context: contextCopy,
|
||||
Purpose: request.Purpose,
|
||||
RequestDigest: RequestDigest(request),
|
||||
}
|
||||
if _, carries := request.Context[ApprovalContextKey]; carries {
|
||||
binding.ApprovalBindingDigest = ApprovalBindingDigest(request)
|
||||
}
|
||||
return binding
|
||||
}
|
||||
|
||||
// NewDecisionBindingFor records the binding of the evaluated request together
|
||||
// with the digest of the request as submitted.
|
||||
//
|
||||
// The two arguments are the same request before and after the evaluator
|
||||
// overlaid registry facts. Where a request named nothing the registry knows they
|
||||
// are equal, and the field is emitted anyway: a consumer that only sees it on
|
||||
// enriched decisions would build a check that passes by absence.
|
||||
func NewDecisionBindingFor(evaluated, submitted CheckRequest) *DecisionBinding {
|
||||
binding := NewDecisionBinding(evaluated)
|
||||
binding.SubmittedRequestDigest = RequestDigest(submitted)
|
||||
return binding
|
||||
}
|
||||
|
||||
// ApprovalBindingDigest is RequestDigest over the same material with the
|
||||
// approval claim removed from context.
|
||||
//
|
||||
// When a request carries no approval claim the two are identical, which is the
|
||||
// point: an approval issued against a claim-free Check records that Check's
|
||||
// request_digest, and the later claim-bearing request reproduces the same value
|
||||
// here. Comparing it to claim.binding.pdp_digest establishes correspondence by
|
||||
// identity rather than by translating between two action vocabularies
|
||||
// (GH-DEC-2026-008).
|
||||
func ApprovalBindingDigest(request CheckRequest) string {
|
||||
if _, carries := request.Context[ApprovalContextKey]; !carries {
|
||||
return RequestDigest(request)
|
||||
}
|
||||
stripped := make(map[string]any, len(request.Context))
|
||||
for key, value := range request.Context {
|
||||
if key == ApprovalContextKey {
|
||||
continue
|
||||
}
|
||||
stripped[key] = value
|
||||
}
|
||||
if len(stripped) == 0 {
|
||||
stripped = nil
|
||||
}
|
||||
return CanonicalDigest(requestDigestMaterial{
|
||||
Tenant: request.Tenant,
|
||||
Subject: request.Subject,
|
||||
Action: request.Action,
|
||||
Resource: request.Resource,
|
||||
Context: stripped,
|
||||
Purpose: request.Purpose,
|
||||
})
|
||||
}
|
||||
|
||||
// RequestDigest is the mechanical §6.4.2 replay test: SHA-256 over canonical
|
||||
// JSON of tenant, subject, action, resource, context, and purpose. Purpose is
|
||||
// omitted when empty, so enforcement digests are unchanged by its existence.
|
||||
func RequestDigest(request CheckRequest) string {
|
||||
return CanonicalDigest(requestDigestMaterial{
|
||||
Tenant: request.Tenant,
|
||||
Subject: request.Subject,
|
||||
Action: request.Action,
|
||||
Resource: request.Resource,
|
||||
Context: request.Context,
|
||||
Purpose: request.Purpose,
|
||||
})
|
||||
}
|
||||
|
||||
// CanonicalDigest returns "sha256:" plus the hex SHA-256 of canonical JSON.
|
||||
// encoding/json sorts map keys, so two equal Go values agree.
|
||||
func CanonicalDigest(value any) string {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
sum := sha256.Sum256(nil)
|
||||
return "sha256:" + hex.EncodeToString(sum[:])
|
||||
}
|
||||
sum := sha256.Sum256(data)
|
||||
return "sha256:" + hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// InputClaimDigests hashes the request-time claim classes the evaluator joined.
|
||||
func InputClaimDigests(request CheckRequest) map[string]string {
|
||||
digests := make(map[string]string)
|
||||
if len(request.Context) > 0 {
|
||||
digests["context"] = CanonicalDigest(request.Context)
|
||||
}
|
||||
if request.CaringContext != nil {
|
||||
digests["caring_context"] = CanonicalDigest(request.CaringContext)
|
||||
}
|
||||
if len(digests) == 0 {
|
||||
return nil
|
||||
}
|
||||
return digests
|
||||
}
|
||||
|
||||
// DecisionCompletion carries evaluator-side inputs used to finish an envelope.
|
||||
type DecisionCompletion struct {
|
||||
AllowTTL string
|
||||
Now time.Time
|
||||
}
|
||||
|
||||
// CompleteDecision stamps contract version, input-claim digests, decision time,
|
||||
// and an explicit allow lifetime. An allow with no stated end becomes a deny.
|
||||
func CompleteDecision(envelope *DecisionEnvelope, request CheckRequest, completion DecisionCompletion) {
|
||||
if envelope == nil {
|
||||
return
|
||||
}
|
||||
if envelope.ContractVersion == "" {
|
||||
envelope.ContractVersion = DecisionRecordContractV1
|
||||
}
|
||||
if envelope.Provenance.InputClaimDigests == nil {
|
||||
envelope.Provenance.InputClaimDigests = InputClaimDigests(request)
|
||||
}
|
||||
ApplyAllowLifetime(envelope, completion.AllowTTL, completion.Now)
|
||||
}
|
||||
|
||||
// ParseAllowTTL resolves a package-declared TTL. ok is false when the allow
|
||||
// would have no stated end. Invalid strings return an error so package
|
||||
// validation can reject them.
|
||||
func ParseAllowTTL(declared string) (time.Duration, error) {
|
||||
trimmed := strings.TrimSpace(declared)
|
||||
if trimmed == "" {
|
||||
return DefaultAllowTTL, nil
|
||||
}
|
||||
if strings.EqualFold(trimmed, "none") {
|
||||
return 0, nil
|
||||
}
|
||||
ttl, err := time.ParseDuration(trimmed)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("allow_ttl %q is not a Go duration: %w", declared, err)
|
||||
}
|
||||
if ttl <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return ttl, nil
|
||||
}
|
||||
|
||||
// ApplyAllowLifetime sets DecisionTime and, for allows, an explicit TTL. A
|
||||
// missing or zero TTL denies the allow rather than mint a standing grant.
|
||||
func ApplyAllowLifetime(envelope *DecisionEnvelope, declaredTTL string, now time.Time) {
|
||||
if envelope == nil {
|
||||
return
|
||||
}
|
||||
if now.IsZero() {
|
||||
now = time.Now().UTC()
|
||||
} else {
|
||||
now = now.UTC()
|
||||
}
|
||||
if envelope.Provenance.DecisionTime == "" {
|
||||
envelope.Provenance.DecisionTime = now.Format(time.RFC3339)
|
||||
}
|
||||
if envelope.Effect != DecisionEffectAllow {
|
||||
return
|
||||
}
|
||||
ttl, err := ParseAllowTTL(declaredTTL)
|
||||
if err != nil || ttl <= 0 {
|
||||
if envelope.Diagnostics == nil {
|
||||
envelope.Diagnostics = map[string]any{}
|
||||
}
|
||||
if envelope.Reason != "" {
|
||||
envelope.Diagnostics["unstated_allow_reason"] = envelope.Reason
|
||||
}
|
||||
envelope.Effect = DecisionEffectDeny
|
||||
envelope.Reason = ReasonAllowLifetimeUnstated
|
||||
envelope.MatchedRule = ReasonAllowLifetimeUnstated
|
||||
envelope.Lifetime = nil
|
||||
return
|
||||
}
|
||||
display := strings.TrimSpace(declaredTTL)
|
||||
if display == "" {
|
||||
display = "15m"
|
||||
}
|
||||
envelope.Lifetime = &DecisionLifetime{
|
||||
Kind: DecisionLifetimeTTL,
|
||||
TTL: display,
|
||||
NotBefore: now.Format(time.RFC3339),
|
||||
ExpiresAt: now.Add(ttl).Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// ActionAuthorizationStatus is the lifecycle state of a durable authorization.
|
||||
type ActionAuthorizationStatus string
|
||||
|
||||
const (
|
||||
ActionAuthorizationPending ActionAuthorizationStatus = "pending"
|
||||
ActionAuthorizationApproved ActionAuthorizationStatus = "approved"
|
||||
ActionAuthorizationDenied ActionAuthorizationStatus = "denied"
|
||||
ActionAuthorizationSuperseded ActionAuthorizationStatus = "superseded"
|
||||
ActionAuthorizationExpired ActionAuthorizationStatus = "expired"
|
||||
ActionAuthorizationRevoked ActionAuthorizationStatus = "revoked"
|
||||
)
|
||||
|
||||
// ActionAuthorization joins a durable approval lifecycle to one exact
|
||||
// flex-auth request and decision. Storage and approval collection remain the
|
||||
// responsibility of the organizational decision authority.
|
||||
type ActionAuthorization struct {
|
||||
SchemaVersion string `json:"schema_version" yaml:"schema_version"`
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Status ActionAuthorizationStatus `json:"status" yaml:"status"`
|
||||
SupersededBy string `json:"superseded_by,omitempty" yaml:"superseded_by,omitempty"`
|
||||
Request CheckRequest `json:"request" yaml:"request"`
|
||||
Validity ActionAuthorizationValidity `json:"validity" yaml:"validity"`
|
||||
Approvals ActionAuthorizationApprovals `json:"approvals" yaml:"approvals"`
|
||||
Decision DecisionEnvelope `json:"decision" yaml:"decision"`
|
||||
Provenance map[string]any `json:"provenance,omitempty" yaml:"provenance,omitempty"`
|
||||
}
|
||||
|
||||
// ActionAuthorizationValidity bounds execution of an approved action.
|
||||
type ActionAuthorizationValidity struct {
|
||||
NotBefore string `json:"not_before,omitempty" yaml:"not_before,omitempty"`
|
||||
ExpiresAt string `json:"expires_at" yaml:"expires_at"`
|
||||
}
|
||||
|
||||
// ActionAuthorizationApprovals declares the approval threshold and evidence.
|
||||
type ActionAuthorizationApprovals struct {
|
||||
RequiredCount int `json:"required_count" yaml:"required_count"`
|
||||
Entries []ActionAuthorizationApprovalEntry `json:"entries" yaml:"entries"`
|
||||
}
|
||||
|
||||
// ActionAuthorizationApprovalEntry is one authenticated approver's evidence.
|
||||
type ActionAuthorizationApprovalEntry struct {
|
||||
SubjectID string `json:"subject_id" yaml:"subject_id"`
|
||||
ApprovedAt string `json:"approved_at" yaml:"approved_at"`
|
||||
Assurance string `json:"assurance,omitempty" yaml:"assurance,omitempty"`
|
||||
EvidenceRef string `json:"evidence_ref,omitempty" yaml:"evidence_ref,omitempty"`
|
||||
}
|
||||
|
||||
// Obligation describes a follow-up behavior required by a decision.
|
||||
type Obligation struct {
|
||||
Type string `json:"type" yaml:"type"`
|
||||
Parameters map[string]any `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||
}
|
||||
|
||||
// DecisionProvenance captures evaluator and policy provenance.
|
||||
type DecisionProvenance struct {
|
||||
Evaluator string `json:"evaluator" yaml:"evaluator"`
|
||||
Mode string `json:"mode" yaml:"mode"`
|
||||
PolicyPackage string `json:"policy_package,omitempty" yaml:"policy_package,omitempty"`
|
||||
PolicyVersion string `json:"policy_version,omitempty" yaml:"policy_version,omitempty"`
|
||||
PolicyPackageDigest string `json:"policy_package_digest,omitempty" yaml:"policy_package_digest,omitempty"`
|
||||
RegistrySnapshotDigest string `json:"registry_snapshot_digest,omitempty" yaml:"registry_snapshot_digest,omitempty"`
|
||||
DirectoryETag string `json:"directory_etag,omitempty" yaml:"directory_etag,omitempty"`
|
||||
InputClaimDigests map[string]string `json:"input_claim_digests,omitempty" yaml:"input_claim_digests,omitempty"`
|
||||
DecisionTime string `json:"decision_time,omitempty" yaml:"decision_time,omitempty"`
|
||||
ApprovalObservation *ApprovalObservationProvenance `json:"approval_observation,omitempty" yaml:"approval_observation,omitempty"`
|
||||
Caller *CallerProvenance `json:"caller,omitempty" yaml:"caller,omitempty"`
|
||||
}
|
||||
|
||||
// ApprovalObservationProvenance binds fresh server-owned fact evidence without
|
||||
// rewriting the original carried claim/context or either PDP request digest.
|
||||
type ApprovalObservationProvenance struct {
|
||||
SourceID string `json:"source_id"`
|
||||
SourceRevision string `json:"source_revision"`
|
||||
ConfigurationDigest string `json:"configuration_digest"`
|
||||
ClaimDigest string `json:"claim_digest"`
|
||||
NativeBindingDigest string `json:"native_binding_digest"`
|
||||
ObservedAt string `json:"observed_at"`
|
||||
NotAfter string `json:"not_after"`
|
||||
ValidatedAt string `json:"validated_at"`
|
||||
}
|
||||
|
||||
// CallerProvenance records how the request was authenticated to flex-auth.
|
||||
// It is not decision material: the same request from a different authenticated
|
||||
// caller must decide identically. Mode is required. Under disabled the object
|
||||
// is {"mode":"disabled"} with no principal (FLEX-DEC-2026-009).
|
||||
type CallerProvenance struct {
|
||||
Mode string `json:"mode" yaml:"mode"`
|
||||
Principal string `json:"principal,omitempty" yaml:"principal,omitempty"`
|
||||
Audience string `json:"audience,omitempty" yaml:"audience,omitempty"`
|
||||
NotAfter string `json:"not_after,omitempty" yaml:"not_after,omitempty"`
|
||||
}
|
||||
|
||||
// CaringDecisionMetadata carries CARING descriptor and conformance details in
|
||||
// a decision envelope.
|
||||
type CaringDecisionMetadata struct {
|
||||
Profile string `json:"profile" yaml:"profile"`
|
||||
Descriptor *CaringAccessDescriptor `json:"descriptor,omitempty" yaml:"descriptor,omitempty"`
|
||||
RestrictionsEvaluated []Restriction `json:"restrictions_evaluated,omitempty" yaml:"restrictions_evaluated,omitempty"`
|
||||
ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"`
|
||||
DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"`
|
||||
ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"`
|
||||
ExposureEvent *CaringExposureEvent `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"`
|
||||
}
|
||||
|
||||
// AuditEvent is the local log shape for decisions and exposure events.
|
||||
type AuditEvent struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Type string `json:"type" yaml:"type"`
|
||||
DecisionID string `json:"decision_id,omitempty" yaml:"decision_id,omitempty"`
|
||||
Subject SubjectRef `json:"subject" yaml:"subject"`
|
||||
Resource ResourceRef `json:"resource,omitempty" yaml:"resource,omitempty"`
|
||||
Action string `json:"action,omitempty" yaml:"action,omitempty"`
|
||||
Effect DecisionEffect `json:"effect,omitempty" yaml:"effect,omitempty"`
|
||||
Timestamp string `json:"timestamp,omitempty" yaml:"timestamp,omitempty"`
|
||||
ExposureEvent *CaringExposureEvent `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
package api
|
||||
|
||||
// CaringProfileCaring040RC2 is the executable profile identifier for the
|
||||
// CARING 0.4.0-RC2 standard pinned by flex-auth.
|
||||
const CaringProfileCaring040RC2 = "caring-0.4.0-rc2"
|
||||
|
||||
// SubjectType is the CARING subject dimension.
|
||||
type SubjectType string
|
||||
|
||||
const (
|
||||
SubjectTypeHuman SubjectType = "Human"
|
||||
SubjectTypeGroup SubjectType = "Group"
|
||||
SubjectTypeOrganization SubjectType = "Organization"
|
||||
SubjectTypeService SubjectType = "Service"
|
||||
SubjectTypeAutomation SubjectType = "Automation"
|
||||
SubjectTypeAgent SubjectType = "Agent"
|
||||
SubjectTypeSystem SubjectType = "System"
|
||||
SubjectTypeDevice SubjectType = "Device"
|
||||
SubjectTypeProcess SubjectType = "Process"
|
||||
SubjectTypeAnonymous SubjectType = "Anonymous"
|
||||
SubjectTypeUnknown SubjectType = "Unknown"
|
||||
)
|
||||
|
||||
// OrganizationRelation is the CARING organization-relation dimension.
|
||||
type OrganizationRelation string
|
||||
|
||||
const (
|
||||
OrganizationRelationVendor OrganizationRelation = "Vendor"
|
||||
OrganizationRelationServiceProvider OrganizationRelation = "ServiceProvider"
|
||||
OrganizationRelationDistributor OrganizationRelation = "Distributor"
|
||||
OrganizationRelationConsultant OrganizationRelation = "Consultant"
|
||||
OrganizationRelationCustomer OrganizationRelation = "Customer"
|
||||
OrganizationRelationCommunity OrganizationRelation = "Community"
|
||||
OrganizationRelationAuthority OrganizationRelation = "Authority"
|
||||
OrganizationRelationUnknown OrganizationRelation = "Unknown"
|
||||
)
|
||||
|
||||
// CanonicalRole is the CARING lifecycle responsibility posture.
|
||||
type CanonicalRole string
|
||||
|
||||
const (
|
||||
CanonicalRoleCreator CanonicalRole = "Creator"
|
||||
CanonicalRoleBuilder CanonicalRole = "Builder"
|
||||
CanonicalRoleVerifier CanonicalRole = "Verifier"
|
||||
CanonicalRoleMaintainer CanonicalRole = "Maintainer"
|
||||
CanonicalRoleIntegrator CanonicalRole = "Integrator"
|
||||
CanonicalRoleOperator CanonicalRole = "Operator"
|
||||
CanonicalRoleManager CanonicalRole = "Manager"
|
||||
CanonicalRoleCoach CanonicalRole = "Coach"
|
||||
CanonicalRoleDoer CanonicalRole = "Doer"
|
||||
)
|
||||
|
||||
// ScopeLevel is the CARING scope ladder.
|
||||
type ScopeLevel string
|
||||
|
||||
const (
|
||||
ScopeLevelEcosystem ScopeLevel = "Ecosystem"
|
||||
ScopeLevelProduct ScopeLevel = "Product"
|
||||
ScopeLevelPlatform ScopeLevel = "Platform"
|
||||
ScopeLevelCluster ScopeLevel = "Cluster"
|
||||
ScopeLevelEnvironment ScopeLevel = "Environment"
|
||||
ScopeLevelTenant ScopeLevel = "Tenant"
|
||||
ScopeLevelNamespace ScopeLevel = "Namespace"
|
||||
ScopeLevelDomain ScopeLevel = "Domain"
|
||||
ScopeLevelWorkspace ScopeLevel = "Workspace"
|
||||
ScopeLevelProject ScopeLevel = "Project"
|
||||
ScopeLevelProcess ScopeLevel = "Process"
|
||||
ScopeLevelDataset ScopeLevel = "Dataset"
|
||||
ScopeLevelResource ScopeLevel = "Resource"
|
||||
ScopeLevelSubresource ScopeLevel = "Subresource"
|
||||
ScopeLevelRecord ScopeLevel = "Record"
|
||||
ScopeLevelField ScopeLevel = "Field"
|
||||
ScopeLevelAction ScopeLevel = "Action"
|
||||
)
|
||||
|
||||
// Plane is the CARING access-surface dimension.
|
||||
type Plane string
|
||||
|
||||
const (
|
||||
PlaneIntent Plane = "Intent"
|
||||
PlaneBuild Plane = "Build"
|
||||
PlaneRuntime Plane = "Runtime"
|
||||
PlaneExecution Plane = "Execution"
|
||||
PlaneConfiguration Plane = "Configuration"
|
||||
PlaneData Plane = "Data"
|
||||
PlaneIdentity Plane = "Identity"
|
||||
PlanePolicy Plane = "Policy"
|
||||
PlaneSecret Plane = "Secret"
|
||||
PlaneAudit Plane = "Audit"
|
||||
PlaneCommercial Plane = "Commercial"
|
||||
PlaneCommunity Plane = "Community"
|
||||
)
|
||||
|
||||
// Capability is a CARING capability verb.
|
||||
type Capability string
|
||||
|
||||
const (
|
||||
CapabilityView Capability = "View"
|
||||
CapabilityViewCollection Capability = "ViewCollection"
|
||||
CapabilityObserve Capability = "Observe"
|
||||
CapabilityCreate Capability = "Create"
|
||||
CapabilityEditOwn Capability = "EditOwn"
|
||||
CapabilityEditAssigned Capability = "EditAssigned"
|
||||
CapabilityEditAny Capability = "EditAny"
|
||||
CapabilityDeleteOwn Capability = "DeleteOwn"
|
||||
CapabilityDeleteAny Capability = "DeleteAny"
|
||||
CapabilityBulkDelete Capability = "BulkDelete"
|
||||
CapabilitySubmit Capability = "Submit"
|
||||
CapabilityComment Capability = "Comment"
|
||||
CapabilityReview Capability = "Review"
|
||||
CapabilityApprove Capability = "Approve"
|
||||
CapabilityReject Capability = "Reject"
|
||||
CapabilityPublish Capability = "Publish"
|
||||
CapabilityArchive Capability = "Archive"
|
||||
CapabilityRestore Capability = "Restore"
|
||||
CapabilityExecute Capability = "Execute"
|
||||
CapabilityConfigure Capability = "Configure"
|
||||
CapabilityOperate Capability = "Operate"
|
||||
CapabilityDeploy Capability = "Deploy"
|
||||
CapabilityIntegrate Capability = "Integrate"
|
||||
CapabilityGrant Capability = "Grant"
|
||||
CapabilityRevoke Capability = "Revoke"
|
||||
CapabilityDelegate Capability = "Delegate"
|
||||
CapabilityImpersonate Capability = "Impersonate"
|
||||
CapabilityExport Capability = "Export"
|
||||
CapabilityImport Capability = "Import"
|
||||
CapabilityReplicate Capability = "Replicate"
|
||||
CapabilityEncrypt Capability = "Encrypt"
|
||||
CapabilityDecrypt Capability = "Decrypt"
|
||||
CapabilityMask Capability = "Mask"
|
||||
CapabilityInspect Capability = "Inspect"
|
||||
CapabilityAudit Capability = "Audit"
|
||||
CapabilityOverride Capability = "Override"
|
||||
CapabilityEscalate Capability = "Escalate"
|
||||
CapabilityBind Capability = "Bind"
|
||||
CapabilityUse Capability = "Use"
|
||||
)
|
||||
|
||||
// ExposureMode describes how much information becomes visible or extractable.
|
||||
type ExposureMode string
|
||||
|
||||
const (
|
||||
ExposureModeNone ExposureMode = "None"
|
||||
ExposureModeMetadata ExposureMode = "Metadata"
|
||||
ExposureModeMasked ExposureMode = "Masked"
|
||||
ExposureModeAggregated ExposureMode = "Aggregated"
|
||||
ExposureModeSynthetic ExposureMode = "Synthetic"
|
||||
ExposureModePseudonymous ExposureMode = "Pseudonymous"
|
||||
ExposureModeEncrypted ExposureMode = "Encrypted"
|
||||
ExposureModePlaintext ExposureMode = "Plaintext"
|
||||
ExposureModeSecretMaterial ExposureMode = "SecretMaterial"
|
||||
ExposureModeExportable ExposureMode = "Exportable"
|
||||
ExposureModeCrossTenantAggregate ExposureMode = "CrossTenantAggregate"
|
||||
)
|
||||
|
||||
// Condition is a CARING runtime or governance condition.
|
||||
type Condition string
|
||||
|
||||
const (
|
||||
ConditionMFARequired Condition = "MFARequired"
|
||||
ConditionDeviceTrusted Condition = "DeviceTrusted"
|
||||
ConditionNetworkTrusted Condition = "NetworkTrusted"
|
||||
ConditionTicketRequired Condition = "TicketRequired"
|
||||
ConditionTenantConsentRequired Condition = "TenantConsentRequired"
|
||||
ConditionCustomerApprovalRequired Condition = "CustomerApprovalRequired"
|
||||
ConditionDualApprovalRequired Condition = "DualApprovalRequired"
|
||||
ConditionTimeLimited Condition = "TimeLimited"
|
||||
ConditionBusinessHoursOnly Condition = "BusinessHoursOnly"
|
||||
ConditionEmergencyOnly Condition = "EmergencyOnly"
|
||||
ConditionTrainingRequired Condition = "TrainingRequired"
|
||||
ConditionContractRequired Condition = "ContractRequired"
|
||||
ConditionNDARequired Condition = "NDARequired"
|
||||
ConditionPurposeBound Condition = "PurposeBound"
|
||||
ConditionCaseBound Condition = "CaseBound"
|
||||
ConditionEnvironmentBound Condition = "EnvironmentBound"
|
||||
ConditionNamespaceBound Condition = "NamespaceBound"
|
||||
ConditionPipelineBound Condition = "PipelineBound"
|
||||
ConditionChangeWindowBound Condition = "ChangeWindowBound"
|
||||
ConditionLogged Condition = "Logged"
|
||||
ConditionRecorded Condition = "Recorded"
|
||||
ConditionNotificationRequired Condition = "NotificationRequired"
|
||||
ConditionPostReviewRequired Condition = "PostReviewRequired"
|
||||
ConditionHumanReviewRequired Condition = "HumanReviewRequired"
|
||||
ConditionPolicyReviewRequired Condition = "PolicyReviewRequired"
|
||||
ConditionWorkloadIdentityRequired Condition = "WorkloadIdentityRequired"
|
||||
)
|
||||
|
||||
// LifecycleState describes why access exists now.
|
||||
type LifecycleState string
|
||||
|
||||
const (
|
||||
LifecycleStateDesign LifecycleState = "Design"
|
||||
LifecycleStateBuild LifecycleState = "Build"
|
||||
LifecycleStateTest LifecycleState = "Test"
|
||||
LifecycleStateReview LifecycleState = "Review"
|
||||
LifecycleStateRelease LifecycleState = "Release"
|
||||
LifecycleStateOnboard LifecycleState = "Onboard"
|
||||
LifecycleStateIntegrate LifecycleState = "Integrate"
|
||||
LifecycleStateMigrate LifecycleState = "Migrate"
|
||||
LifecycleStateOperate LifecycleState = "Operate"
|
||||
LifecycleStateSupport LifecycleState = "Support"
|
||||
LifecycleStateImprove LifecycleState = "Improve"
|
||||
LifecycleStateDeprecate LifecycleState = "Deprecate"
|
||||
LifecycleStateArchive LifecycleState = "Archive"
|
||||
LifecycleStateIncident LifecycleState = "Incident"
|
||||
LifecycleStateLegal LifecycleState = "Legal"
|
||||
LifecycleStateTerminate LifecycleState = "Terminate"
|
||||
)
|
||||
|
||||
// Restriction is an overriding CARING deny or limiting policy effect.
|
||||
type Restriction string
|
||||
|
||||
const (
|
||||
RestrictionNoAccess Restriction = "NoAccess"
|
||||
RestrictionSuspended Restriction = "Suspended"
|
||||
RestrictionTerminated Restriction = "Terminated"
|
||||
RestrictionQuarantined Restriction = "Quarantined"
|
||||
RestrictionScopeExcluded Restriction = "ScopeExcluded"
|
||||
RestrictionDataClassRestricted Restriction = "DataClassRestricted"
|
||||
RestrictionLegalHold Restriction = "LegalHold"
|
||||
RestrictionExportBlocked Restriction = "ExportBlocked"
|
||||
RestrictionImpersonationBlocked Restriction = "ImpersonationBlocked"
|
||||
RestrictionCrossTenantBlocked Restriction = "CrossTenantBlocked"
|
||||
RestrictionSecretAccessBlocked Restriction = "SecretAccessBlocked"
|
||||
RestrictionPolicyFrozen Restriction = "PolicyFrozen"
|
||||
RestrictionEmergencyLocked Restriction = "EmergencyLocked"
|
||||
RestrictionRiskDenied Restriction = "RiskDenied"
|
||||
RestrictionExecutionBlocked Restriction = "ExecutionBlocked"
|
||||
RestrictionWorkloadCreationBlocked Restriction = "WorkloadCreationBlocked"
|
||||
RestrictionPrivilegeEscalationBlocked Restriction = "PrivilegeEscalationBlocked"
|
||||
)
|
||||
|
||||
// ExposureEventType is a CARING exceptional or irregular access class.
|
||||
type ExposureEventType string
|
||||
|
||||
const (
|
||||
ExposureEventSupport ExposureEventType = "X-Support"
|
||||
ExposureEventBreakGlass ExposureEventType = "X-BreakGlass"
|
||||
ExposureEventSecurityTest ExposureEventType = "X-SecurityTest"
|
||||
ExposureEventIncident ExposureEventType = "X-Incident"
|
||||
ExposureEventLegalDemand ExposureEventType = "X-LegalDemand"
|
||||
ExposureEventComplianceAudit ExposureEventType = "X-ComplianceAudit"
|
||||
ExposureEventMigration ExposureEventType = "X-Migration"
|
||||
ExposureEventRecovery ExposureEventType = "X-Recovery"
|
||||
ExposureEventAdversarial ExposureEventType = "X-Adversarial"
|
||||
ExposureEventMisconfiguration ExposureEventType = "X-Misconfiguration"
|
||||
ExposureEventInducedAccess ExposureEventType = "X-InducedAccess"
|
||||
ExposureEventPrivilegeEscalation ExposureEventType = "X-PrivilegeEscalation"
|
||||
)
|
||||
|
||||
// AccessPath describes how access is exercised.
|
||||
type AccessPath string
|
||||
|
||||
const (
|
||||
AccessPathDirect AccessPath = "direct"
|
||||
AccessPathDelegated AccessPath = "delegated"
|
||||
AccessPathMediated AccessPath = "mediated"
|
||||
AccessPathInduced AccessPath = "induced"
|
||||
)
|
||||
|
||||
// CaringScope identifies where a CARING descriptor applies.
|
||||
type CaringScope struct {
|
||||
Level ScopeLevel `json:"level" yaml:"level"`
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Parent string `json:"parent,omitempty" yaml:"parent,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"`
|
||||
Resource string `json:"resource,omitempty" yaml:"resource,omitempty"`
|
||||
Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
// CaringAccessDescriptor is the executable flex-auth representation of a
|
||||
// CARING access assignment.
|
||||
type CaringAccessDescriptor struct {
|
||||
ID string `json:"id,omitempty" yaml:"id,omitempty"`
|
||||
Profile string `json:"profile" yaml:"profile"`
|
||||
SubjectType SubjectType `json:"subject_type" yaml:"subject_type"`
|
||||
OrganizationRelation OrganizationRelation `json:"organization_relation" yaml:"organization_relation"`
|
||||
CanonicalRole CanonicalRole `json:"canonical_role" yaml:"canonical_role"`
|
||||
Scope CaringScope `json:"scope" yaml:"scope"`
|
||||
Planes []Plane `json:"planes" yaml:"planes"`
|
||||
Capabilities []Capability `json:"capabilities" yaml:"capabilities"`
|
||||
ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"`
|
||||
Conditions []Condition `json:"conditions,omitempty" yaml:"conditions,omitempty"`
|
||||
LifecycleState LifecycleState `json:"lifecycle_state,omitempty" yaml:"lifecycle_state,omitempty"`
|
||||
Restrictions []Restriction `json:"restrictions,omitempty" yaml:"restrictions,omitempty"`
|
||||
ExposureEvent ExposureEventType `json:"exposure_event,omitempty" yaml:"exposure_event,omitempty"`
|
||||
DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"`
|
||||
AccessPath AccessPath `json:"access_path,omitempty" yaml:"access_path,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// CaringDerivedCapability records effective authority created by another grant.
|
||||
type CaringDerivedCapability struct {
|
||||
Capability Capability `json:"capability" yaml:"capability"`
|
||||
Reason string `json:"reason" yaml:"reason"`
|
||||
Source string `json:"source,omitempty" yaml:"source,omitempty"`
|
||||
Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"`
|
||||
ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"`
|
||||
}
|
||||
|
||||
// CaringConformanceFinding is a diagnostic emitted by descriptive or
|
||||
// prescriptive CARING validation.
|
||||
type CaringConformanceFinding struct {
|
||||
Code string `json:"code" yaml:"code"`
|
||||
Severity string `json:"severity" yaml:"severity"`
|
||||
Message string `json:"message" yaml:"message"`
|
||||
Fields []string `json:"fields,omitempty" yaml:"fields,omitempty"`
|
||||
Descriptor string `json:"descriptor,omitempty" yaml:"descriptor,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// CaringExposureEvent records exceptional or irregular information exposure.
|
||||
type CaringExposureEvent struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Type ExposureEventType `json:"type" yaml:"type"`
|
||||
Actor string `json:"actor" yaml:"actor"`
|
||||
Subject string `json:"subject" yaml:"subject"`
|
||||
Descriptor *CaringAccessDescriptor `json:"descriptor,omitempty" yaml:"descriptor,omitempty"`
|
||||
Scope *CaringScope `json:"scope,omitempty" yaml:"scope,omitempty"`
|
||||
Planes []Plane `json:"planes,omitempty" yaml:"planes,omitempty"`
|
||||
CapabilitiesUsed []Capability `json:"capabilities_used,omitempty" yaml:"capabilities_used,omitempty"`
|
||||
DerivedCapabilities []CaringDerivedCapability `json:"derived_capabilities,omitempty" yaml:"derived_capabilities,omitempty"`
|
||||
ExposureModes []ExposureMode `json:"exposure_modes,omitempty" yaml:"exposure_modes,omitempty"`
|
||||
Reason string `json:"reason" yaml:"reason"`
|
||||
AuthoritySource string `json:"authority_source,omitempty" yaml:"authority_source,omitempty"`
|
||||
Approval string `json:"approval,omitempty" yaml:"approval,omitempty"`
|
||||
StartTime string `json:"start_time,omitempty" yaml:"start_time,omitempty"`
|
||||
EndTime string `json:"end_time,omitempty" yaml:"end_time,omitempty"`
|
||||
ResourcesAccessed []string `json:"resources_accessed,omitempty" yaml:"resources_accessed,omitempty"`
|
||||
Evidence []string `json:"evidence,omitempty" yaml:"evidence,omitempty"`
|
||||
NotificationStatus string `json:"notification_status,omitempty" yaml:"notification_status,omitempty"`
|
||||
PostReview string `json:"post_review,omitempty" yaml:"post_review,omitempty"`
|
||||
ConformanceFindings []CaringConformanceFinding `json:"conformance_findings,omitempty" yaml:"conformance_findings,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package api
|
||||
|
||||
// ResourceManifest is the shape a protected system publishes to register
|
||||
// its resources with flex-auth. The shape is pinned against the
|
||||
// Markitect-side emitter in markitect-tool (MKTT-WP-0014); see
|
||||
// schemas/resource_manifest.schema.json for the JSON Schema and
|
||||
// examples/markitect/resource_manifest.yaml for the canonical example.
|
||||
type ResourceManifest struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
System string `json:"system" yaml:"system"`
|
||||
Resources []Resource `json:"resources" yaml:"resources"`
|
||||
Actions []string `json:"actions,omitempty" yaml:"actions,omitempty"`
|
||||
CaringProfile string `json:"caring_profile,omitempty" yaml:"caring_profile,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Resource is one entry in a ResourceManifest.
|
||||
type Resource struct {
|
||||
ID string `json:"id" yaml:"id"`
|
||||
Type string `json:"type" yaml:"type"`
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
Parent string `json:"parent,omitempty" yaml:"parent,omitempty"`
|
||||
Labels []string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
TrustZone string `json:"trust_zone,omitempty" yaml:"trust_zone,omitempty"`
|
||||
Owner string `json:"owner,omitempty" yaml:"owner,omitempty"`
|
||||
Caring *CaringAccessDescriptor `json:"caring,omitempty" yaml:"caring,omitempty"`
|
||||
Attributes map[string]any `json:"attributes,omitempty" yaml:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
// FlexAuthContractV0 is the metadata.flex_auth_contract value that
|
||||
// signals the v0 resource-registration contract.
|
||||
const FlexAuthContractV0 = "resource-registration-v0"
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
// Package decisionpep authenticates the producer's decision record for one
|
||||
// current request. It never evaluates policy, caches a verdict, or discovers trust.
|
||||
package decisionpep
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/netkingdom/access-engine/internal/sign"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
)
|
||||
|
||||
var ErrRefused = errors.New("authenticated decision refused")
|
||||
|
||||
const nonceKey = "decisionpep_request_nonce"
|
||||
|
||||
const JSONProfile = "lossless-json-v1"
|
||||
const provenanceMode = "standalone/lossless-json-v1"
|
||||
const maxBytes = 2 << 20
|
||||
|
||||
// Admission is public, independently admitted owner configuration. TokenFile is
|
||||
// only a path to a private rotating caller credential, never a literal token.
|
||||
type Admission struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
CAFile string `json:"ca_file"`
|
||||
TokenFile string `json:"token_file"`
|
||||
Keys map[string]string `json:"keys"`
|
||||
Evaluator string `json:"evaluator"`
|
||||
PolicyPackage string `json:"policy_package"`
|
||||
PolicyVersion string `json:"policy_version"`
|
||||
PolicyDigest string `json:"policy_digest"`
|
||||
RegistryDigest string `json:"registry_digest"`
|
||||
CallerPrincipal string `json:"caller_principal"`
|
||||
CallerAudience string `json:"caller_audience"`
|
||||
MaxTTL string `json:"max_ttl"`
|
||||
MaxClockSkew string `json:"max_clock_skew"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
admission Admission
|
||||
keys map[string]ed25519.PublicKey
|
||||
http *http.Client
|
||||
maxTTL, skew time.Duration
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// Load reads a current independently admitted file. No trust is bootstrapped
|
||||
// from a PDP response, token contents, discovery endpoint, or environment value.
|
||||
func Load(path string) (*Client, error) {
|
||||
raw, err := readFile(path, false)
|
||||
if err != nil {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
var a Admission
|
||||
d := json.NewDecoder(bytes.NewReader(raw))
|
||||
d.DisallowUnknownFields()
|
||||
if d.Decode(&a) != nil {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
if _, err = d.Token(); err != io.EOF {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
return New(a)
|
||||
}
|
||||
|
||||
func New(a Admission) (*Client, error) {
|
||||
u, err := url.Parse(a.Endpoint)
|
||||
if err != nil || u.Scheme != "https" || u.Host == "" || u.User != nil || u.Path != "/v1/check" || u.RawQuery != "" || u.Fragment != "" {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
if !filepath.IsAbs(a.CAFile) || !filepath.IsAbs(a.TokenFile) || a.Evaluator == "" || a.PolicyPackage == "" || a.PolicyVersion == "" || a.CallerPrincipal == "" || a.CallerAudience == "" {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
if !validDigest(a.PolicyDigest) || !validDigest(a.RegistryDigest) || len(a.Keys) == 0 {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
ttl, err := time.ParseDuration(a.MaxTTL)
|
||||
if err != nil || ttl <= 0 {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
skew, err := time.ParseDuration(a.MaxClockSkew)
|
||||
if err != nil || skew < 0 || skew > ttl {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
ca, err := readFile(a.CAFile, false)
|
||||
if err != nil {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
if !roots.AppendCertsFromPEM(ca) {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
keys := map[string]ed25519.PublicKey{}
|
||||
for kid, value := range a.Keys {
|
||||
b, e := base64.RawURLEncoding.Strict().DecodeString(value)
|
||||
if e != nil || kid == "" || len(b) != ed25519.PublicKeySize {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
keys[kid] = append(ed25519.PublicKey(nil), b...)
|
||||
}
|
||||
transport := &http.Transport{Proxy: nil, TLSClientConfig: &tls.Config{RootCAs: roots, MinVersion: tls.VersionTLS12}, DisableKeepAlives: true}
|
||||
client := &http.Client{Transport: transport, Timeout: 10 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return ErrRefused }}
|
||||
return &Client{admission: a, keys: keys, http: client, maxTTL: ttl, skew: skew, now: time.Now}, nil
|
||||
}
|
||||
|
||||
func validDigest(value string) bool {
|
||||
if !strings.HasPrefix(value, "sha256:") || len(value) != 71 {
|
||||
return false
|
||||
}
|
||||
_, e := hex.DecodeString(value[7:])
|
||||
return e == nil && strings.ToLower(value) == value
|
||||
}
|
||||
|
||||
func readFile(path string, private bool) ([]byte, error) {
|
||||
if !filepath.IsAbs(path) {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
f, e := os.Open(path)
|
||||
if e != nil {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
defer f.Close()
|
||||
info, e := f.Stat()
|
||||
if e != nil || !info.Mode().IsRegular() || info.Mode().Perm()&0022 != 0 {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
if private && info.Mode().Perm()&0007 != 0 {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
b, e := io.ReadAll(io.LimitReader(f, maxBytes+1))
|
||||
if e != nil || len(b) > maxBytes {
|
||||
return nil, ErrRefused
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Check sends exactly one request with a cryptographically fresh nonce. The
|
||||
// returned envelope is the producer's authenticated typed interpretation.
|
||||
// Callers must execute only the request's exact operation and honor their own
|
||||
// execution boundary; a login decision is never a later resource decision.
|
||||
func (c *Client) Check(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) {
|
||||
refuse := func() (api.DecisionEnvelope, error) { return api.DecisionEnvelope{}, ErrRefused }
|
||||
if c == nil || request.Purpose != "" || request.Tenant == "" || request.Subject.ID == "" || request.Subject.Type == "" || request.Resource.ID == "" || request.Resource.Type == "" || request.Resource.System == "" || request.Action == "" {
|
||||
return refuse()
|
||||
}
|
||||
if request.Subject.Tenant != request.Tenant || request.Resource.Tenant != request.Tenant {
|
||||
return refuse()
|
||||
}
|
||||
if request.PolicyVersion != "" && request.PolicyVersion != c.admission.PolicyVersion {
|
||||
return refuse()
|
||||
}
|
||||
// Use the producer's typed representation and preserve numeric lexemes. A
|
||||
// producer that rounds before hashing fails this join rather than weakening it.
|
||||
raw, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return refuse()
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||||
decoder.UseNumber()
|
||||
if decoder.Decode(&request) != nil {
|
||||
return refuse()
|
||||
}
|
||||
if request.Context == nil {
|
||||
request.Context = map[string]any{}
|
||||
}
|
||||
if _, exists := request.Context[nonceKey]; exists {
|
||||
return refuse()
|
||||
}
|
||||
nonce := make([]byte, 32)
|
||||
if _, err = rand.Read(nonce); err != nil {
|
||||
return refuse()
|
||||
}
|
||||
request.Context[nonceKey] = hex.EncodeToString(nonce)
|
||||
request.Context["decisionpep_json_profile"] = JSONProfile
|
||||
request.ID = hex.EncodeToString(nonce)
|
||||
request.PolicyVersion = c.admission.PolicyVersion
|
||||
raw, err = json.Marshal(request)
|
||||
if err != nil || len(raw) > maxBytes {
|
||||
return refuse()
|
||||
}
|
||||
token, err := readFile(c.admission.TokenFile, true)
|
||||
if err != nil {
|
||||
return refuse()
|
||||
}
|
||||
value := strings.TrimSpace(string(token))
|
||||
if value == "" || strings.ContainsAny(value, " \t\r\n") {
|
||||
return refuse()
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.admission.Endpoint, bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return refuse()
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Access-Engine-JSON-Profile", JSONProfile)
|
||||
req.Header.Set("Authorization", "Bearer "+value)
|
||||
started := c.now().UTC()
|
||||
response, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return refuse()
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return refuse()
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(response.Body, maxBytes+1))
|
||||
if err != nil || len(body) > maxBytes {
|
||||
return refuse()
|
||||
}
|
||||
envelope, err := sign.DecodeEnvelope(body)
|
||||
if err != nil || sign.Verify(envelope, c.keys) != nil {
|
||||
return refuse()
|
||||
}
|
||||
if c.validate(envelope, request, started, c.now().UTC()) != nil {
|
||||
return refuse()
|
||||
}
|
||||
return envelope, nil
|
||||
}
|
||||
|
||||
func (c *Client) validate(e api.DecisionEnvelope, r api.CheckRequest, started, now time.Time) error {
|
||||
p := e.Provenance
|
||||
b := e.Binding
|
||||
l := e.Lifetime
|
||||
if e.ID == "" || e.ContractVersion != api.DecisionRecordContractV1 || e.Effect != api.DecisionEffectAllow || e.RequestID != r.ID || len(e.Obligations) != 0 || b == nil || l == nil {
|
||||
return ErrRefused
|
||||
}
|
||||
if p.Evaluator != c.admission.Evaluator || p.Mode != provenanceMode || p.PolicyPackage != c.admission.PolicyPackage || p.PolicyVersion != c.admission.PolicyVersion || p.PolicyPackageDigest != c.admission.PolicyDigest || p.RegistrySnapshotDigest != c.admission.RegistryDigest || e.MatchedPolicyVersion != p.PolicyVersion {
|
||||
return ErrRefused
|
||||
}
|
||||
if b.Purpose != "" || b.SubmittedRequestDigest != api.RequestDigest(r) || b.Tenant != r.Tenant || b.Action != r.Action || api.CanonicalDigest(b.Context) != api.CanonicalDigest(r.Context) {
|
||||
return ErrRefused
|
||||
}
|
||||
if !sameSubject(b.Subject, r.Subject) || !sameResource(b.Resource, r.Resource) || !sameSubject(e.Subject, b.Subject) || !sameResource(e.Resource, b.Resource) {
|
||||
return ErrRefused
|
||||
}
|
||||
evaluated := api.CheckRequest{Tenant: b.Tenant, Subject: b.Subject, Action: b.Action, Resource: b.Resource, Context: b.Context, Purpose: b.Purpose}
|
||||
if b.RequestDigest != api.RequestDigest(evaluated) {
|
||||
return ErrRefused
|
||||
}
|
||||
for k, v := range api.InputClaimDigests(r) {
|
||||
if p.InputClaimDigests[k] != v {
|
||||
return ErrRefused
|
||||
}
|
||||
}
|
||||
caller := p.Caller
|
||||
if caller == nil || caller.Mode != "enforce" || caller.Principal != c.admission.CallerPrincipal || caller.Audience != c.admission.CallerAudience {
|
||||
return ErrRefused
|
||||
}
|
||||
callerEnd, err := time.Parse(time.RFC3339Nano, caller.NotAfter)
|
||||
if err != nil || !now.Before(callerEnd) {
|
||||
return ErrRefused
|
||||
}
|
||||
decision, err := time.Parse(time.RFC3339Nano, p.DecisionTime)
|
||||
if err != nil || decision.Before(started.Add(-c.skew)) || decision.After(now.Add(c.skew)) {
|
||||
return ErrRefused
|
||||
}
|
||||
begin, err := time.Parse(time.RFC3339Nano, l.NotBefore)
|
||||
if err != nil || begin.After(now) {
|
||||
return ErrRefused
|
||||
}
|
||||
end, err := time.Parse(time.RFC3339Nano, l.ExpiresAt)
|
||||
if err != nil || !now.Before(end) || !end.After(begin) || end.Sub(begin) > c.maxTTL || end.After(callerEnd) {
|
||||
return ErrRefused
|
||||
}
|
||||
ttl, err := time.ParseDuration(l.TTL)
|
||||
if err != nil || l.Kind != api.DecisionLifetimeTTL || ttl <= 0 || ttl > c.maxTTL {
|
||||
return ErrRefused
|
||||
}
|
||||
// Producer CompleteDecision uses its exact decision instant for both bounds.
|
||||
// RFC3339 output can truncate subsecond precision, so do not invent lost time.
|
||||
if !begin.Equal(decision) || end.Sub(begin) != ttl {
|
||||
return ErrRefused
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sameSubject(a, b api.SubjectRef) bool {
|
||||
return a.ID == b.ID && a.Type == b.Type && a.Tenant == b.Tenant
|
||||
}
|
||||
func sameResource(a, b api.ResourceRef) bool {
|
||||
return a.ID == b.ID && a.Type == b.Type && a.System == b.System && a.Tenant == b.Tenant
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
package decisionpep
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/netkingdom/access-engine/internal/sign"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
)
|
||||
|
||||
type transportFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (f transportFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
||||
func testClient(t *testing.T) (*Client, ed25519.PrivateKey, time.Time) {
|
||||
t.Helper()
|
||||
pub, key, e := ed25519.GenerateKey(rand.Reader)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
now := time.Date(2026, 10, 18, 1, 2, 3, 0, time.UTC)
|
||||
dir := t.TempDir()
|
||||
cert, e := x509.CreateCertificate(rand.Reader, &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "test-only"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign}, &x509.Certificate{SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "test-only"}, NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign}, pub, key)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
ca := filepath.Join(dir, "ca.pem")
|
||||
token := filepath.Join(dir, "token")
|
||||
if e = os.WriteFile(ca, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}), 0600); e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
if e = os.WriteFile(token, []byte("synthetic-caller-token"), 0600); e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
a := Admission{Endpoint: "https://pdp.invalid/v1/check", CAFile: ca, TokenFile: token, Keys: map[string]string{"test-key": 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:test:forgejo", CallerAudience: "access-engine", MaxTTL: "30s", MaxClockSkew: "1s"}
|
||||
c, e := New(a)
|
||||
if e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
c.now = func() time.Time { return now }
|
||||
return c, key, now
|
||||
}
|
||||
func testRequest() api.CheckRequest {
|
||||
return api.CheckRequest{Tenant: "tenant:test", Subject: api.SubjectRef{ID: "subject:test", Type: api.SubjectType("Human"), Tenant: "tenant:test", Attributes: map[string]any{"groups": []string{"engineers"}}}, Action: "application.access", Resource: api.ResourceRef{ID: "application:forgejo", Type: "application", System: "forgejo", Tenant: "tenant:test"}, Context: map[string]any{"big": json.Number("9007199254740993"), "fraction": json.Number("0.123456789123456789")}}
|
||||
}
|
||||
func envelope(c *Client, r api.CheckRequest, key ed25519.PrivateKey, now time.Time) api.DecisionEnvelope {
|
||||
e := api.DecisionEnvelope{ID: "test-decision", RequestID: r.ID, Effect: api.DecisionEffectAllow, MatchedPolicyVersion: c.admission.PolicyVersion, Subject: r.Subject, Resource: r.Resource, Binding: api.NewDecisionBindingFor(r, r), Provenance: api.DecisionProvenance{Evaluator: c.admission.Evaluator, Mode: provenanceMode, PolicyPackage: c.admission.PolicyPackage, PolicyVersion: c.admission.PolicyVersion, PolicyPackageDigest: c.admission.PolicyDigest, RegistrySnapshotDigest: c.admission.RegistryDigest, Caller: &api.CallerProvenance{Mode: "enforce", Principal: c.admission.CallerPrincipal, Audience: c.admission.CallerAudience, NotAfter: now.Add(time.Minute).Format(time.RFC3339Nano)}}}
|
||||
api.CompleteDecision(&e, r, api.DecisionCompletion{AllowTTL: "10s", Now: now})
|
||||
if sign.Sign(&e, key, "test-key") != nil {
|
||||
panic("test sign")
|
||||
}
|
||||
return e
|
||||
}
|
||||
func respond(t *testing.T, c *Client, key ed25519.PrivateKey, now time.Time, mutate func(*api.DecisionEnvelope), resign bool) {
|
||||
t.Helper()
|
||||
c.http = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if req.Header.Get("Access-Engine-JSON-Profile") != JSONProfile {
|
||||
t.Fatal("missing lossless profile")
|
||||
}
|
||||
if req.Header.Get("Authorization") != "Bearer synthetic-caller-token" {
|
||||
t.Fatal("wrong synthetic caller token")
|
||||
}
|
||||
var r api.CheckRequest
|
||||
d := json.NewDecoder(req.Body)
|
||||
d.UseNumber()
|
||||
if d.Decode(&r) != nil {
|
||||
t.Fatal("request decode")
|
||||
}
|
||||
if r.Context["big"] != json.Number("9007199254740993") || r.Context["fraction"] != json.Number("0.123456789123456789") {
|
||||
t.Fatal("request numeric precision lost")
|
||||
}
|
||||
if len(r.Context[nonceKey].(string)) != 64 || r.ID != r.Context[nonceKey] {
|
||||
t.Fatal("nonce")
|
||||
}
|
||||
e := envelope(c, r, key, now)
|
||||
if mutate != nil {
|
||||
mutate(&e)
|
||||
}
|
||||
if resign {
|
||||
if sign.Sign(&e, key, "test-key") != nil {
|
||||
t.Fatal("sign")
|
||||
}
|
||||
}
|
||||
b, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(string(b))), Header: http.Header{}}, nil
|
||||
})}
|
||||
}
|
||||
func TestAuthenticDecisionProducerCanonicalPrecision(t *testing.T) {
|
||||
c, key, now := testClient(t)
|
||||
respond(t, c, key, now, nil, false)
|
||||
e, err := c.Check(context.Background(), testRequest())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e.Binding.Context["big"] != json.Number("9007199254740993") || e.Binding.Context["fraction"] != json.Number("0.123456789123456789") {
|
||||
t.Fatal("signed typed precision lost")
|
||||
}
|
||||
if sign.Verify(e, c.keys) != nil {
|
||||
t.Fatal("returned envelope not authentic producer bytes")
|
||||
}
|
||||
}
|
||||
func TestDecisionAdverseBindings(t *testing.T) {
|
||||
cases := map[string]func(*api.DecisionEnvelope){
|
||||
"unsigned": func(e *api.DecisionEnvelope) { e.Signature = api.UnsignedEnvelopeSignature() },
|
||||
"effect": func(e *api.DecisionEnvelope) { e.Effect = api.DecisionEffectDeny },
|
||||
"evaluator": func(e *api.DecisionEnvelope) { e.Provenance.Evaluator = "impostor" },
|
||||
"policy": func(e *api.DecisionEnvelope) { e.Provenance.PolicyPackage = "other" },
|
||||
"version": func(e *api.DecisionEnvelope) { e.Provenance.PolicyVersion = "v2" },
|
||||
"policy-digest": func(e *api.DecisionEnvelope) { e.Provenance.PolicyPackageDigest = "sha256:" + strings.Repeat("3", 64) },
|
||||
"registry": func(e *api.DecisionEnvelope) {
|
||||
e.Provenance.RegistrySnapshotDigest = "sha256:" + strings.Repeat("4", 64)
|
||||
},
|
||||
"tenant": func(e *api.DecisionEnvelope) { e.Binding.Tenant = "other" },
|
||||
"actor": func(e *api.DecisionEnvelope) { e.Binding.Subject.ID = "other" },
|
||||
"action": func(e *api.DecisionEnvelope) { e.Binding.Action = "write" },
|
||||
"resource": func(e *api.DecisionEnvelope) { e.Binding.Resource.ID = "other" },
|
||||
"purpose": func(e *api.DecisionEnvelope) { e.Binding.Purpose = "readback" },
|
||||
"submitted": func(e *api.DecisionEnvelope) { e.Binding.SubmittedRequestDigest = "wrong" },
|
||||
"context": func(e *api.DecisionEnvelope) { e.Binding.Context["fraction"] = json.Number("0.123456789123456788") },
|
||||
"legacy-numeric-profile": func(e *api.DecisionEnvelope) { e.Provenance.Mode = "standalone" },
|
||||
"caller-warn": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Mode = "warn" },
|
||||
"caller-principal": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Principal = "other" },
|
||||
"caller-audience": func(e *api.DecisionEnvelope) { e.Provenance.Caller.Audience = "other" },
|
||||
"caller-expired": func(e *api.DecisionEnvelope) { e.Provenance.Caller.NotAfter = "2020-01-01T00:00:00Z" },
|
||||
"stale-decision": func(e *api.DecisionEnvelope) { e.Provenance.DecisionTime = "2020-01-01T00:00:00Z" },
|
||||
"expired": func(e *api.DecisionEnvelope) { e.Lifetime.ExpiresAt = "2020-01-01T00:00:00Z" },
|
||||
"future-begin": func(e *api.DecisionEnvelope) { e.Lifetime.NotBefore = "2030-01-01T00:00:00Z" },
|
||||
"subnanosecond-expiry": func(e *api.DecisionEnvelope) { e.Lifetime.ExpiresAt = "2026-10-18T01:02:02.999999999Z" },
|
||||
"missing-TTL": func(e *api.DecisionEnvelope) { e.Lifetime = nil },
|
||||
"excess-TTL": func(e *api.DecisionEnvelope) { e.Lifetime.TTL = "1h" },
|
||||
"obligation": func(e *api.DecisionEnvelope) { e.Obligations = []api.Obligation{{Type: "unimplemented"}} },
|
||||
}
|
||||
for name, mutation := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c, key, now := testClient(t)
|
||||
respond(t, c, key, now, mutation, name != "unsigned")
|
||||
if _, err := c.Check(context.Background(), testRequest()); err == nil {
|
||||
t.Fatal("accepted wrong signed binding")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestSignatureForgeryAndReplay(t *testing.T) {
|
||||
t.Run("forged-payload", func(t *testing.T) {
|
||||
c, key, now := testClient(t)
|
||||
respond(t, c, key, now, func(e *api.DecisionEnvelope) { e.Reason = "changed after signature" }, false)
|
||||
if _, e := c.Check(context.Background(), testRequest()); e == nil {
|
||||
t.Fatal("forgery accepted")
|
||||
}
|
||||
})
|
||||
t.Run("wrong-key", func(t *testing.T) {
|
||||
c, _, now := testClient(t)
|
||||
_, key, _ := ed25519.GenerateKey(rand.Reader)
|
||||
respond(t, c, key, now, nil, false)
|
||||
if _, e := c.Check(context.Background(), testRequest()); e == nil {
|
||||
t.Fatal("wrong key accepted")
|
||||
}
|
||||
})
|
||||
t.Run("replay", func(t *testing.T) {
|
||||
c, key, now := testClient(t)
|
||||
var saved []byte
|
||||
c.http = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if saved == nil {
|
||||
var r api.CheckRequest
|
||||
d := json.NewDecoder(req.Body)
|
||||
d.UseNumber()
|
||||
if d.Decode(&r) != nil {
|
||||
t.Fatal("decode")
|
||||
}
|
||||
e := envelope(c, r, key, now)
|
||||
saved, _ = json.Marshal(e)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(string(saved)))}, nil
|
||||
})}
|
||||
if _, e := c.Check(context.Background(), testRequest()); e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
if _, e := c.Check(context.Background(), testRequest()); e == nil {
|
||||
t.Fatal("old decision replay accepted for new nonce")
|
||||
}
|
||||
})
|
||||
}
|
||||
func TestUnavailableAndPublicTokenFileRefuse(t *testing.T) {
|
||||
c, _, _ := testClient(t)
|
||||
c.http = &http.Client{Transport: transportFunc(func(*http.Request) (*http.Response, error) { return nil, io.ErrUnexpectedEOF })}
|
||||
if _, e := c.Check(context.Background(), testRequest()); e == nil {
|
||||
t.Fatal("unavailable allowed")
|
||||
}
|
||||
if e := os.Chmod(c.admission.TokenFile, 0644); e != nil {
|
||||
t.Fatal(e)
|
||||
}
|
||||
if _, e := c.Check(context.Background(), testRequest()); e == nil {
|
||||
t.Fatal("public token file allowed")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"upstream": "github.com/netkingdom/access-engine",
|
||||
"commit": "d328a2601095686f74376d87791bff01e2a3a2a3",
|
||||
"exact_unchanged_files": {
|
||||
"pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19",
|
||||
"pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3",
|
||||
"pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f",
|
||||
"internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde",
|
||||
"LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6"
|
||||
},
|
||||
"module": "Standalone Go1.24 source slice with original module/import identity; no canonical/schema/signature modifications",
|
||||
"new_code": [
|
||||
"pkg/decisionpep/client.go"
|
||||
],
|
||||
"fixture_code": [
|
||||
"testsupport/signfixture/sign.go"
|
||||
],
|
||||
"source_of_truth": "repository root; generated with tools/export_decisionpep_adapter.py",
|
||||
"exported_file_sha256": {
|
||||
"LICENSE": "ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"go.mod": "fea700810e47c1ba5638b86a168f8531c083a6e1222ca80bd2a8947b2ea6f2c3",
|
||||
"internal/sign/sign.go": "0df9515922e90775ebbfac798361dcafb20da8a4213796b44e246b8a92046cde",
|
||||
"pkg/api/canonical.go": "a122a2e8cd524333b13c5939c7a3685e41efbe47497a64e0c7297c92b7f10d19",
|
||||
"pkg/api/caring.go": "1907156f0f889e7b9e9c14970cf7a86a20d00478a496efaf92c7231f27fec5e3",
|
||||
"pkg/api/resource_manifest.go": "f0fa4080287d389ed48c2b1d6af96cc62e57ef72c902333f677f964420be133f",
|
||||
"pkg/decisionpep/client.go": "0d57f6ddaca5c3f81ce426c0e8b14ba14ea5962096d3be5909aa456eef19eeba",
|
||||
"pkg/decisionpep/client_test.go": "1a6be3485f580f95a1f1406fb76e525f2c4bf02fe04a424cfe0095bb728e3f62",
|
||||
"testsupport/signfixture/sign.go": "fe4aa86f444dc20145aefbaca35b24f046264b2c5c8b4e3b057a4496b77a4d29"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Package signfixture exposes producer signing only for synthetic consumer tests.
|
||||
// Callers supply test keys; this package contains no credentials or runtime signer.
|
||||
package signfixture
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"github.com/netkingdom/access-engine/internal/sign"
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
)
|
||||
|
||||
// Sign uses the unchanged producer envelope format with a caller-owned fixture key.
|
||||
func Sign(envelope *api.DecisionEnvelope, key ed25519.PrivateKey, kid string) error {
|
||||
return sign.Sign(envelope, key, kid)
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
# Target Revenue Source License
|
||||
|
||||
**Version 1.0, Candidate 1 (V1C1)**
|
||||
|
||||
---
|
||||
|
||||
> **PRELIMINARY CANDIDATE — SUBJECT TO CHANGE — NOT FINAL**
|
||||
>
|
||||
> This repository is governed by the Target Revenue Source License
|
||||
> (TRSL), Version 1, Candidate 1. This is the framework's first working
|
||||
> candidate, adopted as the org's preliminary operating license across
|
||||
> all repos (maintainer decision, 2026-07-29) during the build/alpha
|
||||
> stage — see `SCOPE.md` §1 and Appendix A of the canonical text (link
|
||||
> below) for the alpha/beta risk-acceptance decision this reflects and
|
||||
> what it does and does not mean. Full specialist legal review is
|
||||
> explicitly deferred until the framework moves out of beta.
|
||||
>
|
||||
> **Canonical source, full candidate-status banner, and Appendix A
|
||||
> (non-normative candidate notes tracking every open item):** the
|
||||
> `coulomb` org's `target-revenue` repository,
|
||||
> `specs/TargetRevenueSourceLicense-V1C1.md` — this file is the operative
|
||||
> legal text (Preamble through Section 11) only; the canonical document
|
||||
> is authoritative if this copy and that document ever diverge.
|
||||
|
||||
---
|
||||
|
||||
## Preamble
|
||||
|
||||
This Target Revenue Source License ("**License**") governs the Software identified in the applicable Phase Manifest. It implements the Target Revenue Framework: a defined development Phase accumulates Development Credit and Remission Credit against an immutable Initial Target until the Milestone Release automatically and irrevocably converts to a declared permissive Future License.
|
||||
|
||||
Commercial beneficiaries fund the creation and early availability of a software improvement; once the declared target is satisfied, the governed release becomes permissively open source.
|
||||
|
||||
## 1. Definitions
|
||||
|
||||
Capitalized terms used in this License have the meanings given below. Where a term is also defined in the Phase Manifest or Target Ledger for a specific Phase, the Phase Manifest and Target Ledger govern the *values* (amounts, dates, identifiers) and this License governs the *legal effect* of those values — the two must not be read as conflicting definitions of the same concept.
|
||||
|
||||
**"Commercial Entitlement"** means a right, purchased or otherwise granted under a Commercial Use Agreement, to make Commercial Use of the Software during a Phase.
|
||||
|
||||
**"Commercial Use"** means billing, invoicing, or otherwise charging any customer a fee, subscription, license fee, or other consideration for or in connection with use of the Software, at any time before the Conversion Event for the applicable Phase, regardless of whether the person or organization billed would otherwise qualify for Noncommercial Use. Commercial Use occurs by virtue of such billing alone, whether or not the resulting payment is registered with the Trust Service; in particular, billing a customer for pre-conversion use of the Software without recording the corresponding payment in the applicable Phase's Target Ledger is Commercial Use without a valid Commercial Entitlement — a violation of Section 3, addressed under Section 7 and, where applicable, the Enforcement Network described in the canonical repository's `specs/EnforcementNetworkConcept.md`.
|
||||
|
||||
**"Commercial Use Agreement"** means the separate agreement, referenced by the applicable Phase Manifest, under which a Commercial Entitlement is purchased or granted. This License does not itself set pricing, metering, or payment terms — those are governed by the Commercial Use Agreement.
|
||||
|
||||
**"Conversion Event"** means the moment the Outstanding Target for a Phase reaches zero, as computed from the Phase Manifest and Target Ledger per the Target Ledger Specification. The Conversion Event occurs automatically and is not conditioned on any declaration, attestation, or other act by the Licensor or any Trust Service.
|
||||
|
||||
**"Development Credit"** means the portion of a collected and settled payment explicitly allocated toward satisfying the Initial Target of a specific Phase, as recorded in that Phase's Target Ledger.
|
||||
|
||||
**"Future License"** means the permissive license identified in the applicable Phase Manifest, being either the MIT License or the Apache License, Version 2.0, which applies to the Milestone Release upon the Conversion Event.
|
||||
|
||||
**"Initial Target"** means the immutable monetary target declared for a Phase in its Phase Manifest.
|
||||
|
||||
**"Licensor"** means **Binky Hedgehog GmbH**, the party that publishes the Phase Manifest and holds the rights necessary to grant this License and the Future License for the Milestone Release.
|
||||
|
||||
**"Milestone Release"** means the precisely identified software release designated in the applicable Phase Manifest, identified by an immutable source revision, release artifact, or cryptographic digest.
|
||||
|
||||
**"Noncommercial Use"** means use of the Software for personal purposes, private study, hobby or amateur projects; use by any charitable organization, educational institution, public research organization, or government institution acting in a non-revenue-generating capacity; or other use of a materially similar character.
|
||||
|
||||
**"Outstanding Target"** means, at any time, `max(0, Initial Target − cumulative Development Credit − cumulative Remission Credit)` for a Phase, as computed from that Phase's Target Ledger.
|
||||
|
||||
**"Phase"** means a bounded development undertaking governed by one Initial Target, one Milestone Release, one degeneration policy, and one Future License declaration, as declared in a Phase Manifest.
|
||||
|
||||
**"Phase Manifest"** means the published, immutable declaration identifying a Phase, its Milestone Release, Initial Target, Future License, degeneration policy, and Target Ledger location, as specified in the Phase Manifest Specification.
|
||||
|
||||
**"Remission Credit"** means a transparent, non-revenue reduction of a Phase's Outstanding Target, generated under that Phase's published degeneration policy and recorded in the Target Ledger.
|
||||
|
||||
**"Settled Payment"** means a payment that has cleared through its payment processor and is no longer subject to reversal in the ordinary course (chargeback, dispute, or equivalent), as further specified by the applicable Commercial Use Agreement or monetization extension.
|
||||
|
||||
**"Software"** means the source code, object code, and associated documentation of the Milestone Release identified in the applicable Phase Manifest.
|
||||
|
||||
**"Target Ledger"** means the append-only record of Development Credit, Remission Credit, and correction entries for a Phase, as specified in the Target Ledger Specification.
|
||||
|
||||
**"You"** or **"Licensee"** means the individual or entity exercising rights under this License.
|
||||
|
||||
## 2. Grant of Rights for Noncommercial Use
|
||||
|
||||
Subject to the terms of this License, the Licensor grants You a worldwide, royalty-free, non-exclusive license, during the applicable Phase, to:
|
||||
|
||||
(a) use, reproduce, and study the Software for any Noncommercial Use;
|
||||
|
||||
(b) modify the Software and create derivative works of it for any Noncommercial Use; and
|
||||
|
||||
(c) redistribute the Software and Your modifications, in source or object form, for any Noncommercial Use, provided that You include this License, unmodified, with any such redistribution, and that You do not remove or alter any copyright, patent, trademark, or attribution notices contained in the Software.
|
||||
|
||||
This grant does not extend to Commercial Use. Commercial Use requires a Commercial Entitlement under Section 3.
|
||||
|
||||
## 3. Commercial Use
|
||||
|
||||
You may not make Commercial Use of the Software during the applicable Phase unless You hold a valid, current Commercial Entitlement under a Commercial Use Agreement with the Licensor covering the applicable Phase. A Commercial Entitlement granted under one Phase's Commercial Use Agreement does not extend to a later Phase's Milestone Release unless the Commercial Use Agreement expressly says so.
|
||||
|
||||
This Section 3 states the existence and boundary of the commercial-use restriction. It does not itself set pricing, invoicing, metering, audit rights, or payment terms — those are governed exclusively by the applicable Commercial Use Agreement.
|
||||
|
||||
## 4. Patent License
|
||||
|
||||
Subject to the terms of this License, each contributor to the Software grants You, during the applicable Phase and solely to the extent of rights granted under Sections 2 and 3, a perpetual (subject to the termination below), worldwide, non-exclusive, no-charge, royalty-free patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Software, limited to those patent claims licensable by that contributor that are necessarily infringed by their contribution(s) alone or by combination of their contribution(s) with the Software.
|
||||
|
||||
If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software or a contribution incorporated within it constitutes direct or contributory patent infringement, then any patent licenses granted to You under this Section 4 for the Software shall terminate as of the date such litigation is filed.
|
||||
|
||||
## 5. Automatic Conversion to the Future License
|
||||
|
||||
**5.1 Automatic effect.** Upon the Conversion Event for a Phase, the rights and restrictions in Sections 3 (Commercial Use) of this License, as they apply to that Phase's Milestone Release, terminate automatically. In their place, the Milestone Release is licensed under the Future License identified in that Phase's Phase Manifest, effective as of the Conversion Event, without any further act, declaration, or attestation required by the Licensor, any Trust Service, or any other party.
|
||||
|
||||
**5.2 Irrevocability.** Once a valid Conversion Event has occurred for a Phase, no subsequent refund, chargeback, accounting correction, dispute, or termination of this License for an unrelated breach shall revoke, suspend, or otherwise impair the Future License grant for that Phase's Milestone Release. Any shortfall or dispute arising after a Conversion Event is a commercial or accounting matter between the relevant parties and does not reinstate a commercial-use restriction over already-converted Software.
|
||||
|
||||
**5.3 Prior freedom preserved.** A later Phase covering subsequent improvements to the Software does not restrict, withdraw, or otherwise affect the rights granted under the Future License for an earlier Phase's already-converted Milestone Release.
|
||||
|
||||
**5.4 Evidence, not cause.** A Trust Service may publish a Conversion Attestation documenting a Conversion Event. Such an attestation is evidence that the Conversion Event occurred; it is not a condition of, and its absence or delay does not postpone, the automatic effect described in Section 5.1. Any person may independently verify whether a Conversion Event has occurred directly from the Phase Manifest and Target Ledger.
|
||||
|
||||
## 6. Successive Phases
|
||||
|
||||
The Licensor may declare a new Phase covering subsequent improvements to the Software following a Milestone Release's Conversion Event. Each Phase is independently governed by its own Phase Manifest, Initial Target, degeneration policy, and Target Ledger. Nothing in a later Phase's Phase Manifest may be construed to reduce or withdraw rights already granted under Section 5 for an earlier Phase's Milestone Release.
|
||||
|
||||
## 7. Term and Termination
|
||||
|
||||
**7.1 Term.** This License applies to the Software for the duration of the applicable Phase, and, for the Milestone Release, indefinitely following that Phase's Conversion Event under the Future License.
|
||||
|
||||
**7.2 Termination for breach.** If You breach Section 3 (Commercial Use) or Section 2(c) (redistribution notice requirement), the Licensor may terminate this License as to You. Before such termination becomes effective, the Licensor shall provide You written notice of the breach; if You cure the breach within thirty (30) days of that notice, this License continues in effect. A second breach of the same provision within twelve (12) months may be terminated immediately without a further cure opportunity.
|
||||
|
||||
**7.3 Effect of termination.** Termination under this Section 7 affects only Your rights under Sections 2 and 3 for the Phase in which the breach occurred. It does not affect any rights already vested under Section 5 (Automatic Conversion) for a Milestone Release whose Conversion Event has already occurred, per Section 5.2.
|
||||
|
||||
**7.4 Public record of breach and resolution.** The Licensor shall cause the Trust Service to publish, as part of the public record for the affected Phase, notice of: (a) any breach notice issued under Section 7.2, stating the general nature of the breach and the date of notice; (b) whether the breach was cured within the applicable cure period, and the date of cure; and (c) any termination determination made under this Section 7, including its effective date and scope. This public record exists to give the ecosystem a transparent, verifiable conformity signal for the Phase, distinct from and in addition to the Development Credit and Remission Credit facts already published under Section 5.4 and the Target Ledger Specification.
|
||||
|
||||
A breach that You dispute, and that has not been finally determined, shall be recorded as **alleged**; it shall be recorded as **determined** only once the cure period has run without cure, or the dispute has been resolved against You under the applicable Commercial Use Agreement's dispute process, if any. The Trust Service shall update the record promptly upon resolution in either direction. Recording an alleged or determined breach under this Section 7.4 is a ministerial act of publishing the Licensor's determination (or a dispute process's outcome); it does not give the Trust Service discretionary authority to decide whether a breach occurred, consistent with Section 5.4's evidence-not-cause principle.
|
||||
|
||||
Whether, and under what conditions, the public record identifies a Commercial Entitlement holder by name is governed exclusively by the applicable Commercial Use Agreement, which the Licensor and that Commercial Entitlement holder negotiate and agree to directly. This License does not itself set a naming default. Where no Commercial Use Agreement addresses the question, or where the affected party has no Commercial Use Agreement at all (for example, a Section 2(c) breach by a Noncommercial Use licensee), the public record states the Phase and breach category only, without naming the party.
|
||||
|
||||
## 8. Disclaimer of Warranty
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. THE LICENSOR DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR-FREE OR THAT ANY PHASE WILL REACH ITS CONVERSION EVENT.
|
||||
|
||||
## 9. Limitation of Liability
|
||||
|
||||
IN NO EVENT SHALL THE LICENSOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EXCEPT TO THE EXTENT SUCH LIMITATION IS PROHIBITED BY APPLICABLE LAW.
|
||||
|
||||
## 10. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary attribution.
|
||||
|
||||
## 11. General Provisions
|
||||
|
||||
**11.1 Governing law and venue.** Adopted for alpha/beta 2026-07-29: disputes arising under this License shall be resolved by binding arbitration, seated at a neutral, arbitration-mature venue (Singapore or London are the two candidates concretely supported by current research), rather than by litigation in a national court. The specific arbitral institution and substantive governing law remain a per-deployment blank pending final selection; they are not fixed by this candidate. See the canonical repository's `history/260729-TRSL-Jurisdiction-Synthesis.md` §2.
|
||||
|
||||
**11.2 Severability.** If any provision of this License is held unenforceable, the remaining provisions remain in full force, and the unenforceable provision shall be reformed to the minimum extent necessary to make it enforceable.
|
||||
|
||||
**11.3 No waiver.** Failure to enforce any provision of this License is not a waiver of future enforcement of that or any other provision.
|
||||
|
||||
**11.4 Entire agreement (as to licensing).** This License, together with the applicable Phase Manifest and, where applicable, the Commercial Use Agreement, constitutes the entire agreement between You and the Licensor regarding the Software's licensing terms. Operations, service, and consulting arrangements are governed by separate agreements, if any, and are not part of this License.
|
||||
|
||||
**11.5 Definitions control.** Marketing materials, documentation, or other non-normative communications about the Software must not describe pre-Conversion-Event Software as "Open Source," "free software," or "open core." Pre-conversion Noncommercial Use is **source-available**; pre-conversion Commercial Use requires a **Commercial Entitlement**; only post-conversion Software may be described as Open Source, under the Future License.
|
||||
|
||||
---
|
||||
|
||||
**No Phase is currently declared for this repository under this License.** Until a Phase Manifest is published and registered with the Trust Service for a Milestone Release in this repository, Sections 2–7 above have no operative subject matter here — this License establishes the governing framework in advance of that declaration, consistent with the org-wide rollout decision recorded in `target-revenue`'s `workplans/TREV-WP-0008-governance-and-pilot-rollout.md`.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module keycape
|
||||
|
||||
go 1.23.0
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
// Package jose provides strict RS256 JWT signature verification against a JWK
|
||||
// set. It is deliberately narrow: it establishes that a token was signed by a
|
||||
// key in the given set and returns its claims. It applies no claim policy —
|
||||
// issuer, audience, expiry and nonce rules differ per caller and belong with
|
||||
// the caller that knows them.
|
||||
//
|
||||
// The strictness here is the point. A verifier that accepts an unexpected
|
||||
// algorithm, an unbounded exponent or an undersized modulus is worse than no
|
||||
// verifier, because callers stop checking what it appears to guarantee.
|
||||
package jose
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrVerification is returned for every verification failure. The cause is
|
||||
// deliberately not distinguished: a caller acting on why a token failed tends to
|
||||
// leak that distinction to whoever supplied the token.
|
||||
var ErrVerification = errors.New("jose: token verification failed")
|
||||
|
||||
// KeySet maps key IDs to RSA public keys.
|
||||
type KeySet map[string]*rsa.PublicKey
|
||||
|
||||
// ParseJWKS parses a JWK set, keeping only RSA signing keys usable with RS256.
|
||||
// Keys that are malformed, too small, or carry an even or trivial exponent are
|
||||
// rejected rather than skipped, since a key set that is partly wrong is not
|
||||
// evidence of anything.
|
||||
func ParseJWKS(raw []byte) (KeySet, error) {
|
||||
var document struct {
|
||||
Keys []struct {
|
||||
Kty string `json:"kty"`
|
||||
Use string `json:"use"`
|
||||
Alg string `json:"alg"`
|
||||
Kid string `json:"kid"`
|
||||
N string `json:"n"`
|
||||
E string `json:"e"`
|
||||
} `json:"keys"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &document); err != nil {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
keys := make(KeySet, len(document.Keys))
|
||||
for _, key := range document.Keys {
|
||||
// Ignore keys for other algorithms or purposes; they are not errors.
|
||||
if key.Kty != "RSA" || (key.Use != "" && key.Use != "sig") || (key.Alg != "" && key.Alg != "RS256") {
|
||||
continue
|
||||
}
|
||||
if key.Kid == "" {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
modulus, errN := decode(key.N)
|
||||
exponent, errE := decode(key.E)
|
||||
if errN != nil || errE != nil || len(exponent) == 0 || len(exponent) > 4 {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
public := &rsa.PublicKey{
|
||||
N: new(big.Int).SetBytes(modulus),
|
||||
E: int(new(big.Int).SetBytes(exponent).Int64()),
|
||||
}
|
||||
if public.N.BitLen() < 2048 || public.E < 3 || public.E%2 == 0 {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
if _, duplicate := keys[key.Kid]; duplicate {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
keys[key.Kid] = public
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// KeyID returns the key ID from a token's JOSE header, after checking that the
|
||||
// header names RS256 and carries no critical extensions. Callers use it to
|
||||
// decide whether to refresh their key set before verifying.
|
||||
func KeyID(token string) (string, error) {
|
||||
header, _, _, err := split(token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return header.Kid, nil
|
||||
}
|
||||
|
||||
// Verify checks a token's RS256 signature against the key set and returns its
|
||||
// claims. It does not inspect any claim.
|
||||
func Verify(token string, keys KeySet) (map[string]interface{}, error) {
|
||||
return verify(token, keys, "")
|
||||
}
|
||||
|
||||
// VerifyTyped applies the same signature verifier with an explicit JOSE type.
|
||||
// Existing Verify callers retain their claim/type policy; online access-token
|
||||
// consumers opt into the producer's exact JWT header contract here.
|
||||
func VerifyTyped(token string, keys KeySet, expectedType string) (map[string]interface{}, error) {
|
||||
if expectedType == "" {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
return verify(token, keys, expectedType)
|
||||
}
|
||||
|
||||
func verify(token string, keys KeySet, expectedType string) (map[string]interface{}, error) {
|
||||
header, signingInput, signature, err := split(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expectedType != "" && header.Typ != expectedType {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
public, ok := keys[header.Kid]
|
||||
if !ok {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
digest := sha256.Sum256([]byte(signingInput))
|
||||
if rsa.VerifyPKCS1v15(public, crypto.SHA256, digest[:], signature) != nil {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
payload, err := decode(strings.Split(signingInput, ".")[1])
|
||||
if err != nil {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
claims := map[string]interface{}{}
|
||||
if json.Unmarshal(payload, &claims) != nil {
|
||||
return nil, ErrVerification
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
type joseHeader struct {
|
||||
Alg string `json:"alg"`
|
||||
Typ string `json:"typ"`
|
||||
Kid string `json:"kid"`
|
||||
Crit []string `json:"crit"`
|
||||
}
|
||||
|
||||
// split validates the token's shape and header and returns the header, the
|
||||
// signing input, and the decoded signature.
|
||||
func split(token string) (joseHeader, string, []byte, error) {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return joseHeader{}, "", nil, ErrVerification
|
||||
}
|
||||
raw, err := decode(parts[0])
|
||||
if err != nil {
|
||||
return joseHeader{}, "", nil, ErrVerification
|
||||
}
|
||||
var header joseHeader
|
||||
if json.Unmarshal(raw, &header) != nil {
|
||||
return joseHeader{}, "", nil, ErrVerification
|
||||
}
|
||||
// An unexpected algorithm must fail before any key is selected, so "none"
|
||||
// and symmetric algorithms can never reach the RSA path.
|
||||
if header.Alg != "RS256" || header.Kid == "" || len(header.Crit) != 0 {
|
||||
return joseHeader{}, "", nil, ErrVerification
|
||||
}
|
||||
signature, err := decode(parts[2])
|
||||
if err != nil {
|
||||
return joseHeader{}, "", nil, ErrVerification
|
||||
}
|
||||
return header, parts[0] + "." + parts[1], signature, nil
|
||||
}
|
||||
|
||||
func decode(segment string) ([]byte, error) {
|
||||
return base64.RawURLEncoding.DecodeString(segment)
|
||||
}
|
||||
|
|
@ -0,0 +1,379 @@
|
|||
package tokenstatus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"keycape/internal/jose"
|
||||
)
|
||||
|
||||
var ErrOnlineToken = errors.New("current authenticated token refused")
|
||||
|
||||
// OnlineAdmission is independently admitted owner configuration. The public
|
||||
// JWKS/CA and private client credential files are reread for every request.
|
||||
// It selects the owning confidential client, not a resource-server delegation.
|
||||
type OnlineAdmission struct {
|
||||
Issuer string `json:"issuer"`
|
||||
Audience string `json:"audience"`
|
||||
ClientID string `json:"client_id"`
|
||||
CAFile string `json:"ca_file"`
|
||||
JWKSFile string `json:"jwks_file"`
|
||||
SecretFile string `json:"secret_file"`
|
||||
MaxTokenAge string `json:"max_token_age"`
|
||||
}
|
||||
|
||||
type ExpectedIdentity struct {
|
||||
Subject string
|
||||
Tenant string
|
||||
PrincipalType string
|
||||
}
|
||||
|
||||
// CurrentToken contains signed identity facts joined to a current issuer
|
||||
// observation. It is never a permission, approval or portable execution receipt.
|
||||
type CurrentToken struct {
|
||||
Issuer string `json:"issuer"`
|
||||
Audience string `json:"audience"`
|
||||
ClientID string `json:"client_id"`
|
||||
Subject string `json:"subject"`
|
||||
Tenant string `json:"tenant"`
|
||||
PrincipalType string `json:"principal_type"`
|
||||
TokenID string `json:"jti"`
|
||||
TokenDigest string `json:"token_sha256"`
|
||||
IssuedAt time.Time `json:"issued_at"`
|
||||
NotAfter time.Time `json:"not_after"`
|
||||
CheckedAt time.Time `json:"checked_at"`
|
||||
claims map[string]interface{}
|
||||
}
|
||||
|
||||
// Claims returns a separate copy of the verified signed facts. Authentication
|
||||
// assurance/age, tenant provenance, current groups and resource/effect policy
|
||||
// remain the consumer's existing profile obligations, not a new client policy.
|
||||
func (t CurrentToken) Claims() map[string]interface{} {
|
||||
if t.claims == nil {
|
||||
return nil
|
||||
}
|
||||
raw, _ := json.Marshal(t.claims)
|
||||
var out map[string]interface{}
|
||||
_ = json.Unmarshal(raw, &out)
|
||||
return out
|
||||
}
|
||||
|
||||
// InactiveForClient proves only the issuer's current negative response for the
|
||||
// named client/token digest. An unknown/other-client token gives the same result;
|
||||
// original grant ownership is separately required before asserting cleanup.
|
||||
type InactiveForClient struct {
|
||||
Issuer string `json:"issuer"`
|
||||
ClientID string `json:"client_id"`
|
||||
TokenDigest string `json:"token_sha256"`
|
||||
CheckedAt time.Time `json:"checked_at"`
|
||||
}
|
||||
|
||||
type OnlineClient struct {
|
||||
admission OnlineAdmission
|
||||
age time.Duration
|
||||
now func() time.Time
|
||||
// Nil uses the normal resolver/dialer. Tests route a certified DNS name to
|
||||
// isolated loopback; the TLS name/CA and JWT trust checks remain unchanged.
|
||||
dialContext func(context.Context, string, string) (net.Conn, error)
|
||||
}
|
||||
|
||||
func NewOnlineClient(a OnlineAdmission) (*OnlineClient, error) {
|
||||
u, err := url.Parse(a.Issuer)
|
||||
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || strings.ContainsAny(a.Issuer, "?#") || u.RawPath != "" || (u.Path != "" && path.Clean(u.Path) != u.Path) || strings.HasSuffix(a.Issuer, "/") {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
if a.Audience == "" || a.ClientID == "" || !filepath.IsAbs(a.CAFile) || !filepath.IsAbs(a.JWKSFile) || !filepath.IsAbs(a.SecretFile) {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
age, err := time.ParseDuration(a.MaxTokenAge)
|
||||
if err != nil || age <= 0 || age > time.Hour {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
return &OnlineClient{admission: a, age: age, now: time.Now}, nil
|
||||
}
|
||||
|
||||
func readOnlineFile(path string, private bool) ([]byte, error) {
|
||||
limit := int64(1 << 20)
|
||||
if private {
|
||||
limit = 16384
|
||||
}
|
||||
return readOnlineBoundedFile(path, private, limit)
|
||||
}
|
||||
|
||||
func readOnlineBoundedFile(path string, private bool, limit int64) ([]byte, error) {
|
||||
if limit <= 0 || limit > 1<<20 {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
if !filepath.IsAbs(path) {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_RDONLY|syscall.O_NONBLOCK, 0)
|
||||
if err != nil {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
defer f.Close()
|
||||
info, err := f.Stat()
|
||||
if err != nil || !info.Mode().IsRegular() || info.Mode().Perm()&0022 != 0 || (private && info.Mode().Perm()&0007 != 0) {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
raw, err := io.ReadAll(io.LimitReader(f, limit+1))
|
||||
if err != nil || len(raw) > int(limit) || len(raw) == 0 {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func tokenDigest(raw string) string {
|
||||
digest := sha256.Sum256([]byte(raw))
|
||||
return "sha256:" + hex.EncodeToString(digest[:])
|
||||
}
|
||||
|
||||
func exactUnix(value interface{}) (time.Time, bool) {
|
||||
n, ok := value.(float64)
|
||||
if !ok || n <= 0 || n > 9007199254740991 || math.Trunc(n) != n {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return time.Unix(int64(n), 0).UTC(), true
|
||||
}
|
||||
|
||||
func (c *OnlineClient) verify(raw string, expected ExpectedIdentity, now time.Time) (CurrentToken, error) {
|
||||
var out CurrentToken
|
||||
if c == nil || len(raw) == 0 || len(raw) > 32768 || expected.Subject == "" || expected.Tenant == "" || (expected.PrincipalType != "human" && expected.PrincipalType != "service") {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
keysRaw, err := readOnlineFile(c.admission.JWKSFile, false)
|
||||
if err != nil {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
keys, err := jose.ParseJWKS(keysRaw)
|
||||
if err != nil {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
claims, err := jose.VerifyTyped(raw, keys, "JWT")
|
||||
if err != nil {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
if claims["iss"] != c.admission.Issuer || claims["aud"] != c.admission.Audience || claims["sub"] != expected.Subject || claims["tenant"] != expected.Tenant || claims["principal_type"] != expected.PrincipalType {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
if azp, present := claims["azp"]; present && azp != c.admission.ClientID {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
_, scopeOK := claims["scope"].(string)
|
||||
jti, jtiOK := claims["jti"].(string)
|
||||
decodedID, idErr := base64.RawURLEncoding.Strict().DecodeString(jti)
|
||||
issued, issuedOK := exactUnix(claims["iat"])
|
||||
expires, expiresOK := exactUnix(claims["exp"])
|
||||
if !scopeOK || !jtiOK || idErr != nil || len(decodedID) != 32 || !issuedOK || !expiresOK || issued.After(now) || !expires.After(issued) || !now.Before(expires) {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
if nbf, present := claims["nbf"]; present {
|
||||
start, valid := exactUnix(nbf)
|
||||
if !valid || start.After(now) {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
}
|
||||
end := issued.Add(c.age)
|
||||
if expires.Before(end) {
|
||||
end = expires
|
||||
}
|
||||
if !now.Before(end) {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
return CurrentToken{Issuer: c.admission.Issuer, Audience: c.admission.Audience, ClientID: c.admission.ClientID,
|
||||
Subject: expected.Subject, Tenant: expected.Tenant, PrincipalType: expected.PrincipalType,
|
||||
TokenID: jti, TokenDigest: tokenDigest(raw), IssuedAt: issued, NotAfter: end, CheckedAt: now, claims: claims}, nil
|
||||
}
|
||||
|
||||
type statusWire struct {
|
||||
Active *bool `json:"active"`
|
||||
ClientID string `json:"client_id"`
|
||||
TokenType string `json:"token_type"`
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Audience string `json:"aud"`
|
||||
Scope string `json:"scope"`
|
||||
IssuedAt int64 `json:"iat"`
|
||||
ExpiresAt int64 `json:"exp"`
|
||||
TokenID string `json:"jti"`
|
||||
}
|
||||
|
||||
func decodeOnlineObject(raw []byte, out interface{}) (map[string]bool, error) {
|
||||
d := json.NewDecoder(bytes.NewReader(raw))
|
||||
first, err := d.Token()
|
||||
if err != nil || first != json.Delim('{') {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for d.More() {
|
||||
key, err := d.Token()
|
||||
name, ok := key.(string)
|
||||
if err != nil || !ok || seen[name] {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
seen[name] = true
|
||||
var value json.RawMessage
|
||||
if d.Decode(&value) != nil || bytes.Equal(bytes.TrimSpace(value), []byte("null")) {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
}
|
||||
if last, err := d.Token(); err != nil || last != json.Delim('}') {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
if _, err := d.Token(); err != io.EOF {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
d = json.NewDecoder(bytes.NewReader(raw))
|
||||
d.DisallowUnknownFields()
|
||||
if d.Decode(out) != nil {
|
||||
return nil, ErrOnlineToken
|
||||
}
|
||||
return seen, nil
|
||||
}
|
||||
|
||||
func decodeOnlineStatus(raw []byte) (statusWire, error) {
|
||||
var out statusWire
|
||||
seen, err := decodeOnlineObject(raw, &out)
|
||||
if err != nil || out.Active == nil || (!*out.Active && len(seen) != 1) {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
if *out.Active {
|
||||
if len(seen) != 10 {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
for _, field := range []string{"active", "client_id", "token_type", "iss", "sub", "aud", "scope", "iat", "exp", "jti"} {
|
||||
if !seen[field] {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *OnlineClient) request(ctx context.Context, endpoint, raw string) (statusWire, error) {
|
||||
var out statusWire
|
||||
if c == nil || ctx == nil || ctx.Err() != nil || len(raw) == 0 || len(raw) > 32768 || (endpoint != "/introspect" && endpoint != "/revoke") {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
ca, err := readOnlineFile(c.admission.CAFile, false)
|
||||
if err != nil {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
if !roots.AppendCertsFromPEM(ca) {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
secretRaw, err := readOnlineFile(c.admission.SecretFile, true)
|
||||
if err != nil {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
secret := strings.TrimSuffix(strings.TrimSuffix(string(secretRaw), "\n"), "\r")
|
||||
if secret == "" || strings.ContainsAny(secret, "\r\n\x00") {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
form := url.Values{"token": {raw}, "token_type_hint": {"access_token"}}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.admission.Issuer+endpoint, strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
req.SetBasicAuth(url.QueryEscape(c.admission.ClientID), url.QueryEscape(secret))
|
||||
transport := &http.Transport{Proxy: nil, TLSClientConfig: &tls.Config{RootCAs: roots, MinVersion: tls.VersionTLS12}, DisableKeepAlives: true, DialContext: c.dialContext}
|
||||
defer transport.CloseIdleConnections()
|
||||
client := &http.Client{Transport: transport, Timeout: 5 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return ErrOnlineToken }}
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK || response.Header.Get("Cache-Control") != "no-store" {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(response.Body, 65537))
|
||||
if err != nil || len(body) > 65536 {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
if endpoint == "/revoke" {
|
||||
if len(body) != 0 {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
media, _, err := mime.ParseMediaType(response.Header.Get("Content-Type"))
|
||||
if err != nil || media != "application/json" {
|
||||
return out, ErrOnlineToken
|
||||
}
|
||||
return decodeOnlineStatus(body)
|
||||
}
|
||||
|
||||
// Check verifies expected identity before accessing the private client credential,
|
||||
// sends a new authenticated introspection and rechecks lifetime after the network.
|
||||
func (c *OnlineClient) Check(ctx context.Context, raw string, expected ExpectedIdentity) (CurrentToken, error) {
|
||||
if c == nil || ctx == nil || ctx.Err() != nil {
|
||||
return CurrentToken{}, ErrOnlineToken
|
||||
}
|
||||
identity, err := c.verify(raw, expected, c.now().UTC())
|
||||
if err != nil {
|
||||
return CurrentToken{}, ErrOnlineToken
|
||||
}
|
||||
status, err := c.request(ctx, "/introspect", raw)
|
||||
if err != nil || status.Active == nil || !*status.Active {
|
||||
return CurrentToken{}, ErrOnlineToken
|
||||
}
|
||||
expires, _ := exactUnix(identity.claims["exp"])
|
||||
if status.ClientID != c.admission.ClientID || status.TokenType != "Bearer" || status.Issuer != identity.Issuer || status.Subject != identity.Subject || status.Audience != identity.Audience || status.TokenID != identity.TokenID || status.Scope != identity.claims["scope"] || status.IssuedAt != identity.IssuedAt.Unix() || status.ExpiresAt != expires.Unix() {
|
||||
return CurrentToken{}, ErrOnlineToken
|
||||
}
|
||||
end := c.now().UTC()
|
||||
if end.Before(identity.CheckedAt) {
|
||||
return CurrentToken{}, ErrOnlineToken
|
||||
}
|
||||
// A slow lookup cannot retain expired facts or a withdrawn JWT trust key.
|
||||
return c.verify(raw, expected, end)
|
||||
}
|
||||
|
||||
// Revoke performs cleanup only for the exact opaque token. An empty 200 is not
|
||||
// proof: a separate authenticated introspection must confirm inactive-for-client.
|
||||
// The caller retains original grant ownership and uncertainty/retry duties.
|
||||
func (c *OnlineClient) Revoke(ctx context.Context, raw string) (InactiveForClient, error) {
|
||||
if c == nil {
|
||||
return InactiveForClient{}, ErrOnlineToken
|
||||
}
|
||||
if _, err := c.request(ctx, "/revoke", raw); err != nil {
|
||||
return InactiveForClient{}, ErrOnlineToken
|
||||
}
|
||||
return c.ObserveInactive(ctx, raw)
|
||||
}
|
||||
|
||||
// ObserveInactive is read-only. Unavailability or a malformed response is never
|
||||
// interpreted as revocation; the negative result remains scoped to this client.
|
||||
func (c *OnlineClient) ObserveInactive(ctx context.Context, raw string) (InactiveForClient, error) {
|
||||
if c == nil {
|
||||
return InactiveForClient{}, ErrOnlineToken
|
||||
}
|
||||
status, err := c.request(ctx, "/introspect", raw)
|
||||
if err != nil || status.Active == nil || *status.Active {
|
||||
return InactiveForClient{}, ErrOnlineToken
|
||||
}
|
||||
return InactiveForClient{Issuer: c.admission.Issuer, ClientID: c.admission.ClientID, TokenDigest: tokenDigest(raw), CheckedAt: c.now().UTC()}, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"source_of_truth": "key-cape source; generated by tools/export_tokenstatus_client.py",
|
||||
"source_files": [
|
||||
"src/internal/authclient/online.go",
|
||||
"src/internal/jose/jose.go",
|
||||
"LICENSE"
|
||||
],
|
||||
"transformation": "replace only the first package declaration authclient with tokenstatus; all remaining client bytes and shared JOSE bytes exact",
|
||||
"module": "keycape",
|
||||
"profile": "issuer online access-token status; no policy evaluator or deployment",
|
||||
"supported_file_boundary": "Linux owner execution profile; POSIX mode checks; not a Windows ACL contract",
|
||||
"sha256": {
|
||||
"LICENSE": "sha256:ff413f361a3b3bf44cf50f110fbcc4837064d57eefbcef89df2b43997c6262b6",
|
||||
"go.mod": "sha256:e8b028e1002e63a76dba78a4abbd67882307ad198e4783bad631652061aa0f54",
|
||||
"internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"pkg/tokenstatus/client.go": "sha256:f34626a85d67590b9c61474f19ce1021f8afd17093f8f522b3f98e9cef34e575"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 29.8,
|
||||
"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-keycape-token-pair-r1",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod",
|
||||
"/modules",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
|
||||
"/toolchain",
|
||||
"--chdir",
|
||||
"/work/source/src",
|
||||
"--clearenv",
|
||||
"--bind",
|
||||
"/tmp/custodian-keycape-token-status-r1/gocache",
|
||||
"/work/gocache",
|
||||
"--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",
|
||||
"1",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"4",
|
||||
"-race",
|
||||
"-json",
|
||||
"-count=1",
|
||||
"./..."
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"src/go.mod": "sha256:ab299ad4f94feb78e217e2228a4aa161b166460aa28df0c5009451ac1a337456",
|
||||
"src/Makefile": "sha256:6cdd3aa6e1cccaf4e028278acafe3e6a8d28170de89ff3462984d4f6eb56bd96",
|
||||
"src/go.sum": "sha256:b10ad54277fb2bd927d3af727fc4dbb9ebe91a038a75be0469d0253ca699226d",
|
||||
"src/tests/negative/negative_test.go": "sha256:757485bf1967ed95e872d4a1987b7fd985fbffccd05cf41270b9f4061431de3f",
|
||||
"src/tests/conformance/external_test.go": "sha256:b26bd465e31086f957c9e374b6bee1f0f077b7481eda9daa29cf63816bb87fdb",
|
||||
"src/tests/migration/scenario_c_test.go": "sha256:9722146ac224768aff85476acbd83f3d478c1e060e5a3c6f99b1786858879b80",
|
||||
"src/tests/migration/fixtures_test.go": "sha256:3fbcb8f531fa206225f719a67b744b2ecb908095fbae0538432796697971e6de",
|
||||
"src/tests/migration/scenario_b_test.go": "sha256:40ae7239d116f3e284c767570d39e1f32d44fc29b6fa37ea8835987027942ac0",
|
||||
"src/tests/profile/profile_test.go": "sha256:e5ab4568b15f681a13c9295b17907ca3872ee33b702bb56aea749f17758d29e6",
|
||||
"src/internal/domain/assurance_test.go": "sha256:87680e7afd47294dadc13092ca2bb7c47b9625c80e48a701765c00df3293d755",
|
||||
"src/internal/domain/conformance_test.go": "sha256:6420ec21d4e7a97f60902e1f0debbaf20df91aeac6ab110629b82a67f4c5c9a1",
|
||||
"src/internal/domain/repository.go": "sha256:28f6d8f7f3d822e73f164af5e877f252db7dc6faa97f0ebf75d76aad64078011",
|
||||
"src/internal/domain/auth.go": "sha256:ae800c5b1f58408147dd28a5c87ab2603b1537500a289397e711a4cb0e132cee",
|
||||
"src/internal/domain/model.go": "sha256:ce4309fc7426c66e4ebc18022d2f704c10ebbec243cc98e657d1de765ad09029",
|
||||
"src/internal/domain/assurance.go": "sha256:d3a29ece7c4fa4d9daa8b5fda89cbe0dd1ffef2ee2ecae0051ee68010edb8fd7",
|
||||
"src/internal/domain/mfa.go": "sha256:1b7875b08a0f280a20a14a4fa4fdb4fc84c3942795b1d1417943d32634121b87",
|
||||
"src/internal/authclient/cli.go": "sha256:009c9ce900524b62483cf4955f3cbacc0dcb9027b8da77924b51b22e3fa5170c",
|
||||
"src/internal/authclient/client.go": "sha256:61fb150349c5d3714450a90834c59af9a579a8a649c4149297a402cfcf6e39c5",
|
||||
"src/internal/authclient/online_cli.go": "sha256:6763c1e85396726b044067e77179ee2220e397bbd3888ec8de071c2826c6c7f2",
|
||||
"src/internal/authclient/verify.go": "sha256:804735be927ba7d0228ac378056730040d3ccf2ed020b4da0714085bae972f25",
|
||||
"src/internal/authclient/online.go": "sha256:182ec3971ded3a7f3923aab7a23b7317de3ec0f657b8da6d9d882a43bcb0961a",
|
||||
"src/internal/authclient/online_export_test.go": "sha256:5695f233e4caf581e494087cb203a74613d39a5626bbcc323b9f2d553a96b7ba",
|
||||
"src/internal/authclient/client_test.go": "sha256:23642488fcff45e369d22ba0000c47e0e450c5f92560e643ab96303e2a2f43aa",
|
||||
"src/internal/authclient/online_cli_test.go": "sha256:5408a183beda6a885ebae00d3fa420d3fb0d51e4aeb72965fef535d822270c4f",
|
||||
"src/internal/authclient/online_test.go": "sha256:6288329ca2576f72a361256050c94f9d1995e9fbe53b2a6318da2954bcbc7c6a",
|
||||
"src/internal/authclient/online_human_test.go": "sha256:e501c30b6d710a6dea89559ccf8b98de68502c2ee58d1778a56e97f1b7f3b6e0",
|
||||
"src/internal/authclient/verify_test.go": "sha256:7febc6698c87eac5fd8d2520c4f77c68e82f18a2aafa448cfa4990b96fdc46fc",
|
||||
"src/internal/validator/report.go": "sha256:6bc4d31f932ea9a32020cf33203978906730dfe3601350ded0206ff6c8c297a8",
|
||||
"src/internal/validator/validator_test.go": "sha256:8216027a464eab4167d8e87b4d27d130854d7b9cd4cc8a1b6f1a47ff1f9a2276",
|
||||
"src/internal/validator/validator.go": "sha256:3d0a8bf8ebbe54e21e162c8f7df6e4a4d69ab997a01969b74dcbd53decdf559f",
|
||||
"src/internal/config/account_test.go": "sha256:c6b75097d14dd42415c836407fa2a509857cdd2b771fb7e53d4507cbad2f4b1d",
|
||||
"src/internal/config/validate.go": "sha256:2b1766979b817802f43bdd0f999ac61452d86cdfafc1539a5d6be896b900a405",
|
||||
"src/internal/config/optional_mfa_test.go": "sha256:03a00bc309943adbed68bdb9bf47facb6b3b50ef380fd49143b5ed54d684f63e",
|
||||
"src/internal/config/config_test.go": "sha256:af8602010985a0a5a6b4b2bf779f824e7eb5141e6bc2e8d8fcf600b964955100",
|
||||
"src/internal/config/token_status_test.go": "sha256:b2b2ec13215c9661d79142ea698cb8b080f6e017de050bedebf3eb8d16df2ad5",
|
||||
"src/internal/config/config.go": "sha256:19cd234757f8908880d28b8b40939367dfab1322e0401e4e563d56cd3af06683",
|
||||
"src/internal/errors/taxonomy.go": "sha256:7d60f30a4ff0c9a21a3942451eb0292320a6346d3d81508e414a7dfbdc3c666c",
|
||||
"src/internal/errors/taxonomy_test.go": "sha256:6c0bdf1f8e90ea343d88f57f80f6c916c92428a9ac2191a518ee7b70df0eed45",
|
||||
"src/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"src/internal/jose/jose_test.go": "sha256:cbbcbac620b27c8e93d9dc3fefc3ca4fcd20ab58c6bc8f79eb041b980aaa9a43",
|
||||
"src/internal/server/policy/policy_test.go": "sha256:25644557ca532b608dff4e02b202c5c3b7c423fb31e98ad0609febf3660183a2",
|
||||
"src/internal/server/policy/policy.go": "sha256:015d0b42419c501a46171d09a8f7d588dd517d4c98ad14344ff52113294e0fe7",
|
||||
"src/internal/server/errors/enforcement.go": "sha256:f644546b1423992518fc4078ab561ab68fea1fcff00b734795af6652ae3dd4e2",
|
||||
"src/internal/server/errors/enforcement_test.go": "sha256:b4ea0c9a3aaebd66b0b0fcc73f544c76d5a377ac0c9561ccc2638a236844374c",
|
||||
"src/internal/server/telemetry/emitter.go": "sha256:e92f008b6a487a47b8ee2b98011a3a5b821838a61be9c4db4d56cda600e6fda0",
|
||||
"src/internal/server/telemetry/emitter_test.go": "sha256:29fae2460deb7219eb8edc725b3701a02d2b341fee62bffb2661a520f5a7381a",
|
||||
"src/internal/server/telemetry/events.go": "sha256:3f9df9822649ae2701c3bbf475c8e6d600bc60b7ccdea4d0e54193162d5b0167",
|
||||
"src/internal/server/readiness/readiness_test.go": "sha256:10163e0b1d2f41f336d61782c3bfffdd1116a8459247cc933ffb40be645184c6",
|
||||
"src/internal/server/readiness/readiness.go": "sha256:ffe22b99633b1aa50fc299371398ec0f3fc090c9c8a5f4957c6bfa76b001d073",
|
||||
"src/internal/server/oidc/discovery_test.go": "sha256:2d0dc24d59497aa5a9aa581f8fd3d9cca300d7e0cb4962907630570d60e7e7f0",
|
||||
"src/internal/server/oidc/tenant_precondition_test.go": "sha256:2f2c0d90d26da4e3a6c9145aec8c9d7121c791b6b993c2fe8893a763a30fa72f",
|
||||
"src/internal/server/oidc/userinfo.go": "sha256:f9c986f7a2c79f27a9970a296e6b493014fed77af2f22ba96c4a457d049c916f",
|
||||
"src/internal/server/oidc/account.go": "sha256:b0e36d8cb5f9b1bf4cee573e46b204800c0e3162a912a122b271def4712ab72a",
|
||||
"src/internal/server/oidc/policy_runtime_test.go": "sha256:856146013d2aaa60836e669c297024923ed8e2856d31a89744a99baadae4a74a",
|
||||
"src/internal/server/oidc/handoff_test.go": "sha256:23268b7f9f103f8a8798a168004580bc8fdb3a175161220dac26480a89f28e02",
|
||||
"src/internal/server/oidc/human_tenant_test.go": "sha256:459664f90d76224958b3dd807d95d7bfcf0fce5f113498bd9886a41be19903a5",
|
||||
"src/internal/server/oidc/discovery.go": "sha256:00e4c831a2b4d29aecf3597a09c943315b87c5834dbcd4912ba5d8d31ab28cd5",
|
||||
"src/internal/server/oidc/jwks.go": "sha256:426d6596979ed6d097d6aaae66b6475d1c97687888c6ab1050c05cb14653ec60",
|
||||
"src/internal/server/oidc/login_session.go": "sha256:efb0fcd066d81d753b7043c9c3d9e74e35cd510ca4304d21a51ba29dec833e1f",
|
||||
"src/internal/server/oidc/hardening_test.go": "sha256:b41d6a987d5507d5160d970d0f2110ce681851fca1f4f7a3a9573163e8cd6f3e",
|
||||
"src/internal/server/oidc/jwks_test.go": "sha256:9100e83e58ce2cc2f6d2f47804ee0ce035ca424b209597a12c4ef1a8896163aa",
|
||||
"src/internal/server/oidc/account_test.go": "sha256:db1f7b8a2989b1d0deffee205ec9f4f35c7472135dcb8a10b5498893d881d763",
|
||||
"src/internal/server/oidc/token_status_discovery_test.go": "sha256:27adb5797afba46ca2b1f16018721f055f82760498088b58b993730be1fa2031",
|
||||
"src/internal/server/oidc/principal_type_guard_test.go": "sha256:b3fa097691f07594616e09e8775512d586a019ccdf8302bf594c1ac94f5f3f40",
|
||||
"src/internal/server/oidc/token_status_http.go": "sha256:a7202d3176454f8ab42c090426e2debcb5cc2c1fc089e79746bb78e5c455af97",
|
||||
"src/internal/server/oidc/native_optional_test.go": "sha256:329907bf7089d8f3ea05baad919d05677b8c3aaa201e6f233795ae9f69c003b5",
|
||||
"src/internal/server/oidc/authorize_test.go": "sha256:61042d55e20a11fb6e1e9e6c054e50c0f629b702ed6d8da281d52529e5c29b0d",
|
||||
"src/internal/server/oidc/session.go": "sha256:48473ebe123285ed21e7738834d9d9168dfc855d911cb9e7ce7e87b069a23a3c",
|
||||
"src/internal/server/oidc/logout.go": "sha256:d4de80f8056e7a65c7a41f244dd38e447a4e04df729760a8a59cb6bb4e063893",
|
||||
"src/internal/server/oidc/optional_mfa_test.go": "sha256:d758848d845c181cf1b18710e6ca24977f6b7576320dcad1f7293a5433c3b529",
|
||||
"src/internal/server/oidc/token.go": "sha256:e6905bfa0474dde6122a83a6627d6b75a4c3a7730fb26106546e7a34eef8302b",
|
||||
"src/internal/server/oidc/token_status.go": "sha256:d9c22a71bb2efad7ea240f2689a98f14768ce3c1519d1d8b4e78ee7af2dc70b3",
|
||||
"src/internal/server/oidc/userinfo_test.go": "sha256:e88c42c7cdf50d5953c9f594f0b99b4f87f5e223ba1cb44b70d696972016da80",
|
||||
"src/internal/server/oidc/signin_diagnosis_test.go": "sha256:97e80be67c480dfe8eef0683f25868a91a0fdc96472b5296b734b56274270b3e",
|
||||
"src/internal/server/oidc/token_pair_test.go": "sha256:efeb929becf61d62111286ead4d3635c1a59bfd34423280084ccfec2169a2b1d",
|
||||
"src/internal/server/oidc/policy_isolation_test.go": "sha256:35cb9870f5942c61c0e8c6ca875e9080bbd71c46c7b20fb18da01be88e0fa077",
|
||||
"src/internal/server/oidc/token_test.go": "sha256:5721617c6e5eb79a3b5e86efa63e9278a8d81a0d7c01bf7b184255c102848e33",
|
||||
"src/internal/server/oidc/audience_test.go": "sha256:1b751e1aafb5dbdcb751b42b001e32ddfaccfa3d5ba9d5af67b44148d9802ae3",
|
||||
"src/internal/server/oidc/token_status_transport_test.go": "sha256:3a9945f0292125e86d0014b5245a20c0475d6c518eccff0f9b936356d8e0b347",
|
||||
"src/internal/server/oidc/tenant_test.go": "sha256:8a3879789589f0c3d6e473ba56dd7d7c86ec6fbdddaafa4b13354f82d5f88713",
|
||||
"src/internal/server/oidc/assurance_time_test.go": "sha256:911f65e8cea87ef867d6bf98199ed55abd29427b9f8d0838977d228e6186b08a",
|
||||
"src/internal/server/oidc/signin_diagnosis.go": "sha256:b7976a1cfe1d8d1dca890cf0762cc82d08978a2e7537e45988b147ed0222042f",
|
||||
"src/internal/server/oidc/authorize.go": "sha256:04f9bfb6b146ecf869161b7a0eb00b6486f424710c2c867e4f4b70af5f44ff5e",
|
||||
"src/internal/server/oidc/handoff.go": "sha256:a2ffede12c9140a6c7d14632122a39ffbd0e707cd357dcc11d3ad885dc2a7fa4",
|
||||
"src/internal/server/oidc/token_status_internal_test.go": "sha256:3d3ab41987c7586bd53af8b5c36ff6667b5b2a32d59789915eb9a36b7ceeba53",
|
||||
"src/internal/server/oidc/token_status_test.go": "sha256:e2613d44deefcbfbd0ce1db5ce2588849fb4be9b8bed33cb538171cc29f63e90",
|
||||
"src/internal/migration/lldapexport/exporter.go": "sha256:cb041827c9c0edf9da1338e966e7722e3765065582933b8c17b0d9008d44d103",
|
||||
"src/internal/migration/lldapexport/exporter_test.go": "sha256:6f5cf0e43ea8aea9ceeb6d4d3ae0bfd9f3a5d18636dd49bea0c574c5253a44c2",
|
||||
"src/internal/migration/tokeycloak/transformer_test.go": "sha256:5c3f5145a37079262db805f5bedaabe1d42edf2ccec1788c09c02f5b8336639b",
|
||||
"src/internal/migration/tokeycloak/transformer.go": "sha256:856e26fd2075f020ef02373b5b7e9e5a1e98473b5caca95b60b7983287fc10aa",
|
||||
"src/internal/migration/toldap/generator.go": "sha256:e59ba05203145f52818b3f4e34ab62126481faa9dde697f643fc1cda0638bb46",
|
||||
"src/internal/migration/toldap/generator_test.go": "sha256:73b0969ce2e2cb018aa5734714c86152d1695f0099a5caf7dd96b773717fc2cc",
|
||||
"src/internal/adapters/tenantengine/adapter.go": "sha256:a54ef12fafd2efc24c547d562e0a558a7ecef586394e79f7de345dc8cbbbbef3",
|
||||
"src/internal/adapters/tenantengine/adapter_test.go": "sha256:b9c2db04bc187e6e6e56d4a42d4e657b9e56fef06843500fb4640477dee553e0",
|
||||
"src/internal/adapters/lldap/adapter.go": "sha256:e7df7a159e9dd33a97932fd8cbde49a61091b8722796bf54ae4be887b4a3f5ef",
|
||||
"src/internal/adapters/lldap/railiance_admin_test.go": "sha256:63db481bb6e592e91bf4a555e75065fbd3bd8cd408a7967a4b7786e12bef5d5f",
|
||||
"src/internal/adapters/lldap/adapter_test.go": "sha256:650394ccc25d54c73c03746977a1880b1ed905634da8382da0ecf35843454081",
|
||||
"src/internal/adapters/lldap/config_internal_test.go": "sha256:cbc71ce9a795b73c40b2714153eb2ee4d2e4007f7f56a7882eb862c23868cb68",
|
||||
"src/internal/adapters/lldap/config.go": "sha256:33059a27418b00e1987b7ced0b21dd39c1765338bf575fe2333ce3e606e5546b",
|
||||
"src/internal/adapters/privacyidea/credential_file_test.go": "sha256:9db15562c6fdf01e0806bbc478915413b17adb63c7e9ba213acfc4f9b2b2f0b6",
|
||||
"src/internal/adapters/privacyidea/read_scope_test.go": "sha256:4685e1b84dad044db12708ecd231d4f91ec22ac45901ed2b5177e3be17d8cdc3",
|
||||
"src/internal/adapters/privacyidea/adapter.go": "sha256:2559c68b5aa22bc5f61961e47d696b376f8bede533be03715ffc80c0d89ed904",
|
||||
"src/internal/adapters/privacyidea/adapter_test.go": "sha256:6257489b9c51270da2c96f165c0efd1750b273218df51dca907d29c64c98b349",
|
||||
"src/internal/adapters/privacyidea/optional_mfa_test.go": "sha256:e2e415c671d1d0e7f30a0ce22f5ec58914d8e366b12e445e13b1d645a9ca6a65",
|
||||
"src/internal/adapters/privacyidea/enrollment_state_test.go": "sha256:dcf24c486629fa4835cddaa786af31ec78c05907694d4d93bce0f32876806377",
|
||||
"src/internal/adapters/privacyidea/config.go": "sha256:8dd769b18c04c562714794484630acdbda81b34ab1cfaf9cc7445513207e5021",
|
||||
"src/internal/adapters/authelia/verify.go": "sha256:189f9bc18b8d4319d2d88ee76a76e3b86468ebafde85ef460a4bafb7b350b4c4",
|
||||
"src/internal/adapters/authelia/adapter.go": "sha256:2887bd546f43a16338547dab5ce4a90850c670ed6f6c0d610a5d08c72c7f3f45",
|
||||
"src/internal/adapters/authelia/adapter_test.go": "sha256:d450b37e2b2405c189d444e50f0612e64177d6698cb4665a06ad967ae2224f93",
|
||||
"src/internal/adapters/authelia/issuer_probe.go": "sha256:5846e2b514077a572611b898f449c04b096b086aeb724da055b2416994a7a7bb",
|
||||
"src/internal/adapters/authelia/issuer_probe_test.go": "sha256:a85e02399f8a5c81401dbf159e8054420bff56e08c8a98e8026cad8d31fcecec",
|
||||
"src/internal/adapters/authelia/config.go": "sha256:1b85ea3ffd324bced1ba108492ec780af8ba2b015d22cd51b294afaf36f2ec55",
|
||||
"src/internal/adapters/authelia/verify_test.go": "sha256:f1570604d5038e49c14791f4d1aa278ad76a7e90294e82813d2c21e6a80a5511",
|
||||
"src/cmd/keycape-to-keycloak/main.go": "sha256:6141d3f86bbe3df2a814a0c9995e537d32bf7b19f5549e89d40974eb3270faf7",
|
||||
"src/cmd/lldap-to-ldap/main.go": "sha256:adcc9481d009cad909bd55a71d95c17ce30e29ca63e88a7c9e8101b92d4b872e",
|
||||
"src/cmd/keycape/clients_test.go": "sha256:6a3982c1b2bd9b15c387ba64d4c87b37304dc6f615ed2ab22eec888f620cb1da",
|
||||
"src/cmd/keycape/checkconfig.go": "sha256:2c47c9aedf3457627fdec991703e3be50e5ef554e4104cf3741f8347687279f2",
|
||||
"src/cmd/keycape/main.go": "sha256:553094de91cfafb021b7607eac1263fa155fedeb32c1e99e7c71e8d86e20ffd0",
|
||||
"src/cmd/keycape/checkconfig_test.go": "sha256:06955c996774109b26e4751ab20787f12718a1de96e3483a775fb19688d90e66",
|
||||
"src/cmd/validator/main.go": "sha256:e53b0a7860537dfb1cec69a00cdafe7073b35802421b5977f2fece4de01b9c1f",
|
||||
"src/cmd/lldap-export/main_test.go": "sha256:f105e490c717a414a5a3d1e65ceb4edcf74341ad7713b0accf8117a9b1ee02d2",
|
||||
"src/cmd/lldap-export/main.go": "sha256:f019a6dbb0849d06d5c3729874293d520478ca2887929a0595b59a324097ae75"
|
||||
},
|
||||
"counts_including_subtests": {
|
||||
"pass": 726,
|
||||
"fail": 0,
|
||||
"skip": 6
|
||||
},
|
||||
"failures": [],
|
||||
"skips": [
|
||||
"keycape/tests/conformance:TestIssuerAdvertisesItself",
|
||||
"keycape/tests/conformance:TestProfileAuthorizationSurface",
|
||||
"keycape/tests/conformance:TestPublishedKeysAreUsable",
|
||||
"keycape/tests/conformance:TestExcludedGrantsAreNotAdvertised",
|
||||
"keycape/tests/conformance:TestServiceTokenVerifiesAgainstPublishedKeys",
|
||||
"keycape/internal/server/oidc:TestNativeOptionalEnrollmentAndOldSession"
|
||||
],
|
||||
"source": "/tmp/custodian-keycape-token-pair-r1/source",
|
||||
"native_acceptance": false,
|
||||
"credentials_accessed": false,
|
||||
"network": "private empty namespace; fixture loopback only",
|
||||
"home": "new empty sandbox HOME"
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,218 @@
|
|||
{
|
||||
"returncode": 0,
|
||||
"seconds": 9.36,
|
||||
"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-keycape-token-pair-r1",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod",
|
||||
"/modules",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
|
||||
"/toolchain",
|
||||
"--bind",
|
||||
"/tmp/custodian-keycape-token-status-r1/gocache",
|
||||
"/work/gocache",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-current-token-r1/forgejo-oauth2.test",
|
||||
"/forgejo-oauth2.test",
|
||||
"--chdir",
|
||||
"/work/source/src",
|
||||
"--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",
|
||||
"1",
|
||||
"--setenv",
|
||||
"FORGEJO_CURRENT_TEST_BINARY",
|
||||
"/forgejo-oauth2.test",
|
||||
"--setenv",
|
||||
"FORGEJO_CURRENT_REPORT",
|
||||
"/work/forgejo-current-result.json",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"4",
|
||||
"-race",
|
||||
"-json",
|
||||
"-count=1",
|
||||
"-tags",
|
||||
"forgejo_current_fixture",
|
||||
"-run",
|
||||
"^TestOnlineActualForgejoCallbackCurrentIssuer$",
|
||||
"./internal/authclient"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"src/tests/negative/negative_test.go": "sha256:757485bf1967ed95e872d4a1987b7fd985fbffccd05cf41270b9f4061431de3f",
|
||||
"src/tests/conformance/external_test.go": "sha256:b26bd465e31086f957c9e374b6bee1f0f077b7481eda9daa29cf63816bb87fdb",
|
||||
"src/tests/migration/scenario_c_test.go": "sha256:9722146ac224768aff85476acbd83f3d478c1e060e5a3c6f99b1786858879b80",
|
||||
"src/tests/migration/fixtures_test.go": "sha256:3fbcb8f531fa206225f719a67b744b2ecb908095fbae0538432796697971e6de",
|
||||
"src/tests/migration/scenario_b_test.go": "sha256:40ae7239d116f3e284c767570d39e1f32d44fc29b6fa37ea8835987027942ac0",
|
||||
"src/tests/profile/profile_test.go": "sha256:e5ab4568b15f681a13c9295b17907ca3872ee33b702bb56aea749f17758d29e6",
|
||||
"src/internal/domain/assurance_test.go": "sha256:87680e7afd47294dadc13092ca2bb7c47b9625c80e48a701765c00df3293d755",
|
||||
"src/internal/domain/conformance_test.go": "sha256:6420ec21d4e7a97f60902e1f0debbaf20df91aeac6ab110629b82a67f4c5c9a1",
|
||||
"src/internal/domain/repository.go": "sha256:28f6d8f7f3d822e73f164af5e877f252db7dc6faa97f0ebf75d76aad64078011",
|
||||
"src/internal/domain/auth.go": "sha256:ae800c5b1f58408147dd28a5c87ab2603b1537500a289397e711a4cb0e132cee",
|
||||
"src/internal/domain/model.go": "sha256:ce4309fc7426c66e4ebc18022d2f704c10ebbec243cc98e657d1de765ad09029",
|
||||
"src/internal/domain/assurance.go": "sha256:d3a29ece7c4fa4d9daa8b5fda89cbe0dd1ffef2ee2ecae0051ee68010edb8fd7",
|
||||
"src/internal/domain/mfa.go": "sha256:1b7875b08a0f280a20a14a4fa4fdb4fc84c3942795b1d1417943d32634121b87",
|
||||
"src/internal/authclient/cli.go": "sha256:009c9ce900524b62483cf4955f3cbacc0dcb9027b8da77924b51b22e3fa5170c",
|
||||
"src/internal/authclient/forgejo_current_fixture_test.go": "sha256:5456d007ad2a3288e871a8d1ebbc6d533ee374bd3c76d5df3829b893b814bf32",
|
||||
"src/internal/authclient/client.go": "sha256:61fb150349c5d3714450a90834c59af9a579a8a649c4149297a402cfcf6e39c5",
|
||||
"src/internal/authclient/online_cli.go": "sha256:6763c1e85396726b044067e77179ee2220e397bbd3888ec8de071c2826c6c7f2",
|
||||
"src/internal/authclient/verify.go": "sha256:804735be927ba7d0228ac378056730040d3ccf2ed020b4da0714085bae972f25",
|
||||
"src/internal/authclient/online.go": "sha256:182ec3971ded3a7f3923aab7a23b7317de3ec0f657b8da6d9d882a43bcb0961a",
|
||||
"src/internal/authclient/online_export_test.go": "sha256:5695f233e4caf581e494087cb203a74613d39a5626bbcc323b9f2d553a96b7ba",
|
||||
"src/internal/authclient/client_test.go": "sha256:23642488fcff45e369d22ba0000c47e0e450c5f92560e643ab96303e2a2f43aa",
|
||||
"src/internal/authclient/online_cli_test.go": "sha256:5408a183beda6a885ebae00d3fa420d3fb0d51e4aeb72965fef535d822270c4f",
|
||||
"src/internal/authclient/online_test.go": "sha256:6288329ca2576f72a361256050c94f9d1995e9fbe53b2a6318da2954bcbc7c6a",
|
||||
"src/internal/authclient/online_human_test.go": "sha256:e501c30b6d710a6dea89559ccf8b98de68502c2ee58d1778a56e97f1b7f3b6e0",
|
||||
"src/internal/authclient/verify_test.go": "sha256:7febc6698c87eac5fd8d2520c4f77c68e82f18a2aafa448cfa4990b96fdc46fc",
|
||||
"src/internal/validator/report.go": "sha256:6bc4d31f932ea9a32020cf33203978906730dfe3601350ded0206ff6c8c297a8",
|
||||
"src/internal/validator/validator_test.go": "sha256:8216027a464eab4167d8e87b4d27d130854d7b9cd4cc8a1b6f1a47ff1f9a2276",
|
||||
"src/internal/validator/validator.go": "sha256:3d0a8bf8ebbe54e21e162c8f7df6e4a4d69ab997a01969b74dcbd53decdf559f",
|
||||
"src/internal/config/account_test.go": "sha256:c6b75097d14dd42415c836407fa2a509857cdd2b771fb7e53d4507cbad2f4b1d",
|
||||
"src/internal/config/validate.go": "sha256:2b1766979b817802f43bdd0f999ac61452d86cdfafc1539a5d6be896b900a405",
|
||||
"src/internal/config/optional_mfa_test.go": "sha256:03a00bc309943adbed68bdb9bf47facb6b3b50ef380fd49143b5ed54d684f63e",
|
||||
"src/internal/config/config_test.go": "sha256:af8602010985a0a5a6b4b2bf779f824e7eb5141e6bc2e8d8fcf600b964955100",
|
||||
"src/internal/config/token_status_test.go": "sha256:b2b2ec13215c9661d79142ea698cb8b080f6e017de050bedebf3eb8d16df2ad5",
|
||||
"src/internal/config/config.go": "sha256:19cd234757f8908880d28b8b40939367dfab1322e0401e4e563d56cd3af06683",
|
||||
"src/internal/errors/taxonomy.go": "sha256:7d60f30a4ff0c9a21a3942451eb0292320a6346d3d81508e414a7dfbdc3c666c",
|
||||
"src/internal/errors/taxonomy_test.go": "sha256:6c0bdf1f8e90ea343d88f57f80f6c916c92428a9ac2191a518ee7b70df0eed45",
|
||||
"src/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"src/internal/jose/jose_test.go": "sha256:cbbcbac620b27c8e93d9dc3fefc3ca4fcd20ab58c6bc8f79eb041b980aaa9a43",
|
||||
"src/internal/server/policy/policy_test.go": "sha256:25644557ca532b608dff4e02b202c5c3b7c423fb31e98ad0609febf3660183a2",
|
||||
"src/internal/server/policy/policy.go": "sha256:015d0b42419c501a46171d09a8f7d588dd517d4c98ad14344ff52113294e0fe7",
|
||||
"src/internal/server/errors/enforcement.go": "sha256:f644546b1423992518fc4078ab561ab68fea1fcff00b734795af6652ae3dd4e2",
|
||||
"src/internal/server/errors/enforcement_test.go": "sha256:b4ea0c9a3aaebd66b0b0fcc73f544c76d5a377ac0c9561ccc2638a236844374c",
|
||||
"src/internal/server/telemetry/emitter.go": "sha256:e92f008b6a487a47b8ee2b98011a3a5b821838a61be9c4db4d56cda600e6fda0",
|
||||
"src/internal/server/telemetry/emitter_test.go": "sha256:29fae2460deb7219eb8edc725b3701a02d2b341fee62bffb2661a520f5a7381a",
|
||||
"src/internal/server/telemetry/events.go": "sha256:3f9df9822649ae2701c3bbf475c8e6d600bc60b7ccdea4d0e54193162d5b0167",
|
||||
"src/internal/server/readiness/readiness_test.go": "sha256:10163e0b1d2f41f336d61782c3bfffdd1116a8459247cc933ffb40be645184c6",
|
||||
"src/internal/server/readiness/readiness.go": "sha256:ffe22b99633b1aa50fc299371398ec0f3fc090c9c8a5f4957c6bfa76b001d073",
|
||||
"src/internal/server/oidc/discovery_test.go": "sha256:2d0dc24d59497aa5a9aa581f8fd3d9cca300d7e0cb4962907630570d60e7e7f0",
|
||||
"src/internal/server/oidc/tenant_precondition_test.go": "sha256:2f2c0d90d26da4e3a6c9145aec8c9d7121c791b6b993c2fe8893a763a30fa72f",
|
||||
"src/internal/server/oidc/userinfo.go": "sha256:f9c986f7a2c79f27a9970a296e6b493014fed77af2f22ba96c4a457d049c916f",
|
||||
"src/internal/server/oidc/account.go": "sha256:b0e36d8cb5f9b1bf4cee573e46b204800c0e3162a912a122b271def4712ab72a",
|
||||
"src/internal/server/oidc/policy_runtime_test.go": "sha256:856146013d2aaa60836e669c297024923ed8e2856d31a89744a99baadae4a74a",
|
||||
"src/internal/server/oidc/handoff_test.go": "sha256:23268b7f9f103f8a8798a168004580bc8fdb3a175161220dac26480a89f28e02",
|
||||
"src/internal/server/oidc/human_tenant_test.go": "sha256:459664f90d76224958b3dd807d95d7bfcf0fce5f113498bd9886a41be19903a5",
|
||||
"src/internal/server/oidc/discovery.go": "sha256:00e4c831a2b4d29aecf3597a09c943315b87c5834dbcd4912ba5d8d31ab28cd5",
|
||||
"src/internal/server/oidc/jwks.go": "sha256:426d6596979ed6d097d6aaae66b6475d1c97687888c6ab1050c05cb14653ec60",
|
||||
"src/internal/server/oidc/login_session.go": "sha256:efb0fcd066d81d753b7043c9c3d9e74e35cd510ca4304d21a51ba29dec833e1f",
|
||||
"src/internal/server/oidc/hardening_test.go": "sha256:b41d6a987d5507d5160d970d0f2110ce681851fca1f4f7a3a9573163e8cd6f3e",
|
||||
"src/internal/server/oidc/jwks_test.go": "sha256:9100e83e58ce2cc2f6d2f47804ee0ce035ca424b209597a12c4ef1a8896163aa",
|
||||
"src/internal/server/oidc/account_test.go": "sha256:db1f7b8a2989b1d0deffee205ec9f4f35c7472135dcb8a10b5498893d881d763",
|
||||
"src/internal/server/oidc/token_status_discovery_test.go": "sha256:27adb5797afba46ca2b1f16018721f055f82760498088b58b993730be1fa2031",
|
||||
"src/internal/server/oidc/principal_type_guard_test.go": "sha256:b3fa097691f07594616e09e8775512d586a019ccdf8302bf594c1ac94f5f3f40",
|
||||
"src/internal/server/oidc/token_status_http.go": "sha256:a7202d3176454f8ab42c090426e2debcb5cc2c1fc089e79746bb78e5c455af97",
|
||||
"src/internal/server/oidc/native_optional_test.go": "sha256:329907bf7089d8f3ea05baad919d05677b8c3aaa201e6f233795ae9f69c003b5",
|
||||
"src/internal/server/oidc/authorize_test.go": "sha256:61042d55e20a11fb6e1e9e6c054e50c0f629b702ed6d8da281d52529e5c29b0d",
|
||||
"src/internal/server/oidc/session.go": "sha256:48473ebe123285ed21e7738834d9d9168dfc855d911cb9e7ce7e87b069a23a3c",
|
||||
"src/internal/server/oidc/logout.go": "sha256:d4de80f8056e7a65c7a41f244dd38e447a4e04df729760a8a59cb6bb4e063893",
|
||||
"src/internal/server/oidc/optional_mfa_test.go": "sha256:d758848d845c181cf1b18710e6ca24977f6b7576320dcad1f7293a5433c3b529",
|
||||
"src/internal/server/oidc/token.go": "sha256:e6905bfa0474dde6122a83a6627d6b75a4c3a7730fb26106546e7a34eef8302b",
|
||||
"src/internal/server/oidc/token_status.go": "sha256:d9c22a71bb2efad7ea240f2689a98f14768ce3c1519d1d8b4e78ee7af2dc70b3",
|
||||
"src/internal/server/oidc/userinfo_test.go": "sha256:e88c42c7cdf50d5953c9f594f0b99b4f87f5e223ba1cb44b70d696972016da80",
|
||||
"src/internal/server/oidc/signin_diagnosis_test.go": "sha256:97e80be67c480dfe8eef0683f25868a91a0fdc96472b5296b734b56274270b3e",
|
||||
"src/internal/server/oidc/token_pair_test.go": "sha256:efeb929becf61d62111286ead4d3635c1a59bfd34423280084ccfec2169a2b1d",
|
||||
"src/internal/server/oidc/policy_isolation_test.go": "sha256:35cb9870f5942c61c0e8c6ca875e9080bbd71c46c7b20fb18da01be88e0fa077",
|
||||
"src/internal/server/oidc/token_test.go": "sha256:5721617c6e5eb79a3b5e86efa63e9278a8d81a0d7c01bf7b184255c102848e33",
|
||||
"src/internal/server/oidc/audience_test.go": "sha256:1b751e1aafb5dbdcb751b42b001e32ddfaccfa3d5ba9d5af67b44148d9802ae3",
|
||||
"src/internal/server/oidc/token_status_transport_test.go": "sha256:3a9945f0292125e86d0014b5245a20c0475d6c518eccff0f9b936356d8e0b347",
|
||||
"src/internal/server/oidc/tenant_test.go": "sha256:8a3879789589f0c3d6e473ba56dd7d7c86ec6fbdddaafa4b13354f82d5f88713",
|
||||
"src/internal/server/oidc/assurance_time_test.go": "sha256:911f65e8cea87ef867d6bf98199ed55abd29427b9f8d0838977d228e6186b08a",
|
||||
"src/internal/server/oidc/signin_diagnosis.go": "sha256:b7976a1cfe1d8d1dca890cf0762cc82d08978a2e7537e45988b147ed0222042f",
|
||||
"src/internal/server/oidc/authorize.go": "sha256:04f9bfb6b146ecf869161b7a0eb00b6486f424710c2c867e4f4b70af5f44ff5e",
|
||||
"src/internal/server/oidc/handoff.go": "sha256:a2ffede12c9140a6c7d14632122a39ffbd0e707cd357dcc11d3ad885dc2a7fa4",
|
||||
"src/internal/server/oidc/token_status_internal_test.go": "sha256:3d3ab41987c7586bd53af8b5c36ff6667b5b2a32d59789915eb9a36b7ceeba53",
|
||||
"src/internal/server/oidc/token_status_test.go": "sha256:e2613d44deefcbfbd0ce1db5ce2588849fb4be9b8bed33cb538171cc29f63e90",
|
||||
"src/internal/migration/lldapexport/exporter.go": "sha256:cb041827c9c0edf9da1338e966e7722e3765065582933b8c17b0d9008d44d103",
|
||||
"src/internal/migration/lldapexport/exporter_test.go": "sha256:6f5cf0e43ea8aea9ceeb6d4d3ae0bfd9f3a5d18636dd49bea0c574c5253a44c2",
|
||||
"src/internal/migration/tokeycloak/transformer_test.go": "sha256:5c3f5145a37079262db805f5bedaabe1d42edf2ccec1788c09c02f5b8336639b",
|
||||
"src/internal/migration/tokeycloak/transformer.go": "sha256:856e26fd2075f020ef02373b5b7e9e5a1e98473b5caca95b60b7983287fc10aa",
|
||||
"src/internal/migration/toldap/generator.go": "sha256:e59ba05203145f52818b3f4e34ab62126481faa9dde697f643fc1cda0638bb46",
|
||||
"src/internal/migration/toldap/generator_test.go": "sha256:73b0969ce2e2cb018aa5734714c86152d1695f0099a5caf7dd96b773717fc2cc",
|
||||
"src/internal/adapters/tenantengine/adapter.go": "sha256:a54ef12fafd2efc24c547d562e0a558a7ecef586394e79f7de345dc8cbbbbef3",
|
||||
"src/internal/adapters/tenantengine/adapter_test.go": "sha256:b9c2db04bc187e6e6e56d4a42d4e657b9e56fef06843500fb4640477dee553e0",
|
||||
"src/internal/adapters/lldap/adapter.go": "sha256:e7df7a159e9dd33a97932fd8cbde49a61091b8722796bf54ae4be887b4a3f5ef",
|
||||
"src/internal/adapters/lldap/railiance_admin_test.go": "sha256:63db481bb6e592e91bf4a555e75065fbd3bd8cd408a7967a4b7786e12bef5d5f",
|
||||
"src/internal/adapters/lldap/adapter_test.go": "sha256:650394ccc25d54c73c03746977a1880b1ed905634da8382da0ecf35843454081",
|
||||
"src/internal/adapters/lldap/config_internal_test.go": "sha256:cbc71ce9a795b73c40b2714153eb2ee4d2e4007f7f56a7882eb862c23868cb68",
|
||||
"src/internal/adapters/lldap/config.go": "sha256:33059a27418b00e1987b7ced0b21dd39c1765338bf575fe2333ce3e606e5546b",
|
||||
"src/internal/adapters/privacyidea/credential_file_test.go": "sha256:9db15562c6fdf01e0806bbc478915413b17adb63c7e9ba213acfc4f9b2b2f0b6",
|
||||
"src/internal/adapters/privacyidea/read_scope_test.go": "sha256:4685e1b84dad044db12708ecd231d4f91ec22ac45901ed2b5177e3be17d8cdc3",
|
||||
"src/internal/adapters/privacyidea/adapter.go": "sha256:2559c68b5aa22bc5f61961e47d696b376f8bede533be03715ffc80c0d89ed904",
|
||||
"src/internal/adapters/privacyidea/adapter_test.go": "sha256:6257489b9c51270da2c96f165c0efd1750b273218df51dca907d29c64c98b349",
|
||||
"src/internal/adapters/privacyidea/optional_mfa_test.go": "sha256:e2e415c671d1d0e7f30a0ce22f5ec58914d8e366b12e445e13b1d645a9ca6a65",
|
||||
"src/internal/adapters/privacyidea/enrollment_state_test.go": "sha256:dcf24c486629fa4835cddaa786af31ec78c05907694d4d93bce0f32876806377",
|
||||
"src/internal/adapters/privacyidea/config.go": "sha256:8dd769b18c04c562714794484630acdbda81b34ab1cfaf9cc7445513207e5021",
|
||||
"src/internal/adapters/authelia/verify.go": "sha256:189f9bc18b8d4319d2d88ee76a76e3b86468ebafde85ef460a4bafb7b350b4c4",
|
||||
"src/internal/adapters/authelia/adapter.go": "sha256:2887bd546f43a16338547dab5ce4a90850c670ed6f6c0d610a5d08c72c7f3f45",
|
||||
"src/internal/adapters/authelia/adapter_test.go": "sha256:d450b37e2b2405c189d444e50f0612e64177d6698cb4665a06ad967ae2224f93",
|
||||
"src/internal/adapters/authelia/issuer_probe.go": "sha256:5846e2b514077a572611b898f449c04b096b086aeb724da055b2416994a7a7bb",
|
||||
"src/internal/adapters/authelia/issuer_probe_test.go": "sha256:a85e02399f8a5c81401dbf159e8054420bff56e08c8a98e8026cad8d31fcecec",
|
||||
"src/internal/adapters/authelia/config.go": "sha256:1b85ea3ffd324bced1ba108492ec780af8ba2b015d22cd51b294afaf36f2ec55",
|
||||
"src/internal/adapters/authelia/verify_test.go": "sha256:f1570604d5038e49c14791f4d1aa278ad76a7e90294e82813d2c21e6a80a5511",
|
||||
"src/cmd/keycape-to-keycloak/main.go": "sha256:6141d3f86bbe3df2a814a0c9995e537d32bf7b19f5549e89d40974eb3270faf7",
|
||||
"src/cmd/lldap-to-ldap/main.go": "sha256:adcc9481d009cad909bd55a71d95c17ce30e29ca63e88a7c9e8101b92d4b872e",
|
||||
"src/cmd/keycape/clients_test.go": "sha256:6a3982c1b2bd9b15c387ba64d4c87b37304dc6f615ed2ab22eec888f620cb1da",
|
||||
"src/cmd/keycape/checkconfig.go": "sha256:2c47c9aedf3457627fdec991703e3be50e5ef554e4104cf3741f8347687279f2",
|
||||
"src/cmd/keycape/main.go": "sha256:553094de91cfafb021b7607eac1263fa155fedeb32c1e99e7c71e8d86e20ffd0",
|
||||
"src/cmd/keycape/checkconfig_test.go": "sha256:06955c996774109b26e4751ab20787f12718a1de96e3483a775fb19688d90e66",
|
||||
"src/cmd/validator/main.go": "sha256:e53b0a7860537dfb1cec69a00cdafe7073b35802421b5977f2fece4de01b9c1f",
|
||||
"src/cmd/lldap-export/main_test.go": "sha256:f105e490c717a414a5a3d1e65ceb4edcf74341ad7713b0accf8117a9b1ee02d2",
|
||||
"src/cmd/lldap-export/main.go": "sha256:f019a6dbb0849d06d5c3729874293d520478ca2887929a0595b59a324097ae75"
|
||||
},
|
||||
"consumer_test_binary_sha256": "sha256:8052b5598371a595388c32c46ed9815349b3285f3ed864ec3dece75fafa8852e",
|
||||
"real_credentials": false,
|
||||
"native_admission": false
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{"Time":"2026-10-18T10:04:26.152182424Z","Action":"start","Package":"keycape/internal/authclient"}
|
||||
{"Time":"2026-10-18T10:04:26.222220538Z","Action":"run","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer"}
|
||||
{"Time":"2026-10-18T10:04:26.222388827Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":"=== RUN TestOnlineActualForgejoCallbackCurrentIssuer\n"}
|
||||
{"Time":"2026-10-18T10:04:28.845378886Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":"--- PASS: TestOnlineActualForgejoCallbackCurrentIssuer (2.62s)\n"}
|
||||
{"Time":"2026-10-18T10:04:28.845484388Z","Action":"pass","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Elapsed":2.62}
|
||||
{"Time":"2026-10-18T10:04:28.846007181Z","Action":"output","Package":"keycape/internal/authclient","Output":"PASS\n"}
|
||||
{"Time":"2026-10-18T10:04:29.857213576Z","Action":"output","Package":"keycape/internal/authclient","Output":"ok \tkeycape/internal/authclient\t3.702s\n"}
|
||||
{"Time":"2026-10-18T10:04:29.857385445Z","Action":"pass","Package":"keycape/internal/authclient","Elapsed":3.705}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
{
|
||||
"returncode": 1,
|
||||
"seconds": 2.69,
|
||||
"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-keycape-token-pair-before-r1",
|
||||
"/work",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod",
|
||||
"/modules",
|
||||
"--ro-bind",
|
||||
"/home/worsch/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.7.linux-amd64",
|
||||
"/toolchain",
|
||||
"--bind",
|
||||
"/tmp/custodian-keycape-token-status-r1/gocache",
|
||||
"/work/gocache",
|
||||
"--ro-bind",
|
||||
"/tmp/custodian-forgejo-current-token-r1/forgejo-oauth2.test",
|
||||
"/forgejo-oauth2.test",
|
||||
"--chdir",
|
||||
"/work/source/src",
|
||||
"--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",
|
||||
"1",
|
||||
"--setenv",
|
||||
"FORGEJO_CURRENT_TEST_BINARY",
|
||||
"/forgejo-oauth2.test",
|
||||
"--setenv",
|
||||
"FORGEJO_CURRENT_REPORT",
|
||||
"/work/forgejo-current-result.json",
|
||||
"--",
|
||||
"go",
|
||||
"test",
|
||||
"-p",
|
||||
"4",
|
||||
"-race",
|
||||
"-json",
|
||||
"-count=1",
|
||||
"-tags",
|
||||
"forgejo_current_fixture",
|
||||
"-run",
|
||||
"^TestOnlineActualForgejoCallbackCurrentIssuer$",
|
||||
"./internal/authclient"
|
||||
],
|
||||
"source_sha256_at_start": {
|
||||
"src/tests/negative/negative_test.go": "sha256:757485bf1967ed95e872d4a1987b7fd985fbffccd05cf41270b9f4061431de3f",
|
||||
"src/tests/conformance/external_test.go": "sha256:b26bd465e31086f957c9e374b6bee1f0f077b7481eda9daa29cf63816bb87fdb",
|
||||
"src/tests/migration/scenario_c_test.go": "sha256:9722146ac224768aff85476acbd83f3d478c1e060e5a3c6f99b1786858879b80",
|
||||
"src/tests/migration/fixtures_test.go": "sha256:3fbcb8f531fa206225f719a67b744b2ecb908095fbae0538432796697971e6de",
|
||||
"src/tests/migration/scenario_b_test.go": "sha256:40ae7239d116f3e284c767570d39e1f32d44fc29b6fa37ea8835987027942ac0",
|
||||
"src/tests/profile/profile_test.go": "sha256:e5ab4568b15f681a13c9295b17907ca3872ee33b702bb56aea749f17758d29e6",
|
||||
"src/internal/domain/assurance_test.go": "sha256:87680e7afd47294dadc13092ca2bb7c47b9625c80e48a701765c00df3293d755",
|
||||
"src/internal/domain/conformance_test.go": "sha256:6420ec21d4e7a97f60902e1f0debbaf20df91aeac6ab110629b82a67f4c5c9a1",
|
||||
"src/internal/domain/repository.go": "sha256:28f6d8f7f3d822e73f164af5e877f252db7dc6faa97f0ebf75d76aad64078011",
|
||||
"src/internal/domain/auth.go": "sha256:ae800c5b1f58408147dd28a5c87ab2603b1537500a289397e711a4cb0e132cee",
|
||||
"src/internal/domain/model.go": "sha256:ce4309fc7426c66e4ebc18022d2f704c10ebbec243cc98e657d1de765ad09029",
|
||||
"src/internal/domain/assurance.go": "sha256:d3a29ece7c4fa4d9daa8b5fda89cbe0dd1ffef2ee2ecae0051ee68010edb8fd7",
|
||||
"src/internal/domain/mfa.go": "sha256:1b7875b08a0f280a20a14a4fa4fdb4fc84c3942795b1d1417943d32634121b87",
|
||||
"src/internal/authclient/cli.go": "sha256:009c9ce900524b62483cf4955f3cbacc0dcb9027b8da77924b51b22e3fa5170c",
|
||||
"src/internal/authclient/forgejo_current_fixture_test.go": "sha256:5456d007ad2a3288e871a8d1ebbc6d533ee374bd3c76d5df3829b893b814bf32",
|
||||
"src/internal/authclient/client.go": "sha256:61fb150349c5d3714450a90834c59af9a579a8a649c4149297a402cfcf6e39c5",
|
||||
"src/internal/authclient/online_cli.go": "sha256:6763c1e85396726b044067e77179ee2220e397bbd3888ec8de071c2826c6c7f2",
|
||||
"src/internal/authclient/verify.go": "sha256:804735be927ba7d0228ac378056730040d3ccf2ed020b4da0714085bae972f25",
|
||||
"src/internal/authclient/online.go": "sha256:182ec3971ded3a7f3923aab7a23b7317de3ec0f657b8da6d9d882a43bcb0961a",
|
||||
"src/internal/authclient/online_export_test.go": "sha256:5695f233e4caf581e494087cb203a74613d39a5626bbcc323b9f2d553a96b7ba",
|
||||
"src/internal/authclient/client_test.go": "sha256:23642488fcff45e369d22ba0000c47e0e450c5f92560e643ab96303e2a2f43aa",
|
||||
"src/internal/authclient/online_cli_test.go": "sha256:5408a183beda6a885ebae00d3fa420d3fb0d51e4aeb72965fef535d822270c4f",
|
||||
"src/internal/authclient/online_test.go": "sha256:6288329ca2576f72a361256050c94f9d1995e9fbe53b2a6318da2954bcbc7c6a",
|
||||
"src/internal/authclient/online_human_test.go": "sha256:e501c30b6d710a6dea89559ccf8b98de68502c2ee58d1778a56e97f1b7f3b6e0",
|
||||
"src/internal/authclient/verify_test.go": "sha256:7febc6698c87eac5fd8d2520c4f77c68e82f18a2aafa448cfa4990b96fdc46fc",
|
||||
"src/internal/validator/report.go": "sha256:6bc4d31f932ea9a32020cf33203978906730dfe3601350ded0206ff6c8c297a8",
|
||||
"src/internal/validator/validator_test.go": "sha256:8216027a464eab4167d8e87b4d27d130854d7b9cd4cc8a1b6f1a47ff1f9a2276",
|
||||
"src/internal/validator/validator.go": "sha256:3d0a8bf8ebbe54e21e162c8f7df6e4a4d69ab997a01969b74dcbd53decdf559f",
|
||||
"src/internal/config/account_test.go": "sha256:c6b75097d14dd42415c836407fa2a509857cdd2b771fb7e53d4507cbad2f4b1d",
|
||||
"src/internal/config/validate.go": "sha256:2b1766979b817802f43bdd0f999ac61452d86cdfafc1539a5d6be896b900a405",
|
||||
"src/internal/config/optional_mfa_test.go": "sha256:03a00bc309943adbed68bdb9bf47facb6b3b50ef380fd49143b5ed54d684f63e",
|
||||
"src/internal/config/config_test.go": "sha256:af8602010985a0a5a6b4b2bf779f824e7eb5141e6bc2e8d8fcf600b964955100",
|
||||
"src/internal/config/token_status_test.go": "sha256:b2b2ec13215c9661d79142ea698cb8b080f6e017de050bedebf3eb8d16df2ad5",
|
||||
"src/internal/config/config.go": "sha256:19cd234757f8908880d28b8b40939367dfab1322e0401e4e563d56cd3af06683",
|
||||
"src/internal/errors/taxonomy.go": "sha256:7d60f30a4ff0c9a21a3942451eb0292320a6346d3d81508e414a7dfbdc3c666c",
|
||||
"src/internal/errors/taxonomy_test.go": "sha256:6c0bdf1f8e90ea343d88f57f80f6c916c92428a9ac2191a518ee7b70df0eed45",
|
||||
"src/internal/jose/jose.go": "sha256:a6de154cd81e34a521b8aac5dce6b45a3cfb015681ed16e2beb8f2a24c72c00d",
|
||||
"src/internal/jose/jose_test.go": "sha256:cbbcbac620b27c8e93d9dc3fefc3ca4fcd20ab58c6bc8f79eb041b980aaa9a43",
|
||||
"src/internal/server/policy/policy_test.go": "sha256:25644557ca532b608dff4e02b202c5c3b7c423fb31e98ad0609febf3660183a2",
|
||||
"src/internal/server/policy/policy.go": "sha256:015d0b42419c501a46171d09a8f7d588dd517d4c98ad14344ff52113294e0fe7",
|
||||
"src/internal/server/errors/enforcement.go": "sha256:f644546b1423992518fc4078ab561ab68fea1fcff00b734795af6652ae3dd4e2",
|
||||
"src/internal/server/errors/enforcement_test.go": "sha256:b4ea0c9a3aaebd66b0b0fcc73f544c76d5a377ac0c9561ccc2638a236844374c",
|
||||
"src/internal/server/telemetry/emitter.go": "sha256:e92f008b6a487a47b8ee2b98011a3a5b821838a61be9c4db4d56cda600e6fda0",
|
||||
"src/internal/server/telemetry/emitter_test.go": "sha256:29fae2460deb7219eb8edc725b3701a02d2b341fee62bffb2661a520f5a7381a",
|
||||
"src/internal/server/telemetry/events.go": "sha256:3f9df9822649ae2701c3bbf475c8e6d600bc60b7ccdea4d0e54193162d5b0167",
|
||||
"src/internal/server/readiness/readiness_test.go": "sha256:10163e0b1d2f41f336d61782c3bfffdd1116a8459247cc933ffb40be645184c6",
|
||||
"src/internal/server/readiness/readiness.go": "sha256:ffe22b99633b1aa50fc299371398ec0f3fc090c9c8a5f4957c6bfa76b001d073",
|
||||
"src/internal/server/oidc/discovery_test.go": "sha256:2d0dc24d59497aa5a9aa581f8fd3d9cca300d7e0cb4962907630570d60e7e7f0",
|
||||
"src/internal/server/oidc/tenant_precondition_test.go": "sha256:2f2c0d90d26da4e3a6c9145aec8c9d7121c791b6b993c2fe8893a763a30fa72f",
|
||||
"src/internal/server/oidc/userinfo.go": "sha256:f9c986f7a2c79f27a9970a296e6b493014fed77af2f22ba96c4a457d049c916f",
|
||||
"src/internal/server/oidc/account.go": "sha256:b0e36d8cb5f9b1bf4cee573e46b204800c0e3162a912a122b271def4712ab72a",
|
||||
"src/internal/server/oidc/policy_runtime_test.go": "sha256:856146013d2aaa60836e669c297024923ed8e2856d31a89744a99baadae4a74a",
|
||||
"src/internal/server/oidc/handoff_test.go": "sha256:23268b7f9f103f8a8798a168004580bc8fdb3a175161220dac26480a89f28e02",
|
||||
"src/internal/server/oidc/human_tenant_test.go": "sha256:459664f90d76224958b3dd807d95d7bfcf0fce5f113498bd9886a41be19903a5",
|
||||
"src/internal/server/oidc/discovery.go": "sha256:00e4c831a2b4d29aecf3597a09c943315b87c5834dbcd4912ba5d8d31ab28cd5",
|
||||
"src/internal/server/oidc/jwks.go": "sha256:426d6596979ed6d097d6aaae66b6475d1c97687888c6ab1050c05cb14653ec60",
|
||||
"src/internal/server/oidc/login_session.go": "sha256:efb0fcd066d81d753b7043c9c3d9e74e35cd510ca4304d21a51ba29dec833e1f",
|
||||
"src/internal/server/oidc/hardening_test.go": "sha256:b41d6a987d5507d5160d970d0f2110ce681851fca1f4f7a3a9573163e8cd6f3e",
|
||||
"src/internal/server/oidc/jwks_test.go": "sha256:9100e83e58ce2cc2f6d2f47804ee0ce035ca424b209597a12c4ef1a8896163aa",
|
||||
"src/internal/server/oidc/account_test.go": "sha256:db1f7b8a2989b1d0deffee205ec9f4f35c7472135dcb8a10b5498893d881d763",
|
||||
"src/internal/server/oidc/token_status_discovery_test.go": "sha256:27adb5797afba46ca2b1f16018721f055f82760498088b58b993730be1fa2031",
|
||||
"src/internal/server/oidc/principal_type_guard_test.go": "sha256:b3fa097691f07594616e09e8775512d586a019ccdf8302bf594c1ac94f5f3f40",
|
||||
"src/internal/server/oidc/token_status_http.go": "sha256:a7202d3176454f8ab42c090426e2debcb5cc2c1fc089e79746bb78e5c455af97",
|
||||
"src/internal/server/oidc/native_optional_test.go": "sha256:329907bf7089d8f3ea05baad919d05677b8c3aaa201e6f233795ae9f69c003b5",
|
||||
"src/internal/server/oidc/authorize_test.go": "sha256:61042d55e20a11fb6e1e9e6c054e50c0f629b702ed6d8da281d52529e5c29b0d",
|
||||
"src/internal/server/oidc/session.go": "sha256:48473ebe123285ed21e7738834d9d9168dfc855d911cb9e7ce7e87b069a23a3c",
|
||||
"src/internal/server/oidc/logout.go": "sha256:d4de80f8056e7a65c7a41f244dd38e447a4e04df729760a8a59cb6bb4e063893",
|
||||
"src/internal/server/oidc/optional_mfa_test.go": "sha256:d758848d845c181cf1b18710e6ca24977f6b7576320dcad1f7293a5433c3b529",
|
||||
"src/internal/server/oidc/token.go": "sha256:304fa78d79a5436b9174d88991c19c0834356cb5425c6ed369accb85467a402f",
|
||||
"src/internal/server/oidc/token_status.go": "sha256:d9c22a71bb2efad7ea240f2689a98f14768ce3c1519d1d8b4e78ee7af2dc70b3",
|
||||
"src/internal/server/oidc/userinfo_test.go": "sha256:e88c42c7cdf50d5953c9f594f0b99b4f87f5e223ba1cb44b70d696972016da80",
|
||||
"src/internal/server/oidc/signin_diagnosis_test.go": "sha256:97e80be67c480dfe8eef0683f25868a91a0fdc96472b5296b734b56274270b3e",
|
||||
"src/internal/server/oidc/policy_isolation_test.go": "sha256:35cb9870f5942c61c0e8c6ca875e9080bbd71c46c7b20fb18da01be88e0fa077",
|
||||
"src/internal/server/oidc/token_test.go": "sha256:5721617c6e5eb79a3b5e86efa63e9278a8d81a0d7c01bf7b184255c102848e33",
|
||||
"src/internal/server/oidc/audience_test.go": "sha256:1b751e1aafb5dbdcb751b42b001e32ddfaccfa3d5ba9d5af67b44148d9802ae3",
|
||||
"src/internal/server/oidc/token_status_transport_test.go": "sha256:3a9945f0292125e86d0014b5245a20c0475d6c518eccff0f9b936356d8e0b347",
|
||||
"src/internal/server/oidc/tenant_test.go": "sha256:8a3879789589f0c3d6e473ba56dd7d7c86ec6fbdddaafa4b13354f82d5f88713",
|
||||
"src/internal/server/oidc/assurance_time_test.go": "sha256:911f65e8cea87ef867d6bf98199ed55abd29427b9f8d0838977d228e6186b08a",
|
||||
"src/internal/server/oidc/signin_diagnosis.go": "sha256:b7976a1cfe1d8d1dca890cf0762cc82d08978a2e7537e45988b147ed0222042f",
|
||||
"src/internal/server/oidc/authorize.go": "sha256:04f9bfb6b146ecf869161b7a0eb00b6486f424710c2c867e4f4b70af5f44ff5e",
|
||||
"src/internal/server/oidc/handoff.go": "sha256:a2ffede12c9140a6c7d14632122a39ffbd0e707cd357dcc11d3ad885dc2a7fa4",
|
||||
"src/internal/server/oidc/token_status_internal_test.go": "sha256:3d3ab41987c7586bd53af8b5c36ff6667b5b2a32d59789915eb9a36b7ceeba53",
|
||||
"src/internal/server/oidc/token_status_test.go": "sha256:e2613d44deefcbfbd0ce1db5ce2588849fb4be9b8bed33cb538171cc29f63e90",
|
||||
"src/internal/migration/lldapexport/exporter.go": "sha256:cb041827c9c0edf9da1338e966e7722e3765065582933b8c17b0d9008d44d103",
|
||||
"src/internal/migration/lldapexport/exporter_test.go": "sha256:6f5cf0e43ea8aea9ceeb6d4d3ae0bfd9f3a5d18636dd49bea0c574c5253a44c2",
|
||||
"src/internal/migration/tokeycloak/transformer_test.go": "sha256:5c3f5145a37079262db805f5bedaabe1d42edf2ccec1788c09c02f5b8336639b",
|
||||
"src/internal/migration/tokeycloak/transformer.go": "sha256:856e26fd2075f020ef02373b5b7e9e5a1e98473b5caca95b60b7983287fc10aa",
|
||||
"src/internal/migration/toldap/generator.go": "sha256:e59ba05203145f52818b3f4e34ab62126481faa9dde697f643fc1cda0638bb46",
|
||||
"src/internal/migration/toldap/generator_test.go": "sha256:73b0969ce2e2cb018aa5734714c86152d1695f0099a5caf7dd96b773717fc2cc",
|
||||
"src/internal/adapters/tenantengine/adapter.go": "sha256:a54ef12fafd2efc24c547d562e0a558a7ecef586394e79f7de345dc8cbbbbef3",
|
||||
"src/internal/adapters/tenantengine/adapter_test.go": "sha256:b9c2db04bc187e6e6e56d4a42d4e657b9e56fef06843500fb4640477dee553e0",
|
||||
"src/internal/adapters/lldap/adapter.go": "sha256:e7df7a159e9dd33a97932fd8cbde49a61091b8722796bf54ae4be887b4a3f5ef",
|
||||
"src/internal/adapters/lldap/railiance_admin_test.go": "sha256:63db481bb6e592e91bf4a555e75065fbd3bd8cd408a7967a4b7786e12bef5d5f",
|
||||
"src/internal/adapters/lldap/adapter_test.go": "sha256:650394ccc25d54c73c03746977a1880b1ed905634da8382da0ecf35843454081",
|
||||
"src/internal/adapters/lldap/config_internal_test.go": "sha256:cbc71ce9a795b73c40b2714153eb2ee4d2e4007f7f56a7882eb862c23868cb68",
|
||||
"src/internal/adapters/lldap/config.go": "sha256:33059a27418b00e1987b7ced0b21dd39c1765338bf575fe2333ce3e606e5546b",
|
||||
"src/internal/adapters/privacyidea/credential_file_test.go": "sha256:9db15562c6fdf01e0806bbc478915413b17adb63c7e9ba213acfc4f9b2b2f0b6",
|
||||
"src/internal/adapters/privacyidea/read_scope_test.go": "sha256:4685e1b84dad044db12708ecd231d4f91ec22ac45901ed2b5177e3be17d8cdc3",
|
||||
"src/internal/adapters/privacyidea/adapter.go": "sha256:2559c68b5aa22bc5f61961e47d696b376f8bede533be03715ffc80c0d89ed904",
|
||||
"src/internal/adapters/privacyidea/adapter_test.go": "sha256:6257489b9c51270da2c96f165c0efd1750b273218df51dca907d29c64c98b349",
|
||||
"src/internal/adapters/privacyidea/optional_mfa_test.go": "sha256:e2e415c671d1d0e7f30a0ce22f5ec58914d8e366b12e445e13b1d645a9ca6a65",
|
||||
"src/internal/adapters/privacyidea/enrollment_state_test.go": "sha256:dcf24c486629fa4835cddaa786af31ec78c05907694d4d93bce0f32876806377",
|
||||
"src/internal/adapters/privacyidea/config.go": "sha256:8dd769b18c04c562714794484630acdbda81b34ab1cfaf9cc7445513207e5021",
|
||||
"src/internal/adapters/authelia/verify.go": "sha256:189f9bc18b8d4319d2d88ee76a76e3b86468ebafde85ef460a4bafb7b350b4c4",
|
||||
"src/internal/adapters/authelia/adapter.go": "sha256:2887bd546f43a16338547dab5ce4a90850c670ed6f6c0d610a5d08c72c7f3f45",
|
||||
"src/internal/adapters/authelia/adapter_test.go": "sha256:d450b37e2b2405c189d444e50f0612e64177d6698cb4665a06ad967ae2224f93",
|
||||
"src/internal/adapters/authelia/issuer_probe.go": "sha256:5846e2b514077a572611b898f449c04b096b086aeb724da055b2416994a7a7bb",
|
||||
"src/internal/adapters/authelia/issuer_probe_test.go": "sha256:a85e02399f8a5c81401dbf159e8054420bff56e08c8a98e8026cad8d31fcecec",
|
||||
"src/internal/adapters/authelia/config.go": "sha256:1b85ea3ffd324bced1ba108492ec780af8ba2b015d22cd51b294afaf36f2ec55",
|
||||
"src/internal/adapters/authelia/verify_test.go": "sha256:f1570604d5038e49c14791f4d1aa278ad76a7e90294e82813d2c21e6a80a5511",
|
||||
"src/cmd/keycape-to-keycloak/main.go": "sha256:6141d3f86bbe3df2a814a0c9995e537d32bf7b19f5549e89d40974eb3270faf7",
|
||||
"src/cmd/lldap-to-ldap/main.go": "sha256:adcc9481d009cad909bd55a71d95c17ce30e29ca63e88a7c9e8101b92d4b872e",
|
||||
"src/cmd/keycape/clients_test.go": "sha256:6a3982c1b2bd9b15c387ba64d4c87b37304dc6f615ed2ab22eec888f620cb1da",
|
||||
"src/cmd/keycape/checkconfig.go": "sha256:2c47c9aedf3457627fdec991703e3be50e5ef554e4104cf3741f8347687279f2",
|
||||
"src/cmd/keycape/main.go": "sha256:553094de91cfafb021b7607eac1263fa155fedeb32c1e99e7c71e8d86e20ffd0",
|
||||
"src/cmd/keycape/checkconfig_test.go": "sha256:06955c996774109b26e4751ab20787f12718a1de96e3483a775fb19688d90e66",
|
||||
"src/cmd/validator/main.go": "sha256:e53b0a7860537dfb1cec69a00cdafe7073b35802421b5977f2fece4de01b9c1f",
|
||||
"src/cmd/lldap-export/main_test.go": "sha256:f105e490c717a414a5a3d1e65ceb4edcf74341ad7713b0accf8117a9b1ee02d2",
|
||||
"src/cmd/lldap-export/main.go": "sha256:f019a6dbb0849d06d5c3729874293d520478ca2887929a0595b59a324097ae75"
|
||||
},
|
||||
"consumer_test_binary_sha256": "sha256:8052b5598371a595388c32c46ed9815349b3285f3ed864ec3dece75fafa8852e",
|
||||
"real_credentials": false,
|
||||
"native_admission": false
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{"Time":"2026-10-18T10:02:31.733068893Z","Action":"start","Package":"keycape/internal/authclient"}
|
||||
{"Time":"2026-10-18T10:02:31.754934693Z","Action":"run","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer"}
|
||||
{"Time":"2026-10-18T10:02:31.754993666Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":"=== RUN TestOnlineActualForgejoCallbackCurrentIssuer\n"}
|
||||
{"Time":"2026-10-18T10:02:32.082192499Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":" forgejo_current_fixture_test.go:136: actual consumer phase active failed: === RUN TestAccessEngineLiveFixtureCurrentIssuer\n"}
|
||||
{"Time":"2026-10-18T10:02:32.08225947Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":" current_issuer_fixture_test.go:63: actual issuer/provider/current-token/decision callback did not pass\n"}
|
||||
{"Time":"2026-10-18T10:02:32.082273619Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":" --- FAIL: TestAccessEngineLiveFixtureCurrentIssuer (0.03s)\n"}
|
||||
{"Time":"2026-10-18T10:02:32.082285296Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":" FAIL\n"}
|
||||
{"Time":"2026-10-18T10:02:32.084104641Z","Action":"output","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Output":"--- FAIL: TestOnlineActualForgejoCallbackCurrentIssuer (0.33s)\n"}
|
||||
{"Time":"2026-10-18T10:02:32.084164245Z","Action":"fail","Package":"keycape/internal/authclient","Test":"TestOnlineActualForgejoCallbackCurrentIssuer","Elapsed":0.33}
|
||||
{"Time":"2026-10-18T10:02:32.08447593Z","Action":"output","Package":"keycape/internal/authclient","Output":"FAIL\n"}
|
||||
{"Time":"2026-10-18T10:02:32.089445611Z","Action":"output","Package":"keycape/internal/authclient","Output":"FAIL\tkeycape/internal/authclient\t0.356s\n"}
|
||||
{"Time":"2026-10-18T10:02:32.08949324Z","Action":"fail","Package":"keycape/internal/authclient","Elapsed":0.356}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"owner": "key-cape",
|
||||
"base_revision": "git:08c083bae9ff35fbaf028c36d72a95b57d2541b7",
|
||||
"workspace": "/tmp/custodian-keycape-token-pair-r1/source",
|
||||
"native_admission": false
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"workplan_id": "e2eecd1c-45c7-5a7c-bd1c-d31c845e3d38",
|
||||
"id": "61276a62-746d-42b6-9209-b267c9b6f02e",
|
||||
"topic_id": null,
|
||||
"task_id": "2cade51f-d25a-5719-96b4-b57d50c1b97d",
|
||||
"decision_id": null,
|
||||
"event_type": "note",
|
||||
"summary": "Published KeyCape e2c5a947 exact online human ID/access-grant RS256 at_hash binding, legacy disabled profile unchanged; retains separate ID/resource audiences and no access-only fields in ID token. Full source race suite 726 passes including subtests, six existing native/external skips. Same compiled actual Forgejo provider/callback fails against old issuer and passes five fresh-process actual HTTPS PKCE/discovery/UserInfo/introspection/revocation scenarios against new issuer; mixed/revoked/suspended grants refuse. Synthetic MFA/PDP admission only. All six owner source postimages replay exact; primary/remote and all four task bindings verified. KEY42 independent review/native admission remain open; no native gate closed.",
|
||||
"detail": null,
|
||||
"author": "codex",
|
||||
"session_id": null,
|
||||
"created_at": "2026-10-18T10:27:23.554249Z",
|
||||
"workstream_id": "e2eecd1c-45c7-5a7c-bd1c-d31c845e3d38"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"summary": "Published KeyCape e2c5a947 exact online human ID/access-grant RS256 at_hash binding, legacy disabled profile unchanged; retains separate ID/resource audiences and no access-only fields in ID token. Full source race suite 726 passes including subtests, six existing native/external skips. Same compiled actual Forgejo provider/callback fails against old issuer and passes five fresh-process actual HTTPS PKCE/discovery/UserInfo/introspection/revocation scenarios against new issuer; mixed/revoked/suspended grants refuse. Synthetic MFA/PDP admission only. All six owner source postimages replay exact; primary/remote and all four task bindings verified. KEY42 independent review/native admission remain open; no native gate closed.",
|
||||
"event_type": "note",
|
||||
"author": "codex",
|
||||
"workplan_id": "e2eecd1c-45c7-5a7c-bd1c-d31c845e3d38",
|
||||
"task_id": "2cade51f-d25a-5719-96b4-b57d50c1b97d"
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"owner": "key-cape",
|
||||
"base_revision": "git:08c083bae9ff35fbaf028c36d72a95b57d2541b7",
|
||||
"files": [
|
||||
{
|
||||
"path": "docs/current-token-client.md",
|
||||
"before_sha256": "sha256:69db63087c752bba0b07b41aa7784484703aacc064181782d15de62310751c4e",
|
||||
"sha256": "sha256:68a67cd465185513ce1c5cc3110c7bbada2fc85f2e2df2e09f64049c40ee9966"
|
||||
},
|
||||
{
|
||||
"path": "docs/online-token-status.md",
|
||||
"before_sha256": "sha256:56064b1c0df5ff9a20d5c205396e7b1b2d0d70785ea7945c999d34b4de3aa1b8",
|
||||
"sha256": "sha256:123e3de853782aeb78a0c7b385f884955fc804f4fb7183d40706b34193a43396"
|
||||
},
|
||||
{
|
||||
"path": "src/internal/authclient/forgejo_current_fixture_test.go",
|
||||
"before_sha256": null,
|
||||
"sha256": "sha256:5456d007ad2a3288e871a8d1ebbc6d533ee374bd3c76d5df3829b893b814bf32"
|
||||
},
|
||||
{
|
||||
"path": "src/internal/server/oidc/token.go",
|
||||
"before_sha256": "sha256:304fa78d79a5436b9174d88991c19c0834356cb5425c6ed369accb85467a402f",
|
||||
"sha256": "sha256:e6905bfa0474dde6122a83a6627d6b75a4c3a7730fb26106546e7a34eef8302b"
|
||||
},
|
||||
{
|
||||
"path": "src/internal/server/oidc/token_pair_test.go",
|
||||
"before_sha256": null,
|
||||
"sha256": "sha256:efeb929becf61d62111286ead4d3635c1a59bfd34423280084ccfec2169a2b1d"
|
||||
},
|
||||
{
|
||||
"path": "workplans/KEY-WP-0042-online-access-token-status.md",
|
||||
"before_sha256": "sha256:e8b0d15d7461ea34075b4a7f8047af380d96e521f42a0dae6136f6cfdd052b0f",
|
||||
"sha256": "sha256:ae722ff4fbd8e701c3280d31584ee207f51da7e3f719986e6ce2a052c00f93a3"
|
||||
}
|
||||
],
|
||||
"patch_sha256": "sha256:b26ee48371770d8fc110b77b4e657f58f87cde16d5802b8679c1432c44ec22af",
|
||||
"exact_replay_all_source_bytes": true,
|
||||
"fuzz": 0,
|
||||
"native_admission": false
|
||||
}
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
--- a/docs/current-token-client.md
|
||||
+++ b/docs/current-token-client.md
|
||||
@@ -27,6 +27,13 @@
|
||||
not establish Windows ACL behavior or a broader platform admission.
|
||||
|
||||
## Current status is an authenticated fact
|
||||
+
|
||||
+Online human PKCE ID tokens additionally carry the exact access-token `at_hash`
|
||||
+binding described in `online-token-status.md`. This client verifies access-token
|
||||
+facts; a consumer that also uses an ID token separately authenticates that token,
|
||||
+checks its purpose, and joins its signed hash, identity, issuance, assurance and
|
||||
+tenant provenance to the exact access grant. Neither token substitutes for the
|
||||
+other. The online profile's hash is additive; disabled legacy issuance is unchanged.
|
||||
|
||||
`OnlineClient.Check(ctx, token, ExpectedIdentity{...})` validates the signature,
|
||||
issuer, exact single resource audience, subject, tenant, human/service type,
|
||||
--- a/docs/online-token-status.md
|
||||
+++ b/docs/online-token-status.md
|
||||
@@ -31,7 +31,14 @@
|
||||
authentication methods, and connects the same store to issuance and UserInfo.
|
||||
|
||||
Human PKCE and service grants add a cryptographically random `jti` to each access
|
||||
-token. ID-token claims and existing assurance/tenant provenance remain unchanged.
|
||||
+token. Online human PKCE ID tokens add `at_hash`, binding the exact access token
|
||||
+using the RS256 rule in [OIDC Core 3.1.3.6/8](https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken).
|
||||
+The ID token keeps its relying-party audience and omits access-only `jti`/`scope`;
|
||||
+the access token keeps its registered resource audience and omits `at_hash`.
|
||||
+Existing assurance/tenant provenance and the disabled legacy ID-token contract
|
||||
+remain unchanged. Consumers of this online human profile require the exact signed
|
||||
+hash before treating an ID token and access token as one grant; equal identity
|
||||
+facts or issuance seconds alone cannot distinguish separate grants.
|
||||
Distinct grants in the same second have distinct bearer tokens: a new grant can
|
||||
never remint a revoked token. A successful response is emitted only after the
|
||||
token hash, expiry and current registration commitment have been recorded. No
|
||||
--- /dev/null
|
||||
+++ b/src/internal/authclient/forgejo_current_fixture_test.go
|
||||
@@ -0,0 +1,156 @@
|
||||
+//go:build forgejo_current_fixture
|
||||
+
|
||||
+package authclient
|
||||
+
|
||||
+import (
|
||||
+ "bytes"
|
||||
+ "context"
|
||||
+ "crypto/sha256"
|
||||
+ "crypto/x509"
|
||||
+ "encoding/base64"
|
||||
+ "encoding/json"
|
||||
+ "encoding/pem"
|
||||
+ "net/http"
|
||||
+ "net/http/httptest"
|
||||
+ "net/url"
|
||||
+ "os"
|
||||
+ "os/exec"
|
||||
+ "path/filepath"
|
||||
+ "strings"
|
||||
+ "sync"
|
||||
+ "sync/atomic"
|
||||
+ "testing"
|
||||
+ "time"
|
||||
+
|
||||
+ "keycape/internal/domain"
|
||||
+ "keycape/internal/server/oidc"
|
||||
+ "keycape/internal/server/telemetry"
|
||||
+)
|
||||
+
|
||||
+type forgejoFixtureDirectory struct {
|
||||
+ mu sync.RWMutex
|
||||
+ user domain.User
|
||||
+}
|
||||
+
|
||||
+func (d *forgejoFixtureDirectory) LookupUser(context.Context, string) (*domain.User, error) {
|
||||
+ d.mu.RLock()
|
||||
+ defer d.mu.RUnlock()
|
||||
+ user := d.user
|
||||
+ user.Groups = append([]string(nil), d.user.Groups...)
|
||||
+ return &user, nil
|
||||
+}
|
||||
+func (d *forgejoFixtureDirectory) LookupGroups(context.Context, string) ([]domain.Group, error) {
|
||||
+ return nil, nil
|
||||
+}
|
||||
+func (d *forgejoFixtureDirectory) ListUsers(ctx context.Context) ([]domain.User, error) {
|
||||
+ user, _ := d.LookupUser(ctx, "")
|
||||
+ return []domain.User{*user}, nil
|
||||
+}
|
||||
+func (d *forgejoFixtureDirectory) ValidatePassword(context.Context, string, string) (bool, error) {
|
||||
+ return false, nil
|
||||
+}
|
||||
+
|
||||
+func TestOnlineActualForgejoCallbackCurrentIssuer(t *testing.T) {
|
||||
+ f := onlineProvider(t)
|
||||
+ directory := &forgejoFixtureDirectory{user: domain.User{ID: "uid=operator,ou=people,dc=fixture", Username: "operator", Enabled: true, Tenant: "tenant:fixture", Groups: []string{"forge-admins"}}}
|
||||
+ client := f.issuer.ClientConfig["current-client"]
|
||||
+ client.GrantTypes = []string{"authorization_code"}
|
||||
+ client.ServiceSubject = ""
|
||||
+ client.Audience = client.ClientID
|
||||
+ client.AllowedScopes = []string{"openid", "profile", "groups"}
|
||||
+ client.RedirectURIs = []string{"https://forgejo.invalid/callback"}
|
||||
+ mux := http.NewServeMux()
|
||||
+ server := httptest.NewTLSServer(mux)
|
||||
+ t.Cleanup(server.Close)
|
||||
+ f.issuer.Users = directory
|
||||
+ f.issuer.Issuer = server.URL
|
||||
+ f.status.Users = directory
|
||||
+ f.status.Issuer = server.URL
|
||||
+ userinfo := &oidc.UserinfoHandler{Users: directory, SigningKey: &f.issuer.SigningKey.PublicKey, Issuer: server.URL, Tokens: f.issuer.Tokens, ClientConfig: f.issuer.ClientConfig, Emitter: telemetry.NoopEmitter{}}
|
||||
+ var statusCalls, userinfoCalls atomic.Int64
|
||||
+ mux.Handle("/token", f.issuer)
|
||||
+ mux.HandleFunc("/introspect", func(w http.ResponseWriter, r *http.Request) { statusCalls.Add(1); f.status.Introspect(w, r) })
|
||||
+ mux.HandleFunc("/revoke", f.status.Revoke)
|
||||
+ mux.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) { userinfoCalls.Add(1); userinfo.ServeHTTP(w, r) })
|
||||
+ keys := oidc.NewKeySet()
|
||||
+ keys.AddKey("key-1", &f.issuer.SigningKey.PublicKey)
|
||||
+ mux.Handle("/jwks", oidc.NewJWKSHandler(keys))
|
||||
+ mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
+ w.Header().Set("Content-Type", "application/json")
|
||||
+ _ = json.NewEncoder(w).Encode(map[string]any{"issuer": server.URL, "authorization_endpoint": server.URL + "/authorize", "token_endpoint": server.URL + "/token", "userinfo_endpoint": server.URL + "/userinfo", "jwks_uri": server.URL + "/jwks", "id_token_signing_alg_values_supported": []string{"RS256"}})
|
||||
+ })
|
||||
+ admission := f.client.admission
|
||||
+ admission.Issuer = server.URL
|
||||
+ admission.Audience = client.ClientID
|
||||
+ if os.WriteFile(admission.CAFile, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0644) != nil {
|
||||
+ t.Fatal("fixture CA write failed")
|
||||
+ }
|
||||
+ var err error
|
||||
+ f.client, err = NewOnlineClient(admission)
|
||||
+ if err != nil {
|
||||
+ t.Fatal("fixture admission failed")
|
||||
+ }
|
||||
+ der, err := x509.MarshalPKIXPublicKey(&f.issuer.SigningKey.PublicKey)
|
||||
+ if err != nil {
|
||||
+ t.Fatal("fixture public key failed")
|
||||
+ }
|
||||
+ publicKey := string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))
|
||||
+ issue := func() Tokens {
|
||||
+ verifier := strings.Repeat("v", 43)
|
||||
+ digest := sha256.Sum256([]byte(verifier))
|
||||
+ code := f.issuer.Sessions.Create(&oidc.PKCESession{ClientID: client.ClientID, Username: directory.user.Username, RedirectURI: client.RedirectURIs[0], PKCEChallengeMethod: "S256", PKCEChallenge: base64.RawURLEncoding.EncodeToString(digest[:]), Scopes: client.AllowedScopes, MFAVerified: true, AuthTime: time.Now().Add(-time.Minute).Truncate(time.Second), ExpiresAt: time.Now().Add(time.Minute)})
|
||||
+ form := url.Values{"grant_type": {"authorization_code"}, "client_id": {client.ClientID}, "code": {code}, "code_verifier": {verifier}, "redirect_uri": {client.RedirectURIs[0]}}
|
||||
+ r, _ := http.NewRequest(http.MethodPost, server.URL+"/token", strings.NewReader(form.Encode()))
|
||||
+ r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
+ r.SetBasicAuth(url.QueryEscape(client.ClientID), url.QueryEscape(client.ClientSecret))
|
||||
+ response, err := server.Client().Do(r)
|
||||
+ if err != nil {
|
||||
+ t.Fatal("actual fixture token HTTP failed")
|
||||
+ }
|
||||
+ defer response.Body.Close()
|
||||
+ var tokens Tokens
|
||||
+ if response.StatusCode != 200 || json.NewDecoder(response.Body).Decode(&tokens) != nil || tokens.AccessToken == "" || tokens.IDToken == "" {
|
||||
+ t.Fatal("actual fixture human PKCE failed")
|
||||
+ }
|
||||
+ return tokens
|
||||
+ }
|
||||
+ first, second := issue(), issue()
|
||||
+ directoryPath := t.TempDir()
|
||||
+ var phases []string
|
||||
+ probe := func(tokens Tokens, phase string) {
|
||||
+ input := map[string]any{"id_token": tokens.IDToken, "access_token": tokens.AccessToken, "public_key_pem": publicKey, "tenant": directory.user.Tenant, "phase": phase, "current": admission}
|
||||
+ raw, _ := json.Marshal(input)
|
||||
+ file := filepath.Join(directoryPath, "consumer-input.json")
|
||||
+ if os.WriteFile(file, raw, 0600) != nil {
|
||||
+ t.Fatal("private fixture input failed")
|
||||
+ }
|
||||
+ command := exec.Command(os.Getenv("FORGEJO_CURRENT_TEST_BINARY"), "-test.run=^TestAccessEngineLiveFixtureCurrentIssuer$", "-test.v")
|
||||
+ command.Env = append(os.Environ(), "FORGEJO_CURRENT_ISSUER_FIXTURE="+file)
|
||||
+ output, err := command.CombinedOutput()
|
||||
+ for _, private := range []string{first.AccessToken, first.IDToken, second.AccessToken, second.IDToken, client.ClientSecret} {
|
||||
+ if bytes.Contains(output, []byte(private)) {
|
||||
+ t.Fatal("consumer fixture diagnostic disclosed private input")
|
||||
+ }
|
||||
+ }
|
||||
+ if err != nil {
|
||||
+ t.Fatalf("actual consumer phase %s failed: %s", phase, output)
|
||||
+ }
|
||||
+ phases = append(phases, phase)
|
||||
+ }
|
||||
+ probe(first, "active")
|
||||
+ probe(Tokens{AccessToken: second.AccessToken, IDToken: first.IDToken}, "mixed-pair")
|
||||
+ if _, err := f.client.Revoke(context.Background(), first.AccessToken); err != nil {
|
||||
+ t.Fatal("actual fixture revoke/independent negative failed")
|
||||
+ }
|
||||
+ probe(first, "revoked-after-userinfo")
|
||||
+ probe(second, "active")
|
||||
+ directory.mu.Lock()
|
||||
+ directory.user.Groups = []string{"netkingdom-suspended"}
|
||||
+ directory.mu.Unlock()
|
||||
+ probe(second, "suspended-after-userinfo")
|
||||
+ report := map[string]any{"status": "passed", "consumer_processes": len(phases), "phases": phases, "actual_https_human_PKCE": true, "actual_discovery_and_userinfo": true, "actual_introspection_and_revocation": true, "status_calls": statusCalls.Load(), "userinfo_calls": userinfoCalls.Load(), "actual_forgejo_callback_and_generated_client": true, "synthetic_MFA_session_seed_and_PDP_admission": true, "native_acceptance": false, "private_inputs": "fixture memory/tmpfs only"}
|
||||
+ raw, _ := json.MarshalIndent(report, "", " ")
|
||||
+ if os.WriteFile(os.Getenv("FORGEJO_CURRENT_REPORT"), append(raw, '\n'), 0600) != nil {
|
||||
+ t.Fatal("public fixture result failed")
|
||||
+ }
|
||||
+}
|
||||
--- a/src/internal/server/oidc/token.go
|
||||
+++ b/src/internal/server/oidc/token.go
|
||||
@@ -224,10 +224,17 @@
|
||||
|
||||
// 7. Sign JWT with RSA-SHA256.
|
||||
kid := "key-1" // static kid for v0.1
|
||||
- jwtToken, err := buildJWT(claims, kid, h.SigningKey)
|
||||
- if err != nil {
|
||||
- http.Error(w, "failed to build JWT", http.StatusInternalServerError)
|
||||
- return
|
||||
+ identityClaims := make(map[string]interface{}, len(claims)+1)
|
||||
+ for name, value := range claims {
|
||||
+ identityClaims[name] = value
|
||||
+ }
|
||||
+ var jwtToken string
|
||||
+ if h.Tokens == nil {
|
||||
+ jwtToken, err = buildJWT(identityClaims, kid, h.SigningKey)
|
||||
+ if err != nil {
|
||||
+ http.Error(w, "failed to build JWT", http.StatusInternalServerError)
|
||||
+ return
|
||||
+ }
|
||||
}
|
||||
|
||||
// Access tokens target the statically registered resource server. ID tokens
|
||||
@@ -250,6 +257,16 @@
|
||||
return
|
||||
}
|
||||
if h.Tokens != nil {
|
||||
+ // The online profile binds the signed ID token to this exact access
|
||||
+ // grant, including same-second distinct jtis. OIDC RS256 at_hash uses
|
||||
+ // the base64url-encoded left half of SHA-256 over the access token.
|
||||
+ digest := sha256.Sum256([]byte(accessToken))
|
||||
+ identityClaims["at_hash"] = base64.RawURLEncoding.EncodeToString(digest[:16])
|
||||
+ jwtToken, err = buildJWT(identityClaims, kid, h.SigningKey)
|
||||
+ if err != nil {
|
||||
+ http.Error(w, "failed to build JWT", http.StatusInternalServerError)
|
||||
+ return
|
||||
+ }
|
||||
if err := h.Tokens.register(accessToken, client, time.Unix(exp.Unix(), 0), now); err != nil {
|
||||
http.Error(w, "token status unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
--- /dev/null
|
||||
+++ b/src/internal/server/oidc/token_pair_test.go
|
||||
@@ -0,0 +1,62 @@
|
||||
+package oidc_test
|
||||
+
|
||||
+import (
|
||||
+ "crypto/sha256"
|
||||
+ "encoding/base64"
|
||||
+ "net/http"
|
||||
+ "testing"
|
||||
+
|
||||
+ "keycape/internal/jose"
|
||||
+)
|
||||
+
|
||||
+func TestOnlineHumanIDTokenBindsExactAccessGrant(t *testing.T) {
|
||||
+ f := newStatusFixture(t, 10)
|
||||
+ f.token.ClientConfig["test-client"].Audience = "resource:forgejo"
|
||||
+ first, firstID, code := f.issue(t)
|
||||
+ second, secondID, secondCode := f.issue(t)
|
||||
+ if code != http.StatusOK || secondCode != http.StatusOK || first == second {
|
||||
+ t.Fatal("distinct actual grants were not issued")
|
||||
+ }
|
||||
+ keys := jose.KeySet{"key-1": &f.token.SigningKey.PublicKey}
|
||||
+ for _, pair := range [][2]string{{first, firstID}, {second, secondID}} {
|
||||
+ claims, err := jose.VerifyTyped(pair[1], keys, "JWT")
|
||||
+ if err != nil {
|
||||
+ t.Fatal("actual ID signature refused")
|
||||
+ }
|
||||
+ digest := sha256.Sum256([]byte(pair[0]))
|
||||
+ if claims["at_hash"] != base64.RawURLEncoding.EncodeToString(digest[:16]) || claims["aud"] != "test-client" {
|
||||
+ t.Fatal("ID token lost exact access-grant or relying-party binding")
|
||||
+ }
|
||||
+ if _, ok := claims["jti"]; ok {
|
||||
+ t.Fatal("access-grant jti escaped into ID token")
|
||||
+ }
|
||||
+ if _, ok := claims["scope"]; ok {
|
||||
+ t.Fatal("access scope escaped into ID token")
|
||||
+ }
|
||||
+ access, err := jose.VerifyTyped(pair[0], keys, "JWT")
|
||||
+ if err != nil || access["aud"] != "resource:forgejo" {
|
||||
+ t.Fatal("resource audience changed")
|
||||
+ }
|
||||
+ if _, ok := access["at_hash"]; ok {
|
||||
+ t.Fatal("ID-only grant binding escaped into access token")
|
||||
+ }
|
||||
+ }
|
||||
+ firstClaims, _ := jose.VerifyTyped(firstID, keys, "JWT")
|
||||
+ secondClaims, _ := jose.VerifyTyped(secondID, keys, "JWT")
|
||||
+ if firstClaims["at_hash"] == secondClaims["at_hash"] {
|
||||
+ t.Fatal("distinct grants shared an ID-token access binding")
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+func TestDisabledOnlineProfileKeepsLegacyIDToken(t *testing.T) {
|
||||
+ f := newStatusFixture(t, 10)
|
||||
+ f.token.Tokens = nil
|
||||
+ _, id, code := f.issue(t)
|
||||
+ claims, err := jose.VerifyTyped(id, jose.KeySet{"key-1": &f.token.SigningKey.PublicKey}, "JWT")
|
||||
+ if code != http.StatusOK || err != nil {
|
||||
+ t.Fatal("legacy profile failed")
|
||||
+ }
|
||||
+ if _, ok := claims["at_hash"]; ok {
|
||||
+ t.Fatal("disabled profile changed ID-token contract")
|
||||
+ }
|
||||
+}
|
||||
--- a/workplans/KEY-WP-0042-online-access-token-status.md
|
||||
+++ b/workplans/KEY-WP-0042-online-access-token-status.md
|
||||
@@ -33,7 +33,8 @@
|
||||
|
||||
The reviewable contract is `docs/online-token-status.md`: opt-in RFC 7662/7009
|
||||
access-token status; registered owning-client authentication; private hash-only
|
||||
-bounded state; unique access-token jti; no ID-token change; singleton store-loss
|
||||
+bounded state; unique access-token jti; online human ID-token exact `at_hash`
|
||||
+binding with the disabled legacy ID-token contract unchanged; singleton store-loss
|
||||
refusal; disabled legacy behavior explicit. Source preparation is authorized by
|
||||
the full Custodian implementation goal. Independent issuer/profile/consumer
|
||||
assent remains required before admission; no such assent is inferred here.
|
||||
@@ -53,6 +54,20 @@
|
||||
cross-client refusals, current registration and human disposition, revocation
|
||||
during directory lookup, process-store loss and absence of secret output. Keep
|
||||
the complete existing source suite, disabled profile and build passing.
|
||||
+
|
||||
+2026-10-18 T02 source continuation: the actual consumer join exposed missing
|
||||
+same-grant correspondence. The online human ID token now signs the exact access
|
||||
+token's RS256 `at_hash`, retaining its own audience and excluding access-only
|
||||
+fields. Distinct same-second grants cannot share that binding; disabled issuance
|
||||
+is unchanged. This qualifies the earlier unscoped "no ID-token change" statement
|
||||
+for the proposed online profile, without changing existing deployment behavior.
|
||||
+The complete source race suite passes 726 test/subtest results with the same six
|
||||
+native/external skips. The actual HTTPS PKCE/discovery/UserInfo/status/revocation
|
||||
+handlers and real Forgejo provider/callback pass five separate consumer processes:
|
||||
+two current grants accepted, mixed grants/revoked original/suspended human refused.
|
||||
+The original issuer fails the same current consumer. MFA session seeds and PDP
|
||||
+admissions are synthetic. Source-only evidence: Custodian forgejo-current-token-r1.
|
||||
+Independent issuer/profile/consumer review and T03 native admission remain open.
|
||||
|
||||
## Admit and accept the issuer lifecycle at native scope
|
||||
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
# Shared current-token client and private CLI observations
|
||||
|
||||
Source preparation under proposed KEY-WP-0042 and the existing CUST-WP-0080 /
|
||||
NK48 / Apps operation and application obligations. This is a client of the
|
||||
issuer's optional online profile, not another issuer, PDP, approval service or
|
||||
application-local token registry. Native issuer/profile/consumer admission is
|
||||
still required. Actual operation/effect permission remains with existing owners.
|
||||
|
||||
## One producer, two consumption surfaces
|
||||
|
||||
`src/internal/authclient/online.go` is the single implementation. The existing
|
||||
KeyCape executable exposes it as `check-current-token`, `revoke-token` and
|
||||
`check-inactive-token` for owner processes. Go consumers use the generated
|
||||
`adapters/go/tokenstatus` module and import `keycape/pkg/tokenstatus` through an
|
||||
exact pinned source/dependency lock. `make tokenstatus-export` generates that
|
||||
five-file module; `make tokenstatus-check`, ordinary `make test` and the root Go
|
||||
suite refuse drift. The sole client transformation is its first package
|
||||
declaration; all remaining bytes and the shared JOSE primitive are exact.
|
||||
No policy evaluator, signing key, credential, daemon or deployment is exported.
|
||||
|
||||
The client uses the existing producer `internal/jose` verifier. Its additive
|
||||
`VerifyTyped` path requires the actual RS256 / JWT producer header; legacy
|
||||
`Verify` callers retain their existing type policy. Keys are independently
|
||||
admitted public JWKS, never fetched from the status response or discovered by
|
||||
the token. The minimal generated module uses Go 1.23 and standard libraries.
|
||||
Its file boundary is the current **Linux owner profile**: POSIX mode checks do
|
||||
not establish Windows ACL behavior or a broader platform admission.
|
||||
|
||||
## Current status is an authenticated fact
|
||||
|
||||
Online human PKCE ID tokens additionally carry the exact access-token `at_hash`
|
||||
binding described in `online-token-status.md`. This client verifies access-token
|
||||
facts; a consumer that also uses an ID token separately authenticates that token,
|
||||
checks its purpose, and joins its signed hash, identity, issuance, assurance and
|
||||
tenant provenance to the exact access grant. Neither token substitutes for the
|
||||
other. The online profile's hash is additive; disabled legacy issuance is unchanged.
|
||||
|
||||
`OnlineClient.Check(ctx, token, ExpectedIdentity{...})` validates the signature,
|
||||
issuer, exact single resource audience, subject, tenant, human/service type,
|
||||
optional authorized-party binding, access-token purpose, unique jti and integer
|
||||
issued/expiry/not-before bounds before reading the private caller credential.
|
||||
It bounds identity by expiry and an independently admitted token age (at most
|
||||
one hour), then performs a new authenticated introspection. It joins every
|
||||
returned identity/client/audience/jti/scope/time field to those signed facts.
|
||||
After network I/O it checks time again and reloads JWT trust so a delayed answer
|
||||
cannot retain expired facts or a withdrawn signing key. No positive is cached.
|
||||
|
||||
The explicit HTTPS issuer origin supplies the exact `/introspect` and `/revoke`
|
||||
paths; redirects, query/fragment/ambiguous issuer paths, environment proxies and
|
||||
ambient transports are not used. CA, JWKS and private caller credential files
|
||||
are reread for each operation with regular-file/mode/size guards. File opens do
|
||||
not block on FIFO replacement. Private credentials can be atomically rotated
|
||||
without retaining a cached value. Requests have a five-second bound and responses
|
||||
a 64 KiB bound. Duplicate/null/missing/extra fields, mismatched facts, non-JSON or
|
||||
cacheable status responses and unavailable/unauthenticated transport refuse with
|
||||
one fixed error. Secret, token and response values are never part of errors.
|
||||
|
||||
`CurrentToken.Claims()` returns a separate copy of verified signed facts. Those
|
||||
facts remain snapshots: original authentication time is not the current lookup
|
||||
time. Minimum assurance/authentication age, tenant provenance, current groups,
|
||||
resource/action policy, exact approval and effect authority remain the existing
|
||||
consumer profile's obligations. A fresh status response is not a new MFA act or
|
||||
permission. A Forgejo sign-in token does not grant release-write or inverse
|
||||
authority, and no operator client/route is selected by this source package.
|
||||
|
||||
## Negative scope and cleanup
|
||||
|
||||
`ObserveInactive` performs an independent **read-only** status lookup. It never
|
||||
infers inactivity from transport failure, malformed output or an unsuccessful
|
||||
positive check. `Revoke` sends the exact token, requires an empty successful
|
||||
response and then obtains that separate negative readback. Both return only
|
||||
`InactiveForClient`: the exact digest is currently inactive for the configured
|
||||
client. Unknown and other-client tokens have the same negative response.
|
||||
|
||||
That result alone cannot prove original grant ownership, global revocation or a
|
||||
completed operation's cleanup. The owner retains its independently authenticated
|
||||
original issuance/current-positive evidence, exact token digest/jti and operation
|
||||
correspondence before mapping a negative observation to a parent-session receipt.
|
||||
Cleanup can continue after business-token freshness/key withdrawal; it creates
|
||||
no business authority. Unavailable/ambiguous cleanup remains unverified and
|
||||
requires the existing durable owner retry/recovery path. It is not success.
|
||||
|
||||
## Existing CLI
|
||||
|
||||
The public admission file contains only references and independently admitted
|
||||
bindings; for example (paths/client are placeholders, not operational defaults):
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://owner-admitted-issuer.example",
|
||||
"audience": "owner-admitted-resource",
|
||||
"client_id": "owner-admitted-confidential-client",
|
||||
"ca_file": "/run/owner-identity/issuer-ca.pem",
|
||||
"jwks_file": "/run/owner-identity/admitted-jwks.json",
|
||||
"secret_file": "/run/owner-identity/current-client-credential",
|
||||
"max_token_age": "5m"
|
||||
}
|
||||
```
|
||||
|
||||
Use an existing owner-acquired private KeyCape token JSON file, and the access
|
||||
token's exact `sha256:` digest from the owner-bound session/transaction record.
|
||||
No token or client credential goes in argv. Follow the existing Warden routing /
|
||||
owner-native acquisition and custody path; this source adds no credential lane.
|
||||
|
||||
```text
|
||||
keycape check-current-token --admission-file /ABS/profile.json --token-file /ABS/private-token.json --token-sha256 sha256:OWNER_BOUND_DIGEST --subject OWNER_EXPECTED_SUBJECT --tenant OWNER_EXPECTED_TENANT --principal-type human --out /ABS/PRIVATE-DIR/new-observation.json
|
||||
keycape revoke-token --admission-file /ABS/profile.json --token-file /ABS/private-token.json --token-sha256 sha256:OWNER_BOUND_DIGEST --out /ABS/PRIVATE-DIR/new-cleanup-observation.json
|
||||
keycape check-inactive-token --admission-file /ABS/profile.json --token-file /ABS/private-token.json --token-sha256 sha256:OWNER_BOUND_DIGEST --out /ABS/PRIVATE-DIR/new-independent-negative.json
|
||||
```
|
||||
|
||||
The placeholders deliberately do not form an operational command. Current checks
|
||||
require explicit expected subject/tenant/type. Negative/cleanup commands refuse
|
||||
those business-identity flags because they report only the bounded client status.
|
||||
Duplicate flags and duplicate/unknown/null input fields refuse. The token file
|
||||
must be private, the digest must match the exact parsed access token, and the new
|
||||
observation must have a 0700 parent outside Git. Outputs are exclusive 0600 files;
|
||||
failures remove only their own inode and cannot delete another attempt's replaced
|
||||
file. Standard output/errors expose fixed status, not claims or credential values.
|
||||
|
||||
`keycape.current-token-observation.v1` is a **private, unsigned producer command
|
||||
result**, not a portable execution receipt. Current output includes bounded
|
||||
identity/token references and signed assurance/tenant-provenance facts; it omits
|
||||
bearers, credentials and group/role snapshots. Negative output states its client
|
||||
scope. The invoking owner authenticates exact binary/config/custody and binds its
|
||||
own case, acceptance, descriptor, source/target, principal, transaction and effect.
|
||||
Local file presence, digests or a successful command do not supply those grants.
|
||||
|
||||
## Scope of verification
|
||||
|
||||
Source checks use actual token/status handlers over isolated HTTPS, actual human
|
||||
PKCE claim construction and the same CLI implementation. The separate cross-owner
|
||||
fixture runs a built executable, generated public Go consumer and actual Warden
|
||||
Executor/SQLite in distinct Python processes. Synthetic case/catalog/owner
|
||||
attestations and a disposable file target do not establish Forgejo/browser
|
||||
acceptance, real-factor possession, production custody or approved native effects.
|
||||
The online issuer profile, owner mappings and source/package/type admission remain
|
||||
under review; all corresponding native gates stay open.
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# Optional online access-token status
|
||||
|
||||
Source proposal under KEY-WP-0042, driven by CUST-WP-0080 and existing
|
||||
NK-WP-0048-T34/T42/T43 and RAPPS-WP-0016-T08/T09. Independent issuer/consumer
|
||||
contract review and native admission remain required. Existing deployments are
|
||||
unchanged. This extends the bounded issuer; it does not introduce a broker,
|
||||
approval store, PDP, or a second application-local token registry.
|
||||
|
||||
The legacy profile authenticates self-contained JWTs until expiry. That does
|
||||
not establish that an operation's initiating token is still unrevoked. The
|
||||
optional profile adds issuer-owned current access-token status using
|
||||
[RFC 7662](https://www.rfc-editor.org/rfc/rfc7662.html) introspection and
|
||||
[RFC 7009](https://www.rfc-editor.org/rfc/rfc7009.html) revocation. It supports
|
||||
access tokens only, without refresh tokens or token exchange.
|
||||
|
||||
## Enablement and lifecycle
|
||||
|
||||
Owner-admitted configuration opts in explicitly:
|
||||
|
||||
```yaml
|
||||
tokenStatus:
|
||||
enabled: true
|
||||
maxEntries: 10000
|
||||
```
|
||||
|
||||
The issuer must use HTTPS at its existing trusted ingress. The server's internal
|
||||
HTTP listener and reverse-proxy topology are unchanged; direct HTTP exposure is
|
||||
not admitted by an HTTPS issuer string. Omitted configuration keeps the existing
|
||||
expiry-only token/UserInfo contract and advertises no new endpoints. Enabling
|
||||
status registers `/introspect` and `/revoke`, advertises their implemented client
|
||||
authentication methods, and connects the same store to issuance and UserInfo.
|
||||
|
||||
Human PKCE and service grants add a cryptographically random `jti` to each access
|
||||
token. Online human PKCE ID tokens add `at_hash`, binding the exact access token
|
||||
using the RS256 rule in [OIDC Core 3.1.3.6/8](https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken).
|
||||
The ID token keeps its relying-party audience and omits access-only `jti`/`scope`;
|
||||
the access token keeps its registered resource audience and omits `at_hash`.
|
||||
Existing assurance/tenant provenance and the disabled legacy ID-token contract
|
||||
remain unchanged. Consumers of this online human profile require the exact signed
|
||||
hash before treating an ID token and access token as one grant; equal identity
|
||||
facts or issuance seconds alone cannot distinguish separate grants.
|
||||
Distinct grants in the same second have distinct bearer tokens: a new grant can
|
||||
never remint a revoked token. A successful response is emitted only after the
|
||||
token hash, expiry and current registration commitment have been recorded. No
|
||||
bearer, client secret or identity/group snapshot is retained in the status store.
|
||||
|
||||
The store is bounded to the configured count (default 10000, maximum 1000000).
|
||||
Live records and revocation tombstones are never evicted to admit another grant;
|
||||
capacity exhaustion refuses issuance with a fixed 503. Expired entries are
|
||||
collected when admitting a new token. Capacity and per-client ingress rate limits
|
||||
need workload-representative admission: this is not a throughput/availability
|
||||
claim or protection against an authorized client filling the bound.
|
||||
|
||||
This profile uses KeyCape's existing **single-instance topology**. Status is
|
||||
process-local. Restart, deployment, rollback or store loss makes every earlier
|
||||
token inactive at online endpoints, even if its JWT signature/expiry still
|
||||
validates offline. Consumers must reauthenticate; there is no recovery replay
|
||||
or token-state import that can resurrect revoked authority. Multiple replicas
|
||||
are unsupported. Native admission must include this interruption and the
|
||||
measured reauthentication/recovery procedure. A persisted/shared store is not
|
||||
introduced here.
|
||||
|
||||
## Introspection and revocation
|
||||
|
||||
Both endpoints accept POST form bodies only, bound to 64 KiB; token values are
|
||||
bound to 32 KiB. Query parameters, duplicate form or authentication fields,
|
||||
unsupported content types and malformed authentication refuse without echoing
|
||||
input. Responses use `Cache-Control: no-store` and `Pragma: no-cache`.
|
||||
|
||||
Introspection requires the existing registered confidential client's
|
||||
`client_secret_basic` authentication. Only that client's own issued tokens can
|
||||
be inspected. A separate resource audience is returned as `aud`, not confused
|
||||
with the issuing `client_id`. Cross-client resource-server delegation needs a
|
||||
separately reviewed authorization contract and is not implicitly granted by a
|
||||
matching audience string. Public clients cannot enumerate token status.
|
||||
|
||||
An active response means the exact access token was issued in this process,
|
||||
has not expired or been revoked, still matches its current complete registration,
|
||||
and passes current issuer/signature/purpose validation. For humans, directory
|
||||
lookup must succeed and the current account must be enabled and not suspended.
|
||||
Status is checked again after directory lookup. Service subjects remain bound
|
||||
to their current registration. Active output contains only standard access-token
|
||||
metadata; inactive output contains only `active: false`. Unknown tokens, ID
|
||||
tokens, altered tokens, another client's tokens and expired/revoked tokens all
|
||||
return that same inactive response to an authenticated introspector.
|
||||
|
||||
Revocation authenticates confidential clients using the same method. A registered
|
||||
public client may revoke its own token using `client_id` plus possession of that
|
||||
bearer; public-client introspection remains unavailable. Revocation only affects
|
||||
the exact token issued to that client. Unknown, other-client, expired and already
|
||||
revoked tokens all produce the same empty 200. No token existence is disclosed.
|
||||
Token-type hints do not bypass the actual access-token lookup. Removal or changes
|
||||
to a registration invalidate online status; the commitment explicitly includes
|
||||
the secret digest and internal token lifetime omitted from public Client JSON.
|
||||
|
||||
When enabled, actual UserInfo consults the same store before lookup and again
|
||||
after it. Revocation therefore affects that existing endpoint immediately on its
|
||||
next check. Ordinary login/logout and upstream Authelia sessions are distinct:
|
||||
neither browser logout nor deleting a local cookie is represented as global
|
||||
JWT revocation.
|
||||
|
||||
## Consumer contract and remaining acceptance
|
||||
|
||||
Online status establishes current access-token status, not permission
|
||||
for an application resource or privileged effect. Consumers still independently
|
||||
validate the actual signed token, admitted issuer/key/audience/type, subject,
|
||||
tenant, principal type, assurance and freshness. The authenticated introspection
|
||||
response must refer to that exact token/client. Obtain a new current response
|
||||
before each protected operation; unavailable, malformed, inactive or ambiguous
|
||||
responses refuse. A cached positive is not a current-session check.
|
||||
|
||||
Access Engine retains resource/action decisions, Approval Engine exact one-shot
|
||||
approval, Warden admission and owner adapters execution. Bind their existing
|
||||
descriptor, case, transaction and exact effect before dispatch. A separate new
|
||||
operation/inverse token must be acquired and revoked on every terminal path;
|
||||
renewed authentication cannot revive a consumed approval or replay a mutation.
|
||||
This issuer capability alone implements none of those consumer bindings.
|
||||
|
||||
The producer-owned [current-token client](current-token-client.md) supplies the
|
||||
shared Go/CLI transport and cryptographic/status join. Its private observations
|
||||
retain this distinction: an inactive-for-client result is not an original
|
||||
ownership or execution receipt. Native client/profile adoption remains open.
|
||||
|
||||
Native completion still requires independent issuer/profile review, exact
|
||||
image/config/trust admission, bounded retained-token custody, real human/service
|
||||
issuance, private transport, current-token/revocation integration in each owner,
|
||||
all exit-path cleanup, restart/expiry/unavailable/refusal checks, ongoing
|
||||
HTTP/API/Git/SSH authorization and audit evidence. Offline consumers which never
|
||||
consult the endpoint retain their existing JWT validity; no estate-wide immediate
|
||||
revocation claim follows from source implementation. Disabling the feature is
|
||||
not an acceptable fallback for a consumer admitted under the online profile.
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
//go:build forgejo_current_fixture
|
||||
|
||||
package authclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"keycape/internal/domain"
|
||||
"keycape/internal/server/oidc"
|
||||
"keycape/internal/server/telemetry"
|
||||
)
|
||||
|
||||
type forgejoFixtureDirectory struct {
|
||||
mu sync.RWMutex
|
||||
user domain.User
|
||||
}
|
||||
|
||||
func (d *forgejoFixtureDirectory) LookupUser(context.Context, string) (*domain.User, error) {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
user := d.user
|
||||
user.Groups = append([]string(nil), d.user.Groups...)
|
||||
return &user, nil
|
||||
}
|
||||
func (d *forgejoFixtureDirectory) LookupGroups(context.Context, string) ([]domain.Group, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (d *forgejoFixtureDirectory) ListUsers(ctx context.Context) ([]domain.User, error) {
|
||||
user, _ := d.LookupUser(ctx, "")
|
||||
return []domain.User{*user}, nil
|
||||
}
|
||||
func (d *forgejoFixtureDirectory) ValidatePassword(context.Context, string, string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func TestOnlineActualForgejoCallbackCurrentIssuer(t *testing.T) {
|
||||
f := onlineProvider(t)
|
||||
directory := &forgejoFixtureDirectory{user: domain.User{ID: "uid=operator,ou=people,dc=fixture", Username: "operator", Enabled: true, Tenant: "tenant:fixture", Groups: []string{"forge-admins"}}}
|
||||
client := f.issuer.ClientConfig["current-client"]
|
||||
client.GrantTypes = []string{"authorization_code"}
|
||||
client.ServiceSubject = ""
|
||||
client.Audience = client.ClientID
|
||||
client.AllowedScopes = []string{"openid", "profile", "groups"}
|
||||
client.RedirectURIs = []string{"https://forgejo.invalid/callback"}
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewTLSServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
f.issuer.Users = directory
|
||||
f.issuer.Issuer = server.URL
|
||||
f.status.Users = directory
|
||||
f.status.Issuer = server.URL
|
||||
userinfo := &oidc.UserinfoHandler{Users: directory, SigningKey: &f.issuer.SigningKey.PublicKey, Issuer: server.URL, Tokens: f.issuer.Tokens, ClientConfig: f.issuer.ClientConfig, Emitter: telemetry.NoopEmitter{}}
|
||||
var statusCalls, userinfoCalls atomic.Int64
|
||||
mux.Handle("/token", f.issuer)
|
||||
mux.HandleFunc("/introspect", func(w http.ResponseWriter, r *http.Request) { statusCalls.Add(1); f.status.Introspect(w, r) })
|
||||
mux.HandleFunc("/revoke", f.status.Revoke)
|
||||
mux.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) { userinfoCalls.Add(1); userinfo.ServeHTTP(w, r) })
|
||||
keys := oidc.NewKeySet()
|
||||
keys.AddKey("key-1", &f.issuer.SigningKey.PublicKey)
|
||||
mux.Handle("/jwks", oidc.NewJWKSHandler(keys))
|
||||
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"issuer": server.URL, "authorization_endpoint": server.URL + "/authorize", "token_endpoint": server.URL + "/token", "userinfo_endpoint": server.URL + "/userinfo", "jwks_uri": server.URL + "/jwks", "id_token_signing_alg_values_supported": []string{"RS256"}})
|
||||
})
|
||||
admission := f.client.admission
|
||||
admission.Issuer = server.URL
|
||||
admission.Audience = client.ClientID
|
||||
if os.WriteFile(admission.CAFile, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0644) != nil {
|
||||
t.Fatal("fixture CA write failed")
|
||||
}
|
||||
var err error
|
||||
f.client, err = NewOnlineClient(admission)
|
||||
if err != nil {
|
||||
t.Fatal("fixture admission failed")
|
||||
}
|
||||
der, err := x509.MarshalPKIXPublicKey(&f.issuer.SigningKey.PublicKey)
|
||||
if err != nil {
|
||||
t.Fatal("fixture public key failed")
|
||||
}
|
||||
publicKey := string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))
|
||||
issue := func() Tokens {
|
||||
verifier := strings.Repeat("v", 43)
|
||||
digest := sha256.Sum256([]byte(verifier))
|
||||
code := f.issuer.Sessions.Create(&oidc.PKCESession{ClientID: client.ClientID, Username: directory.user.Username, RedirectURI: client.RedirectURIs[0], PKCEChallengeMethod: "S256", PKCEChallenge: base64.RawURLEncoding.EncodeToString(digest[:]), Scopes: client.AllowedScopes, MFAVerified: true, AuthTime: time.Now().Add(-time.Minute).Truncate(time.Second), ExpiresAt: time.Now().Add(time.Minute)})
|
||||
form := url.Values{"grant_type": {"authorization_code"}, "client_id": {client.ClientID}, "code": {code}, "code_verifier": {verifier}, "redirect_uri": {client.RedirectURIs[0]}}
|
||||
r, _ := http.NewRequest(http.MethodPost, server.URL+"/token", strings.NewReader(form.Encode()))
|
||||
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
r.SetBasicAuth(url.QueryEscape(client.ClientID), url.QueryEscape(client.ClientSecret))
|
||||
response, err := server.Client().Do(r)
|
||||
if err != nil {
|
||||
t.Fatal("actual fixture token HTTP failed")
|
||||
}
|
||||
defer response.Body.Close()
|
||||
var tokens Tokens
|
||||
if response.StatusCode != 200 || json.NewDecoder(response.Body).Decode(&tokens) != nil || tokens.AccessToken == "" || tokens.IDToken == "" {
|
||||
t.Fatal("actual fixture human PKCE failed")
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
first, second := issue(), issue()
|
||||
directoryPath := t.TempDir()
|
||||
var phases []string
|
||||
probe := func(tokens Tokens, phase string) {
|
||||
input := map[string]any{"id_token": tokens.IDToken, "access_token": tokens.AccessToken, "public_key_pem": publicKey, "tenant": directory.user.Tenant, "phase": phase, "current": admission}
|
||||
raw, _ := json.Marshal(input)
|
||||
file := filepath.Join(directoryPath, "consumer-input.json")
|
||||
if os.WriteFile(file, raw, 0600) != nil {
|
||||
t.Fatal("private fixture input failed")
|
||||
}
|
||||
command := exec.Command(os.Getenv("FORGEJO_CURRENT_TEST_BINARY"), "-test.run=^TestAccessEngineLiveFixtureCurrentIssuer$", "-test.v")
|
||||
command.Env = append(os.Environ(), "FORGEJO_CURRENT_ISSUER_FIXTURE="+file)
|
||||
output, err := command.CombinedOutput()
|
||||
for _, private := range []string{first.AccessToken, first.IDToken, second.AccessToken, second.IDToken, client.ClientSecret} {
|
||||
if bytes.Contains(output, []byte(private)) {
|
||||
t.Fatal("consumer fixture diagnostic disclosed private input")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("actual consumer phase %s failed: %s", phase, output)
|
||||
}
|
||||
phases = append(phases, phase)
|
||||
}
|
||||
probe(first, "active")
|
||||
probe(Tokens{AccessToken: second.AccessToken, IDToken: first.IDToken}, "mixed-pair")
|
||||
if _, err := f.client.Revoke(context.Background(), first.AccessToken); err != nil {
|
||||
t.Fatal("actual fixture revoke/independent negative failed")
|
||||
}
|
||||
probe(first, "revoked-after-userinfo")
|
||||
probe(second, "active")
|
||||
directory.mu.Lock()
|
||||
directory.user.Groups = []string{"netkingdom-suspended"}
|
||||
directory.mu.Unlock()
|
||||
probe(second, "suspended-after-userinfo")
|
||||
report := map[string]any{"status": "passed", "consumer_processes": len(phases), "phases": phases, "actual_https_human_PKCE": true, "actual_discovery_and_userinfo": true, "actual_introspection_and_revocation": true, "status_calls": statusCalls.Load(), "userinfo_calls": userinfoCalls.Load(), "actual_forgejo_callback_and_generated_client": true, "synthetic_MFA_session_seed_and_PDP_admission": true, "native_acceptance": false, "private_inputs": "fixture memory/tmpfs only"}
|
||||
raw, _ := json.MarshalIndent(report, "", " ")
|
||||
if os.WriteFile(os.Getenv("FORGEJO_CURRENT_REPORT"), append(raw, '\n'), 0600) != nil {
|
||||
t.Fatal("public fixture result failed")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,642 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"keycape/internal/adapters/tenantengine"
|
||||
"keycape/internal/domain"
|
||||
profileerrors "keycape/internal/errors"
|
||||
"keycape/internal/server/telemetry"
|
||||
)
|
||||
|
||||
// TokenHandler implements POST /token.
|
||||
type TokenHandler struct {
|
||||
ClientConfig map[string]*domain.Client
|
||||
Sessions *SessionStore
|
||||
Users domain.UserRepository
|
||||
SigningKey *rsa.PrivateKey
|
||||
Issuer string
|
||||
TokenLifetime time.Duration
|
||||
Emitter telemetry.Emitter
|
||||
// TenantEngine sources the optional tenant_roles claim (KEY-WP-0005-T02).
|
||||
// Nil disables it entirely -- token issuance never depends on it.
|
||||
TenantEngine *tenantengine.Client
|
||||
// Tokens enables issuer-owned online token status; nil retains the legacy
|
||||
// expiry-only profile. Enabling it requires explicit deployment admission.
|
||||
Tokens *TokenStatusStore
|
||||
}
|
||||
|
||||
// tokenResponse is the JSON body returned on a successful token exchange.
|
||||
type tokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
}
|
||||
|
||||
// ServeHTTP handles POST /token.
|
||||
func (h *TokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "invalid form body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
grantType := r.FormValue("grant_type")
|
||||
if grantType == "client_credentials" {
|
||||
h.serveClientCredentials(w, r)
|
||||
return
|
||||
}
|
||||
clientID := r.FormValue("client_id")
|
||||
code := r.FormValue("code")
|
||||
codeVerifier := r.FormValue("code_verifier")
|
||||
|
||||
// 1. Validate grant_type.
|
||||
if grantType != "authorization_code" {
|
||||
profileerrors.FeatureNotSupported(
|
||||
"only grant_type=authorization_code is supported",
|
||||
"grant_type="+grantType,
|
||||
).Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Validate client exists and may use this grant.
|
||||
client, ok := h.ClientConfig[clientID]
|
||||
if !ok {
|
||||
profileerrors.InvalidProfileUsage("unknown client_id", "client_id").
|
||||
Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Grant-type eligibility, enforced equivalently to the service path
|
||||
// (KEY-WP-0016-T02). An empty grantTypes is an implicit authorization-code
|
||||
// client, matching config validation; a client_credentials-only client must
|
||||
// not reach the browser path.
|
||||
if len(client.GrantTypes) > 0 && !containsString(client.GrantTypes, "authorization_code") {
|
||||
profileerrors.InvalidProfileUsage(
|
||||
"client is not registered for grant_type=authorization_code",
|
||||
"grant_type",
|
||||
).Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Confidential authorization-code clients authenticate with their secret,
|
||||
// using the same credential sources as the service grant. A public client
|
||||
// must not be able to present a secret and be treated as authenticated.
|
||||
if client.ClientType == "confidential" {
|
||||
presentedID, secret, ok := basicClientCredentials(r)
|
||||
if !ok || presentedID != clientID || client.ClientSecret == "" ||
|
||||
!secretsEqual(secret, client.ClientSecret) {
|
||||
profileerrors.InvalidProfileUsage(
|
||||
"client authentication failed",
|
||||
"Authorization",
|
||||
).Write(w, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Consume the PKCE session. Single-use and atomic: see
|
||||
// SessionStore.Consume (KEY-WP-0016-T01).
|
||||
sess, ok := h.Sessions.Consume(code)
|
||||
if !ok {
|
||||
profileerrors.InvalidProfileUsage(
|
||||
"authorization code not found or expired",
|
||||
"code",
|
||||
).Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify client_id matches the session.
|
||||
if sess.ClientID != clientID {
|
||||
profileerrors.InvalidProfileUsage(
|
||||
"client_id does not match the authorization code",
|
||||
"client_id",
|
||||
).Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Bind the exchange to the redirect URI the code was issued for
|
||||
// (RFC 6749 section 4.1.3, KEY-WP-0016-T02). /authorize always records an
|
||||
// exactly-matched registered redirect, so the parameter is always required
|
||||
// here and must be identical.
|
||||
if redirectURI := r.FormValue("redirect_uri"); redirectURI != sess.RedirectURI {
|
||||
profileerrors.InvalidProfileUsage(
|
||||
"redirect_uri does not match the authorization request",
|
||||
"redirect_uri",
|
||||
).Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Recheck grants in case the client registration changed after authorization.
|
||||
for _, scope := range sess.Scopes {
|
||||
if !containsString(h.ClientConfig[clientID].AllowedScopes, scope) {
|
||||
profileerrors.InvalidProfileUsage("requested scope is not allowed", "scope").Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Verify PKCE code_verifier.
|
||||
if !verifyPKCE(codeVerifier, sess.PKCEChallenge) {
|
||||
profileerrors.InvalidProfileUsage(
|
||||
"code_verifier does not match code_challenge",
|
||||
"code_verifier",
|
||||
).Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. Look up user.
|
||||
user, err := h.Users.LookupUser(ctx, sess.Username)
|
||||
if err != nil {
|
||||
http.Error(w, "user not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if isSuspended(user) {
|
||||
profileerrors.RejectedForSafety(
|
||||
"account is suspended",
|
||||
"account_lifecycle",
|
||||
).Write(w, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// 6. Build JWT claims.
|
||||
now := time.Now()
|
||||
exp := now.Add(h.TokenLifetime)
|
||||
|
||||
claims := map[string]interface{}{
|
||||
"iss": h.Issuer,
|
||||
"sub": user.ID,
|
||||
"aud": clientID,
|
||||
"exp": exp.Unix(),
|
||||
"iat": now.Unix(),
|
||||
}
|
||||
if sess.Nonce != "" {
|
||||
claims["nonce"] = sess.Nonce
|
||||
}
|
||||
|
||||
scopeSet := make(map[string]bool)
|
||||
for _, s := range sess.Scopes {
|
||||
scopeSet[s] = true
|
||||
}
|
||||
|
||||
if scopeSet["profile"] {
|
||||
claims["preferred_username"] = user.Username
|
||||
}
|
||||
if scopeSet["email"] {
|
||||
claims["email"] = user.Email
|
||||
}
|
||||
|
||||
// Core claims required by net-kingdom/canon/standards/iam-profile_v0.3.md
|
||||
// for every production token -- not scope-gated, unlike the recommended
|
||||
// human claims above (KEY-WP-0005-T01).
|
||||
tenant, tenantSource, err := humanTenant(h.ClientConfig[clientID], user)
|
||||
if err != nil {
|
||||
profileerrors.RejectedForSafety(
|
||||
"tenant binding conflict",
|
||||
"tenant_binding",
|
||||
).Write(w, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
claims["tenant"] = tenant
|
||||
claims["tenant_source"] = tenantSource
|
||||
claims["principal_type"] = "human"
|
||||
claims["groups"] = nonNilStrings(user.Groups)
|
||||
claims["roles"] = nonNilStrings(user.Roles)
|
||||
claims["assurance"] = assuranceClaim(sess.MFAVerified, sess.AuthTime, now)
|
||||
|
||||
// Optional cached tenant_roles claim (KEY-WP-0005-T02). Fails open --
|
||||
// see internal/adapters/tenantengine's package doc for why this is the
|
||||
// one place in the whole tenant_roles design where that's correct.
|
||||
if roles, ok := h.TenantEngine.Roles(ctx, tenant); ok {
|
||||
claims["tenant_roles"] = roles
|
||||
}
|
||||
|
||||
// 7. Sign JWT with RSA-SHA256.
|
||||
kid := "key-1" // static kid for v0.1
|
||||
identityClaims := make(map[string]interface{}, len(claims)+1)
|
||||
for name, value := range claims {
|
||||
identityClaims[name] = value
|
||||
}
|
||||
var jwtToken string
|
||||
if h.Tokens == nil {
|
||||
jwtToken, err = buildJWT(identityClaims, kid, h.SigningKey)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to build JWT", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Access tokens target the statically registered resource server. ID tokens
|
||||
// remain bound to the OIDC relying party.
|
||||
if audience := h.ClientConfig[clientID].Audience; audience != "" {
|
||||
claims["aud"] = audience
|
||||
}
|
||||
claims["scope"] = strings.Join(sess.Scopes, " ")
|
||||
if h.Tokens != nil {
|
||||
id := make([]byte, 32)
|
||||
if _, err := rand.Read(id); err != nil {
|
||||
http.Error(w, "token status unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
claims["jti"] = base64.RawURLEncoding.EncodeToString(id)
|
||||
}
|
||||
accessToken, err := buildJWT(claims, kid, h.SigningKey)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to build JWT", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if h.Tokens != nil {
|
||||
// The online profile binds the signed ID token to this exact access
|
||||
// grant, including same-second distinct jtis. OIDC RS256 at_hash uses
|
||||
// the base64url-encoded left half of SHA-256 over the access token.
|
||||
digest := sha256.Sum256([]byte(accessToken))
|
||||
identityClaims["at_hash"] = base64.RawURLEncoding.EncodeToString(digest[:16])
|
||||
jwtToken, err = buildJWT(identityClaims, kid, h.SigningKey)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to build JWT", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := h.Tokens.register(accessToken, client, time.Unix(exp.Unix(), 0), now); err != nil {
|
||||
http.Error(w, "token status unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Build response. The session was already consumed at lookup, so no
|
||||
// separate replay-prevention delete is needed here.
|
||||
resp := tokenResponse{
|
||||
AccessToken: accessToken,
|
||||
TokenType: "Bearer",
|
||||
ExpiresIn: int(h.TokenLifetime.Seconds()),
|
||||
IDToken: jwtToken,
|
||||
}
|
||||
|
||||
// 10. Emit token_issued telemetry.
|
||||
h.Emitter.Emit(ctx, telemetry.Event{
|
||||
Timestamp: time.Now(),
|
||||
EventType: telemetry.EventTokenIssued,
|
||||
ClientID: clientID,
|
||||
Endpoint: "/token",
|
||||
Result: "success",
|
||||
Scopes: sess.Scopes,
|
||||
GrantType: grantType,
|
||||
})
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
// basicClientCredentials reads client_secret_basic credentials, applying the
|
||||
// form-encoding decode RFC 6749 appendix B requires of both halves. Shared by
|
||||
// the service grant and confidential authorization-code client authentication.
|
||||
func basicClientCredentials(r *http.Request) (clientID, clientSecret string, ok bool) {
|
||||
clientID, clientSecret, ok = r.BasicAuth()
|
||||
if !ok {
|
||||
return "", "", false
|
||||
}
|
||||
decodedID, idErr := url.QueryUnescape(clientID)
|
||||
decodedSecret, secretErr := url.QueryUnescape(clientSecret)
|
||||
if idErr != nil || secretErr != nil {
|
||||
return "", "", false
|
||||
}
|
||||
return decodedID, decodedSecret, true
|
||||
}
|
||||
|
||||
// secretsEqual compares two secrets in constant time. Digesting first keeps the
|
||||
// comparison length-independent, so a wrong-length secret is indistinguishable
|
||||
// from a wrong-value one.
|
||||
func secretsEqual(presented, expected string) bool {
|
||||
presentedDigest := sha256.Sum256([]byte(presented))
|
||||
expectedDigest := sha256.Sum256([]byte(expected))
|
||||
return subtle.ConstantTimeCompare(presentedDigest[:], expectedDigest[:]) == 1
|
||||
}
|
||||
|
||||
func (h *TokenHandler) serveClientCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
clientID, clientSecret, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
profileerrors.InvalidProfileUsage("client_secret_basic authentication required", "Authorization").
|
||||
Write(w, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
clientID, idErr := url.QueryUnescape(clientID)
|
||||
clientSecret, secretErr := url.QueryUnescape(clientSecret)
|
||||
if idErr != nil || secretErr != nil {
|
||||
profileerrors.InvalidProfileUsage("invalid client authentication encoding", "Authorization").Write(w, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
client, ok := h.ClientConfig[clientID]
|
||||
if !ok || client.ClientType != "confidential" || !containsString(client.GrantTypes, "client_credentials") {
|
||||
profileerrors.InvalidProfileUsage("invalid confidential client", "client_id").
|
||||
Write(w, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
presentedDigest := sha256.Sum256([]byte(clientSecret))
|
||||
expectedDigest := sha256.Sum256([]byte(client.ClientSecret))
|
||||
if client.ClientSecret == "" ||
|
||||
subtle.ConstantTimeCompare(presentedDigest[:], expectedDigest[:]) != 1 {
|
||||
profileerrors.InvalidProfileUsage("invalid client authentication", "Authorization").
|
||||
Write(w, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
requestedScopes := strings.Fields(r.FormValue("scope"))
|
||||
if len(requestedScopes) == 0 {
|
||||
requestedScopes = append([]string(nil), client.AllowedScopes...)
|
||||
}
|
||||
for _, scope := range requestedScopes {
|
||||
if !containsString(client.AllowedScopes, scope) {
|
||||
profileerrors.InvalidProfileUsage("requested scope is not allowed", "scope").
|
||||
Write(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
tokenLifetime := h.TokenLifetime
|
||||
if client.TokenLifetime > 0 {
|
||||
tokenLifetime = client.TokenLifetime
|
||||
}
|
||||
claims := map[string]interface{}{
|
||||
"iss": h.Issuer,
|
||||
"sub": client.ServiceSubject,
|
||||
"aud": accessAudience(client),
|
||||
"exp": now.Add(tokenLifetime).Unix(),
|
||||
"iat": now.Unix(),
|
||||
"tenant": client.Tenant,
|
||||
// A service client's tenant is always registration-supplied: there is no
|
||||
// directory principal behind it to assert one (GH-DEC-2026-013 §5).
|
||||
"tenant_source": TenantSourceRegistration,
|
||||
"principal_type": "service",
|
||||
"groups": []string{},
|
||||
"roles": nonNilStrings(client.Roles),
|
||||
"scope": strings.Join(requestedScopes, " "),
|
||||
"assurance": map[string]interface{}{
|
||||
"level": "aal1", "methods": []string{"client_secret"},
|
||||
"mfa": false, "source": "key-cape", "at": now.Unix(),
|
||||
},
|
||||
}
|
||||
if roles, ok := h.TenantEngine.Roles(ctx, client.Tenant); ok {
|
||||
claims["tenant_roles"] = roles
|
||||
}
|
||||
if h.Tokens != nil {
|
||||
id := make([]byte, 32)
|
||||
if _, err := rand.Read(id); err != nil {
|
||||
http.Error(w, "token status unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
claims["jti"] = base64.RawURLEncoding.EncodeToString(id)
|
||||
}
|
||||
jwtToken, err := buildJWT(claims, "key-1", h.SigningKey)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to build JWT", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if h.Tokens != nil {
|
||||
if err := h.Tokens.register(jwtToken, client, time.Unix(now.Add(tokenLifetime).Unix(), 0), now); err != nil {
|
||||
http.Error(w, "token status unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
}
|
||||
h.Emitter.Emit(ctx, telemetry.Event{
|
||||
Timestamp: now, EventType: telemetry.EventTokenIssued, ClientID: clientID,
|
||||
Endpoint: "/token", Result: "success", Scopes: requestedScopes,
|
||||
GrantType: "client_credentials",
|
||||
})
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_ = json.NewEncoder(w).Encode(tokenResponse{
|
||||
AccessToken: jwtToken, TokenType: "Bearer",
|
||||
ExpiresIn: int(tokenLifetime.Seconds()),
|
||||
})
|
||||
}
|
||||
|
||||
func containsString(values []string, wanted string) bool {
|
||||
for _, value := range values {
|
||||
if value == wanted {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isSuspended(user *domain.User) bool {
|
||||
return containsString(user.Groups, "netkingdom-suspended")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IAM Profile core claims (KEY-WP-0005-T01)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// defaultTenant is the fallback tenant claim for users with no explicit
|
||||
// Tenant assignment yet. This workstation currently operates a single
|
||||
// tenant (tenant:coulomb, ADR-0006); later tenants (e.g. tenant:friendly:binky,
|
||||
// ADR-0013) require an explicit domain.User.Tenant value -- this default
|
||||
// never silently assigns a user to a tenant other than the platform's
|
||||
// original one.
|
||||
const defaultTenant = "tenant:coulomb"
|
||||
|
||||
// effectiveTenant resolves the tenant claim for a user, falling back to
|
||||
// defaultTenant when the user has no explicit tenant assignment. The IAM
|
||||
// Profile requires a non-empty tenant claim on every token.
|
||||
func effectiveTenant(user *domain.User) string {
|
||||
if user.Tenant != "" {
|
||||
return user.Tenant
|
||||
}
|
||||
return defaultTenant
|
||||
}
|
||||
|
||||
// humanTenant resolves the tenant claim for a human token (KEY-WP-0013-T05).
|
||||
//
|
||||
// A human's tenant is normally a property of the person, read from the
|
||||
// directory. But the approval chain is bound to the landlord zone by decision
|
||||
// 5ed3fb35-eca9-413a-82b9-95171ba85bf6, and approval-engine compares the claim
|
||||
// by exact string equality, so an approver client has to be able to state the
|
||||
// zone it issues into. Without this every human token fell back to
|
||||
// defaultTenant and would have been refused downstream -- as a failed approval
|
||||
// rather than as a registration defect.
|
||||
//
|
||||
// The rule is deliberately not an override:
|
||||
//
|
||||
// - no client tenant declared -> the directory answer, unchanged;
|
||||
// - declared, user unassigned -> the declared zone;
|
||||
// - declared and equal -> agreement, no ambiguity;
|
||||
// - declared and different -> refuse to issue.
|
||||
//
|
||||
// So a registration can bind a zone for users the directory has not placed, and
|
||||
// can never relabel a user the directory HAS placed into a different one. That
|
||||
// last case fails closed rather than picking a winner, because either answer
|
||||
// would be a silent cross-tenant assertion. It also means this stays correct if
|
||||
// the directory later populates Tenant: the same code turns from supplying the
|
||||
// zone into enforcing agreement with it, with no second migration.
|
||||
//
|
||||
// This is only safe because client registrations are static and
|
||||
// deployment-owned; KeyCape excludes dynamic client registration by design. A
|
||||
// self-service client that could name its users' tenant would be an escalation.
|
||||
func humanTenant(client *domain.Client, user *domain.User) (string, string, error) {
|
||||
if client == nil || client.Tenant == "" {
|
||||
if user.Tenant != "" {
|
||||
return user.Tenant, TenantSourceDirectory, nil
|
||||
}
|
||||
return defaultTenant, TenantSourceDefault, nil
|
||||
}
|
||||
if user.Tenant != "" {
|
||||
if user.Tenant != client.Tenant {
|
||||
return "", "", fmt.Errorf("client %q binds tenant %q but the directory assigns this user a different tenant", client.ClientID, client.Tenant)
|
||||
}
|
||||
// Agreement: the directory did assert this about the person, so the
|
||||
// stronger provenance is the true one.
|
||||
return user.Tenant, TenantSourceDirectory, nil
|
||||
}
|
||||
return client.Tenant, TenantSourceRegistration, nil
|
||||
}
|
||||
|
||||
// Tenant provenance values for the tenant_source claim (GH-DEC-2026-013 §5).
|
||||
//
|
||||
// A bare tenant string cannot tell a consumer whether the identity layer
|
||||
// asserted the zone about this PERSON or a registration supplied it about the
|
||||
// CLIENT they came through. approval-engine admits an approver by exact-matching
|
||||
// that string while its contract reads as though it relies on the first, so the
|
||||
// check is sound and the property a reader infers from it is absent. The ruling
|
||||
// requires the claim to carry its provenance and forbids a consumer treating the
|
||||
// two as equivalent for any decision turning on a fact about the person. It names
|
||||
// the property; the field is ours.
|
||||
//
|
||||
// Three values, not the two the ruling names, and the third is the point. A
|
||||
// tenant nobody asserted -- neither directory nor registration, just the
|
||||
// profile's non-empty default -- is not directory-asserted, and labelling it so
|
||||
// would reintroduce the same defect one level down: a consumer would read
|
||||
// "directory" for a fact the directory never stated. That is the unknown-versus-
|
||||
// absent distinction the ruling cites from GH-DEC-2026-011 §3, applied to our own
|
||||
// fallback rather than only to the case we were asked about.
|
||||
const (
|
||||
// TenantSourceDirectory: the identity layer asserted this zone about this
|
||||
// person. Includes the agreement case, where a registration declared the
|
||||
// same zone the directory did -- the directory still asserted it.
|
||||
TenantSourceDirectory = "directory"
|
||||
|
||||
// TenantSourceRegistration: supplied by the client registration for a person
|
||||
// the directory has placed nowhere. A fact about the client, not the person.
|
||||
TenantSourceRegistration = "registration"
|
||||
|
||||
// TenantSourceDefault: nobody asserted a zone; this is the profile default,
|
||||
// emitted because the profile requires a non-empty tenant. Weaker than both.
|
||||
TenantSourceDefault = "default"
|
||||
)
|
||||
|
||||
// nonNilStrings returns s, or an empty (non-nil) slice if s is nil, so the
|
||||
// claim always serializes as `[]`, never `null` -- the profile requires
|
||||
// groups/roles to be present, "possibly empty", not absent.
|
||||
func nonNilStrings(s []string) []string {
|
||||
if s == nil {
|
||||
return []string{}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// assuranceClaim builds the profile's `assurance` object from whether MFA
|
||||
// was actually verified during this authorization (session.MFAVerified),
|
||||
// not from static enrollment state -- a user who has MFA enrolled but
|
||||
// wasn't challenged for it in this particular flow gets aal1, not aal2.
|
||||
//
|
||||
// `at` is the time the user authenticated, not the time this token was
|
||||
// minted. Those differ whenever a browser session is reused, and the gap is
|
||||
// the whole point: approval-engine stores this object verbatim as the only
|
||||
// downstream record that MFA occurred (KEY-WP-0013-T05), so mint time would
|
||||
// overstate how recently the person proved anything. issuedAt is the
|
||||
// fallback for a session predating this field.
|
||||
func assuranceClaim(mfaVerified bool, authTime, issuedAt time.Time) map[string]interface{} {
|
||||
at := authTime
|
||||
if at.IsZero() {
|
||||
at = issuedAt
|
||||
}
|
||||
level := "aal1"
|
||||
methods := []string{"pwd"}
|
||||
if mfaVerified {
|
||||
level = "aal2"
|
||||
methods = append(methods, "otp")
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"level": level,
|
||||
"methods": methods,
|
||||
"mfa": mfaVerified,
|
||||
"source": "key-cape",
|
||||
"at": at.Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PKCE verification
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// verifyPKCE checks BASE64URL(SHA256(verifier)) == challenge (S256 method).
|
||||
func verifyPKCE(verifier, challenge string) bool {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(verifier))
|
||||
computed := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
|
||||
return computed == challenge
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// JWT construction (stdlib only — no external JWT library)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type jwtHeader struct {
|
||||
Alg string `json:"alg"`
|
||||
Typ string `json:"typ"`
|
||||
Kid string `json:"kid"`
|
||||
}
|
||||
|
||||
// buildJWT constructs and signs a JWT using RSA-SHA256 with the standard library.
|
||||
// Format: base64url(header) + "." + base64url(payload) + "." + base64url(signature)
|
||||
func buildJWT(claims map[string]interface{}, kid string, key *rsa.PrivateKey) (string, error) {
|
||||
// Header.
|
||||
hdr := jwtHeader{Alg: "RS256", Typ: "JWT", Kid: kid}
|
||||
hdrJSON, err := json.Marshal(hdr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hdrB64 := base64.RawURLEncoding.EncodeToString(hdrJSON)
|
||||
|
||||
// Payload.
|
||||
payloadJSON, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
payloadB64 := base64.RawURLEncoding.EncodeToString(payloadJSON)
|
||||
|
||||
// Signing input.
|
||||
signingInput := hdrB64 + "." + payloadB64
|
||||
|
||||
// Digest.
|
||||
digest := sha256.Sum256([]byte(signingInput))
|
||||
|
||||
// Sign with PKCS1v15 / SHA256.
|
||||
sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, digest[:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sigB64 := base64.RawURLEncoding.EncodeToString(sig)
|
||||
|
||||
return strings.Join([]string{hdrB64, payloadB64, sigB64}, "."), nil
|
||||
}
|
||||
|
||||
// accessAudience is configured by the issuer, never selected by request input.
|
||||
func accessAudience(client *domain.Client) string {
|
||||
if client.Audience != "" {
|
||||
return client.Audience
|
||||
}
|
||||
return client.ClientID
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package oidc_test
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"keycape/internal/jose"
|
||||
)
|
||||
|
||||
func TestOnlineHumanIDTokenBindsExactAccessGrant(t *testing.T) {
|
||||
f := newStatusFixture(t, 10)
|
||||
f.token.ClientConfig["test-client"].Audience = "resource:forgejo"
|
||||
first, firstID, code := f.issue(t)
|
||||
second, secondID, secondCode := f.issue(t)
|
||||
if code != http.StatusOK || secondCode != http.StatusOK || first == second {
|
||||
t.Fatal("distinct actual grants were not issued")
|
||||
}
|
||||
keys := jose.KeySet{"key-1": &f.token.SigningKey.PublicKey}
|
||||
for _, pair := range [][2]string{{first, firstID}, {second, secondID}} {
|
||||
claims, err := jose.VerifyTyped(pair[1], keys, "JWT")
|
||||
if err != nil {
|
||||
t.Fatal("actual ID signature refused")
|
||||
}
|
||||
digest := sha256.Sum256([]byte(pair[0]))
|
||||
if claims["at_hash"] != base64.RawURLEncoding.EncodeToString(digest[:16]) || claims["aud"] != "test-client" {
|
||||
t.Fatal("ID token lost exact access-grant or relying-party binding")
|
||||
}
|
||||
if _, ok := claims["jti"]; ok {
|
||||
t.Fatal("access-grant jti escaped into ID token")
|
||||
}
|
||||
if _, ok := claims["scope"]; ok {
|
||||
t.Fatal("access scope escaped into ID token")
|
||||
}
|
||||
access, err := jose.VerifyTyped(pair[0], keys, "JWT")
|
||||
if err != nil || access["aud"] != "resource:forgejo" {
|
||||
t.Fatal("resource audience changed")
|
||||
}
|
||||
if _, ok := access["at_hash"]; ok {
|
||||
t.Fatal("ID-only grant binding escaped into access token")
|
||||
}
|
||||
}
|
||||
firstClaims, _ := jose.VerifyTyped(firstID, keys, "JWT")
|
||||
secondClaims, _ := jose.VerifyTyped(secondID, keys, "JWT")
|
||||
if firstClaims["at_hash"] == secondClaims["at_hash"] {
|
||||
t.Fatal("distinct grants shared an ID-token access binding")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisabledOnlineProfileKeepsLegacyIDToken(t *testing.T) {
|
||||
f := newStatusFixture(t, 10)
|
||||
f.token.Tokens = nil
|
||||
_, id, code := f.issue(t)
|
||||
claims, err := jose.VerifyTyped(id, jose.KeySet{"key-1": &f.token.SigningKey.PublicKey}, "JWT")
|
||||
if code != http.StatusOK || err != nil {
|
||||
t.Fatal("legacy profile failed")
|
||||
}
|
||||
if _, ok := claims["at_hash"]; ok {
|
||||
t.Fatal("disabled profile changed ID-token contract")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
id: KEY-WP-0042
|
||||
type: workplan
|
||||
title: "Provide issuer-owned current access-token status and scoped revocation"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: proposed
|
||||
owner: codex
|
||||
topic_slug: custodian
|
||||
created: "2026-10-18"
|
||||
updated: "2026-10-18"
|
||||
flavor: extension
|
||||
depends_on: []
|
||||
state_hub_workstream_id: "e2eecd1c-45c7-5a7c-bd1c-d31c845e3d38"
|
||||
---
|
||||
|
||||
CUST-WP-0080's current-operation/inverse requirement exposed a concrete missing
|
||||
issuer facility. KEY-WP-0025-T03 truthfully documents the expiry-only limit;
|
||||
KEY-WP-0038 owns Forgejo's existing client, not a token-liveness API. This is an
|
||||
explicit proposed scope extension in the existing issuer, not a silent change
|
||||
to that completed limit record. No new service or duplicate session authority.
|
||||
Consumer binding/authorization and native case acceptance stay in
|
||||
NK-WP-0048-T34/T42/T43 and RAPPS-WP-0016-T08/T09; they are not copied here.
|
||||
|
||||
## Review the bounded issuer/profile extension
|
||||
|
||||
```task
|
||||
id: KEY-WP-0042-T01
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "d8af8e77-f6e5-5d0d-8b44-794af3dee5cf"
|
||||
```
|
||||
|
||||
The reviewable contract is `docs/online-token-status.md`: opt-in RFC 7662/7009
|
||||
access-token status; registered owning-client authentication; private hash-only
|
||||
bounded state; unique access-token jti; online human ID-token exact `at_hash`
|
||||
binding with the disabled legacy ID-token contract unchanged; singleton store-loss
|
||||
refusal; disabled legacy behavior explicit. Source preparation is authorized by
|
||||
the full Custodian implementation goal. Independent issuer/profile/consumer
|
||||
assent remains required before admission; no such assent is inferred here.
|
||||
|
||||
## Implement and verify the actual issuer paths
|
||||
|
||||
```task
|
||||
id: KEY-WP-0042-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "2cade51f-d25a-5719-96b4-b57d50c1b97d"
|
||||
```
|
||||
|
||||
Wire the same issuer store to both actual grant types, UserInfo, introspection,
|
||||
revocation, configuration validation and implemented discovery. Verify distinct
|
||||
grants, exact expiry, replay/tombstones, capacity, malformed/unauthenticated and
|
||||
cross-client refusals, current registration and human disposition, revocation
|
||||
during directory lookup, process-store loss and absence of secret output. Keep
|
||||
the complete existing source suite, disabled profile and build passing.
|
||||
|
||||
2026-10-18 T02 source continuation: the actual consumer join exposed missing
|
||||
same-grant correspondence. The online human ID token now signs the exact access
|
||||
token's RS256 `at_hash`, retaining its own audience and excluding access-only
|
||||
fields. Distinct same-second grants cannot share that binding; disabled issuance
|
||||
is unchanged. This qualifies the earlier unscoped "no ID-token change" statement
|
||||
for the proposed online profile, without changing existing deployment behavior.
|
||||
The complete source race suite passes 726 test/subtest results with the same six
|
||||
native/external skips. The actual HTTPS PKCE/discovery/UserInfo/status/revocation
|
||||
handlers and real Forgejo provider/callback pass five separate consumer processes:
|
||||
two current grants accepted, mixed grants/revoked original/suspended human refused.
|
||||
The original issuer fails the same current consumer. MFA session seeds and PDP
|
||||
admissions are synthetic. Source-only evidence: Custodian forgejo-current-token-r1.
|
||||
Independent issuer/profile/consumer review and T03 native admission remain open.
|
||||
|
||||
## Admit and accept the issuer lifecycle at native scope
|
||||
|
||||
```task
|
||||
id: KEY-WP-0042-T03
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "a5539bb0-2501-5774-a7d8-acddfb0938ab"
|
||||
```
|
||||
|
||||
After independent contract review, admit exact issuer image/config/profile,
|
||||
HTTPS ingress/client custody and measured capacity/headroom. Exercise real
|
||||
human PKCE and service tokens, actual endpoint authentication, revoked-token
|
||||
UserInfo refusal, expiry, unavailable dependency, restart/store loss and fresh
|
||||
reauthentication. Preserve old offline-token semantics and document every
|
||||
consumer's selected mode. Review disabling/rollback explicitly; never silently
|
||||
fall back to offline acceptance for online-profile consumers. Provide owner
|
||||
receipts to existing NK48/Apps consumer tasks and Audit source admission.
|
||||
|
||||
2026-10-18 source verification: all 659 test/subtest results in the complete
|
||||
race suite pass, with six preexisting native/external skips. The standalone
|
||||
binary passes 26 isolated process checks, including actual main/router/config,
|
||||
service issuance, capacity, revocation, process restart, fresh issuance and
|
||||
disabled-profile behavior; fixture diagnostics omit synthetic private inputs.
|
||||
The HTTPS human PKCE flow and revocation during directory lookups also pass.
|
||||
An isolated control omitting the revoked-state predicate fails seven intended
|
||||
checks. This closes source task T02 only. T01 independent issuer/profile review,
|
||||
T03 native admission and all existing consumer operation/effect bindings remain
|
||||
open. Evidence is retained by CUST-WP-0080 under keycape-token-status-r1.
|
||||
|
||||
## Provide the shared producer client and owner-private command observations
|
||||
|
||||
```task
|
||||
id: KEY-WP-0042-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "4c71c9f7-8e14-5fec-8491-d7a7995f97e2"
|
||||
```
|
||||
|
||||
2026-10-18 source outcome: `internal/authclient/online.go` owns strict current
|
||||
token/status joining, explicit HTTPS/CA/JWKS/current-credential admission,
|
||||
identity-first credential access, per-check rereads, response/clock/lifetime
|
||||
rechecks and independent read-only inactive observation. The existing executable
|
||||
exposes `check-current-token`, `revoke-token`, `check-inactive-token`, with exact
|
||||
token digest binding and new private observation outputs outside Git. Negative
|
||||
output is explicitly inactive-for-client, never global revocation/ownership.
|
||||
The public five-file Go adapter is generated from the same client and JOSE bytes;
|
||||
ordinary source checks refuse drift. Source/contract: `docs/current-token-client.md`.
|
||||
|
||||
The full source race suite passes 724 test/subtest results with the same six
|
||||
native/external skips. Human PKCE tests retain actual subject/assurance/auth-time/
|
||||
tenant provenance and refuse ID-token substitution. A separate source fixture
|
||||
uses the built CLI, generated Go consumer and actual Warden kernel across fresh
|
||||
owner processes, with real fixture HTTPS status and independent negative cleanup
|
||||
checks. Owner approvals/case/catalog and target are synthetic; there is no native
|
||||
admission or second real application acceptance. The first CLI fixture's directory
|
||||
permissions, archive-only Warden Git metadata failure and original API absence are
|
||||
retained as failures, not backfilled successes. Source/evidence:
|
||||
`the-custodian/docs/changes/netkingdom-target-2026-10-18/keycape-current-client-r1/`.
|
||||
T01 independent issuer/profile review, T03 native lifecycle, NK48/Apps actual
|
||||
operation/inverse/ongoing-resource bindings and all full target gates remain open.
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
//go:build forgejo_current_fixture
|
||||
|
||||
package authclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"keycape/internal/domain"
|
||||
"keycape/internal/server/oidc"
|
||||
"keycape/internal/server/telemetry"
|
||||
)
|
||||
|
||||
type forgejoFixtureDirectory struct {
|
||||
mu sync.RWMutex
|
||||
user domain.User
|
||||
}
|
||||
|
||||
func (d *forgejoFixtureDirectory) LookupUser(context.Context, string) (*domain.User, error) {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
user := d.user
|
||||
user.Groups = append([]string(nil), d.user.Groups...)
|
||||
return &user, nil
|
||||
}
|
||||
func (d *forgejoFixtureDirectory) LookupGroups(context.Context, string) ([]domain.Group, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (d *forgejoFixtureDirectory) ListUsers(ctx context.Context) ([]domain.User, error) {
|
||||
user, _ := d.LookupUser(ctx, "")
|
||||
return []domain.User{*user}, nil
|
||||
}
|
||||
func (d *forgejoFixtureDirectory) ValidatePassword(context.Context, string, string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func TestOnlineActualForgejoCallbackCurrentIssuer(t *testing.T) {
|
||||
f := onlineProvider(t)
|
||||
directory := &forgejoFixtureDirectory{user: domain.User{ID: "uid=operator,ou=people,dc=fixture", Username: "operator", Enabled: true, Tenant: "tenant:fixture", Groups: []string{"forge-admins"}}}
|
||||
client := f.issuer.ClientConfig["current-client"]
|
||||
client.GrantTypes = []string{"authorization_code"}
|
||||
client.ServiceSubject = ""
|
||||
client.Audience = client.ClientID
|
||||
client.AllowedScopes = []string{"openid", "profile", "groups"}
|
||||
client.RedirectURIs = []string{"https://forgejo.invalid/callback"}
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewTLSServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
f.issuer.Users = directory
|
||||
f.issuer.Issuer = server.URL
|
||||
f.status.Users = directory
|
||||
f.status.Issuer = server.URL
|
||||
userinfo := &oidc.UserinfoHandler{Users: directory, SigningKey: &f.issuer.SigningKey.PublicKey, Issuer: server.URL, Tokens: f.issuer.Tokens, ClientConfig: f.issuer.ClientConfig, Emitter: telemetry.NoopEmitter{}}
|
||||
var statusCalls, userinfoCalls atomic.Int64
|
||||
mux.Handle("/token", f.issuer)
|
||||
mux.HandleFunc("/introspect", func(w http.ResponseWriter, r *http.Request) { statusCalls.Add(1); f.status.Introspect(w, r) })
|
||||
mux.HandleFunc("/revoke", f.status.Revoke)
|
||||
mux.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) { userinfoCalls.Add(1); userinfo.ServeHTTP(w, r) })
|
||||
keys := oidc.NewKeySet()
|
||||
keys.AddKey("key-1", &f.issuer.SigningKey.PublicKey)
|
||||
mux.Handle("/jwks", oidc.NewJWKSHandler(keys))
|
||||
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"issuer": server.URL, "authorization_endpoint": server.URL + "/authorize", "token_endpoint": server.URL + "/token", "userinfo_endpoint": server.URL + "/userinfo", "jwks_uri": server.URL + "/jwks", "id_token_signing_alg_values_supported": []string{"RS256"}})
|
||||
})
|
||||
admission := f.client.admission
|
||||
admission.Issuer = server.URL
|
||||
admission.Audience = client.ClientID
|
||||
if os.WriteFile(admission.CAFile, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.Certificate().Raw}), 0644) != nil {
|
||||
t.Fatal("fixture CA write failed")
|
||||
}
|
||||
var err error
|
||||
f.client, err = NewOnlineClient(admission)
|
||||
if err != nil {
|
||||
t.Fatal("fixture admission failed")
|
||||
}
|
||||
der, err := x509.MarshalPKIXPublicKey(&f.issuer.SigningKey.PublicKey)
|
||||
if err != nil {
|
||||
t.Fatal("fixture public key failed")
|
||||
}
|
||||
publicKey := string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))
|
||||
issue := func() Tokens {
|
||||
verifier := strings.Repeat("v", 43)
|
||||
digest := sha256.Sum256([]byte(verifier))
|
||||
code := f.issuer.Sessions.Create(&oidc.PKCESession{ClientID: client.ClientID, Username: directory.user.Username, RedirectURI: client.RedirectURIs[0], PKCEChallengeMethod: "S256", PKCEChallenge: base64.RawURLEncoding.EncodeToString(digest[:]), Scopes: client.AllowedScopes, MFAVerified: true, AuthTime: time.Now().Add(-time.Minute).Truncate(time.Second), ExpiresAt: time.Now().Add(time.Minute)})
|
||||
form := url.Values{"grant_type": {"authorization_code"}, "client_id": {client.ClientID}, "code": {code}, "code_verifier": {verifier}, "redirect_uri": {client.RedirectURIs[0]}}
|
||||
r, _ := http.NewRequest(http.MethodPost, server.URL+"/token", strings.NewReader(form.Encode()))
|
||||
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
r.SetBasicAuth(url.QueryEscape(client.ClientID), url.QueryEscape(client.ClientSecret))
|
||||
response, err := server.Client().Do(r)
|
||||
if err != nil {
|
||||
t.Fatal("actual fixture token HTTP failed")
|
||||
}
|
||||
defer response.Body.Close()
|
||||
var tokens Tokens
|
||||
if response.StatusCode != 200 || json.NewDecoder(response.Body).Decode(&tokens) != nil || tokens.AccessToken == "" || tokens.IDToken == "" {
|
||||
t.Fatal("actual fixture human PKCE failed")
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
first, second := issue(), issue()
|
||||
directoryPath := t.TempDir()
|
||||
var phases []string
|
||||
probe := func(tokens Tokens, phase string) {
|
||||
input := map[string]any{"id_token": tokens.IDToken, "access_token": tokens.AccessToken, "public_key_pem": publicKey, "tenant": directory.user.Tenant, "phase": phase, "current": admission}
|
||||
raw, _ := json.Marshal(input)
|
||||
file := filepath.Join(directoryPath, "consumer-input.json")
|
||||
if os.WriteFile(file, raw, 0600) != nil {
|
||||
t.Fatal("private fixture input failed")
|
||||
}
|
||||
command := exec.Command(os.Getenv("FORGEJO_CURRENT_TEST_BINARY"), "-test.run=^TestAccessEngineLiveFixtureCurrentIssuer$", "-test.v")
|
||||
command.Env = append(os.Environ(), "FORGEJO_CURRENT_ISSUER_FIXTURE="+file)
|
||||
output, err := command.CombinedOutput()
|
||||
for _, private := range []string{first.AccessToken, first.IDToken, second.AccessToken, second.IDToken, client.ClientSecret} {
|
||||
if bytes.Contains(output, []byte(private)) {
|
||||
t.Fatal("consumer fixture diagnostic disclosed private input")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("actual consumer phase %s failed: %s", phase, output)
|
||||
}
|
||||
phases = append(phases, phase)
|
||||
}
|
||||
probe(first, "active")
|
||||
probe(Tokens{AccessToken: second.AccessToken, IDToken: first.IDToken}, "mixed-pair")
|
||||
if _, err := f.client.Revoke(context.Background(), first.AccessToken); err != nil {
|
||||
t.Fatal("actual fixture revoke/independent negative failed")
|
||||
}
|
||||
probe(first, "revoked-after-userinfo")
|
||||
probe(second, "active")
|
||||
directory.mu.Lock()
|
||||
directory.user.Groups = []string{"netkingdom-suspended"}
|
||||
directory.mu.Unlock()
|
||||
probe(second, "suspended-after-userinfo")
|
||||
report := map[string]any{"status": "passed", "consumer_processes": len(phases), "phases": phases, "actual_https_human_PKCE": true, "actual_discovery_and_userinfo": true, "actual_introspection_and_revocation": true, "status_calls": statusCalls.Load(), "userinfo_calls": userinfoCalls.Load(), "actual_forgejo_callback_and_generated_client": true, "synthetic_MFA_session_seed_and_PDP_admission": true, "native_acceptance": false, "private_inputs": "fixture memory/tmpfs only"}
|
||||
raw, _ := json.MarshalIndent(report, "", " ")
|
||||
if os.WriteFile(os.Getenv("FORGEJO_CURRENT_REPORT"), append(raw, '\n'), 0600) != nil {
|
||||
t.Fatal("public fixture result failed")
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue