External Access Control
External Access Control is the tenant-level policy for which training assignments a learner must complete before they can carry out a specific action.
An admin decides which assignments are required in the External Access Control settings.
Gating functionality is built into the aspen-connector (SecurityJourney's CI connector), a tool that can be added as a step in GitHub or GitLab CI to wire various Aspen capabilities, as well as external access control, into your pipeline.
How External Access Control Works
Gating functionality runs as a step in your CI pipeline. On each run, it:
- Resolves the committer's email from the CI platform.
POST`s{"emails": ["dev@example.com"]}` to Security Journey's external access control status endpoint and reads back the result for that email.- Fails the pipeline with the reason returned if the committer isn't compliant with the External Access Control policy.
- Optionally posts that reason as a comment on the pull/merge request, so the committer doesn't have to dig through CI logs to find the reason for the failure.
Note: A failed pipeline does not block a merge by itself. GitHub and GitLab both let a PR/MR merge past a failing check unless you explicitly mark this job as required (see Integration below). Because the check is identity-based rather than code-based, this check is typically used as a merge gate: set it as a required status check (GitHub) or a required pipeline (GitLab) so a non-compliant committer's changes actually can't merge until they complete the outstanding assignments.
Configuration and Setup
Configuring External Access Control
Which assignments are required is configured in Security Journey's admin settings, not in the pipeline. The connector has no input for scoping or overriding this per-repo; it always enforces whatever the tenant has set up.
- In your Security Journey tenant admin settings, there is a card called
External Access Control. - Under the
Required Assignmentssetting, select every training assignment a learner must complete to satisfy the policy. Only active assignments can be newly selected; if a required assignment is later deactivated or deleted, it stays visible in the list (flagged) so you can catch and clean up stale requirements rather than having it silently drop out. - Selections save immediately.
This tenant-wide list is what Aspen evaluates a learner's training status against, whenever any entrypoint checks compliance (currently only through the aspen-connector).
Prerequisites
-
A Security Journey Aspen API token
(ASPEN_API_TOKEN)with theapi:aspen_readrole. If you're reusing a token provisioned for Adapt/Guardian, confirm it has READ permissions before wiring up gating functionality. -
Pull/merge request comments (optional, on by default): the pipeline needs write access to post PR/MR comments.
- GitHub Actions:
permissions: pull-requests: writeon the job. Note that GitHub forcesGITHUB_TOKENto read-only onpull_requestworkflows triggered from a fork, regardless of this setting. Commenting will silently skip (with a warning) on fork PRs. - GitLab CI: either a project/group access token with the
apiscope (GITLAB_TOKEN), or CI/CD job token API access enabled for the projectSettings → CI/CD → Token Access.
- GitHub Actions:
- No git write access is required for the gate check itself, only for commenting, and only PR/MR comments, never a commit-back.
- GitLab only: make sure the pipeline's checkout has enough history for the triggering commit to be reachable (
GIT_DEPTH: 0is the safe default). Gating functionality reads the committer's email straight fromgit logon that commit rather than GitLab's predefined CI/CD variables, which aren't reliable on merge-request pipelines. A shallow clone can leave that lookup with nothing to fall back on butGITLAB_USER_EMAIL(the pipeline-triggering account's email, which may not match the actual commit author).
Integration
Gating functionality integrates via the aspen-connector, same as Adapt and Guardian. Today, this is the connector's CI-side implementation of External Access Control enforcement.
GitHub Actions example:
permissions:
pull-requests: write # needed for gate_comment_on_failure
steps:
- uses: actions/checkout@v4
- uses: SecurityJourney/aspen-connector@v0.2.0
with:
api_token: $
enforce_gate: 'true'
# Optional overrides:
# gate_fail_open: 'true'
# gate_comment_on_failure: 'true'
GitLab CI example:
aspen_gate:
image: node:22
script:
- npx @securityjourney/aspen-connector@0.2.0
variables:
ASPEN_API_TOKEN: $SECURITYJOURNEY_TOKEN
ASPEN_ENFORCE_GATE: 'true'
# Optional overrides:
# ASPEN_GATE_FAIL_OPEN: 'true'
# ASPEN_GATE_COMMENT_ON_FAILURE: 'true'
To block merges on a failed gate, mark this job as a required status check (GitHub branch protection) or require successful pipelines (GitLab merge request settings).
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
ASPEN_API_TOKEN |
Yes | None | Security Journey API token |
ASPEN_ENFORCE_GATE |
Yes | False | Set `true` to run the gate check |
ASPEN_GATE_FAIL_OPEN |
No | True | If `false`, blocks CI when the gate endpoint reports an internal error, times out, or returns something unreadable. This only covers our own infrastructure failing to answer. It never overrides an actual non-compliant result. See below. |
ASPEN_GATE_COMMENT_ON_FAILURE |
No | True | Posts the gate failure reason as a comment on the PR/MR when the committer is non-compliant |
How a Commit Is Evaluated
Gating functionality looks up the committer's compliance status and evaluates the result matching their email (falling back to the first result if no exact match is found). If Security Journey reports a reason for non-compliance, that's used verbatim; otherwise, the connector builds a fallback reason from the required assignments the committer hasn't completed. If the committer isn't compliant, the pipeline fails with that reason.
A required assignment that hasn't been assigned to the user doesn't block
-
required assignments are configured tenant-wide, but each assignment has its own target audience, so different teams can legitimately have different assignments. A committer outside an assignment's target audience will correctly come back not assigned to it. That's expected scoping, not a misconfiguration, so it's excluded from the compliance check rather than treated as a failure. The connector still logs a non-blocking
::warning::naming any such assignments every time the gate runs, so it doesn't go unnoticed.
Fail-Open Behavior
ASPEN_GATE_FAIL_OPEN defaults to true, which covers only these infrastructure cases, never a real non-compliant answer:
- the gate endpoint is unreachable, times out (10s), or returns non-2xx or unparseable JSON
- the response has no result matching the committer's email
- a result carries a per-user
error(e.g. an unrecognized email, meaning Aspen couldn't determine compliance, as opposed to determining the committer is non-compliant) - a result is missing the
compliantfield entirely - the committer's email couldn't be resolved from the CI platform at all
Set ASPEN_GATE_FAIL_OPEN=false to block CI whenever the gate check can't be completed for any of the above, rather than letting the pipeline through.
PR/MR Comments
-
When a commit is blocked and
ASPEN_GATE_COMMENT_ON_FAILUREistrue(the default), Gating functionality posts the failure reason as a comment on the associated pull/merge request, so a developer sees _why_ their branch is blocked without leaving GitHub/GitLab. Comment failures (e.g. missing permissions) are logged as warnings and never override the underlying gate result. The pipeline still fails or passes based on the gate check itself. - Comments are skipped when there's no PR/MR context (e.g. a
pushpipeline); only the gate check runs.
For complete configuration options, see the Aspen Connector GitHub repository. For other questions, contact support@securityjourney.com.