Accept managed collector service tokens on the collector API
A token scoped to the new collector.ingest permission is a collector service token: the collector endpoints accept it via X-API-Key or Bearer alongside the env fleet keys (which remain the fallback), giving the fleet credential rotation, revocation, and last-used visibility from the API Tokens page. Containment holds both ways: a collector token authorizes nothing else, and no other credential gains collector access. Shared token validation refactored out of the auth shim; a Collector service token quick-preset in the create modal; integration guide documents minting, rotation via site-config.json, and the service-identity pattern. 765 tests pass; live acceptance matrix verified. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,18 @@
|
||||
<small class="form-hint">Leave blank for a token that never expires.</small>
|
||||
</div>
|
||||
|
||||
<div v-if="canCollectorPreset" class="form-group preset-group">
|
||||
<button type="button" class="btn btn-secondary btn-sm"
|
||||
@click="applyCollectorPreset">
|
||||
Collector service token
|
||||
</button>
|
||||
<small class="form-hint">
|
||||
For GE-Enforce / fleet reporting. Pre-selects only
|
||||
<code>collector.ingest</code>; the token works only on the
|
||||
collector API and nothing else.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="form.restrict" />
|
||||
@@ -308,6 +320,17 @@ const availableGrouped = computed(() => {
|
||||
return result
|
||||
})
|
||||
|
||||
// The collector preset is offered only when the caller can actually mint a
|
||||
// collector.ingest token (admins hold everything; others must hold the perm).
|
||||
const COLLECTOR_SCOPE = 'collector.ingest'
|
||||
const canCollectorPreset = computed(
|
||||
() => isAdmin.value || myPermissions.value.includes(COLLECTOR_SCOPE))
|
||||
|
||||
function applyCollectorPreset() {
|
||||
form.value.restrict = true
|
||||
form.value.scopes = [COLLECTOR_SCOPE]
|
||||
}
|
||||
|
||||
onMounted(() => loadData())
|
||||
|
||||
async function loadData() {
|
||||
@@ -499,6 +522,17 @@ async function revokeToken() {
|
||||
color: var(--text-light);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.preset-group {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg);
|
||||
}
|
||||
.preset-group code {
|
||||
background: var(--bg-card);
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.secret-warning {
|
||||
color: var(--danger);
|
||||
margin-bottom: 1rem;
|
||||
|
||||
Reference in New Issue
Block a user