Extension permissions
Cove has two different extension permission surfaces:
extension.jsoncan declare scraper and downloader runtime needs.- Compiled extensions can contribute assignable role permissions with
IPermissionContributor.
They are not interchangeable. A manifest declaration does not grant a Cove user a role permission, and registering a role permission does not automatically protect extension code.
Manifest runtime declarations
Section titled “Manifest runtime declarations”The permissions object has exactly three lists:
{ "permissions": { "network": ["metadata.example.invalid"], "scraperRuntime": ["javascript"], "downloaderRuntime": [] }}| Field | Intended declaration |
|---|---|
network | Network hosts the package expects to contact. |
scraperRuntime | Extra scraper runtime capabilities the package expects, such as JavaScript or browser debugging. |
downloaderRuntime | Extra downloader runtime capabilities the package expects. |
These values currently deserialize as arbitrary strings. Cove does not validate a vocabulary, present an approval grant, or use these lists as a runtime authorization boundary. Treat them as package metadata, not a sandbox policy.
For current provider requests, Cove derives AllowNetworkHosts from the host in the requested URL. Name-only scraper searches receive no allowed host, and scraper AllowJavaScript and AllowCdp remain false. Downloader requests likewise receive the requested URL host. A provider must honor the ScraperPermissions or DownloaderPermissions passed with each request and must not infer access from its manifest.
Contribute assignable permissions
Section titled “Contribute assignable permissions”Implement IPermissionContributor when administrators should be able to grant a compiled extension capability through Settings → Security & Access → Roles.
using Cove.Core.Auth;using Cove.Plugins;using Cove.Sdk;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Routing;
namespace Example.Reports;
public sealed class ReportsExtension : CoveExtensionBase, IPermissionContributor, IApiExtension{ private const string ViewReports = "com.example.reports.read";
public IEnumerable<PermissionDefinition> ContributePermissions() => [ new( Key: ViewReports, Category: "Example reports", Description: "View generated library reports.") ];
public void MapEndpoints(IEndpointRouteBuilder endpoints) { endpoints.MapGet( "/api/ext/com.example.reports/summary", () => Results.Ok(new { status = "ready" })) .RequireCovePermission(ViewReports); }}PermissionDefinition fields
Section titled “PermissionDefinition fields”| Field | Behavior |
|---|---|
Key | Stable machine key. It must start with the exact extension id plus ., such as com.example.reports.read. |
Category | Group heading in the permission catalog and role editor. |
Description | Administrator-facing description of what the permission permits. |
Dangerous | Marks a high-impact capability for stronger visual review. Default false. |
Implies | Optional permission keys expanded when this permission is granted. Use only keys that are meaningful to the host. |
Source | The registry overwrites this with extension:<extension-id>. Do not depend on a value supplied by the extension. |
GrantToAdminsByDefault | When true, Cove adds this key to the built-in Admin role during catalog refresh. Default false; use it deliberately. |
The registry rejects * and every key outside the contributor’s <extension-id>. namespace. Accepted definitions are added to the in-memory catalog, persisted to the permission table, and exposed to role management. If a definition disappears on a later startup, its persisted catalog row is marked orphaned rather than silently reused as a live definition.
Permission contributors are collected after extensions initialize during Cove startup. Installing a new contributor at runtime does not refresh that collection in the current host; restart Cove before expecting its keys in the role editor.
Protect extension API endpoints
Section titled “Protect extension API endpoints”IPermissionContributor registers metadata. It does not guard an endpoint, job, event handler, background worker, or service call.
Cove evaluates authorization metadata on extension minimal APIs before it switches the request into the extension’s service container. Use the conventions from Cove.Sdk:
| Convention | Behavior |
|---|---|
RequireCovePermission(permission...) | Requires an authenticated principal with every listed permission. |
RequireCovePermission(PermissionMode.Any, permission...) | Requires at least one listed permission. PermissionMode.All is the explicit form of the default. |
RequireCoveEntityAccess(entityKind, routeValueName, permission) | Authorizes the declared permission against the positive integer entity id in the named route value. |
RequireCoveEntityAccess(entityKind, routeValueName) | Infers the entity permission only when the endpoint declares exactly one distinct Cove permission. |
AllowWithoutCovePermission() | Requires an authenticated Cove principal but no specific permission. |
AllowCoveAnonymous() | Explicitly allows a request without a Cove principal. |
Permission and entity requirements compose: every attached requirement must pass. Do not combine either explicit allow convention with another allow convention or with a permission or entity requirement; Cove treats conflicting metadata as an invalid policy and denies the request.
For backward compatibility, an extension endpoint with none of these Cove conventions remains anonymously accessible. Cove writes one warning each time it registers an affected extension, including the total endpoint count and at most ten route templates. Treat that warning as a migration task: new endpoints should always declare their intended access. ASP.NET Core’s AllowAnonymous attribute alone is not a Cove authorization declaration; use AllowCoveAnonymous() when anonymous access is intentional.
Route-bound entity authorization is for ids present in the route. If an entity id comes from a request body, validate the body and call Cove’s IAuthorizationService before performing the operation.
- A job or other non-HTTP entry point must receive or resolve the calling principal where its host contract provides one and make the same authorization decision before work begins.
- A service method reachable through several entry points should enforce any invariant that cannot safely depend on every caller attaching equivalent endpoint metadata.
- Do not use raw role names as authorization. Roles collect permissions; the permission key is the stable capability boundary.
API token scope is an additional restriction. Cove intersects an API token’s scope with the token owner’s current role permissions, so a scoped token cannot expand the permission contributed by the extension.
Gate extension UI contributions
Section titled “Gate extension UI contributions”Permission metadata keeps unavailable extension navigation and controls out of the interface, but it is presentation logic rather than a server-side boundary.
UIPageDefinition.RequiredPermissionrequires one permission. A non-emptyRequiredPermissionsarray takes precedence and usesRequiredPermissionMode, which defaults toAlland also supportsAny.- Unauthorized contributed pages are omitted from navigation. Direct navigation renders Cove’s access-denied page instead of the extension component.
UITabContributionsupports the same singular permission, array, and mode fields. Unauthorized entity tabs are not returned to detail pages.ExtensionAction.RequiredPermissionhides the action in supported toolbar and bulk-action locations.
Use the UIManifestBuilder overloads that accept a complete UIPageDefinition or UITabContribution when setting the array and mode fields. The builder replaces any supplied ExtensionId with the owning extension id.
Always attach matching server-side authorization to the API used by a page, tab, or action. A user can issue an HTTP request without rendering the contributed UI.
Contribute content policies
Section titled “Contribute content policies”IContentPolicyContributor has this contract:
IReadOnlyList<ContentPolicy> ContributePolicies();A ContentPolicy has a stable id, an EntityKind, a description, a required CanRead predicate, and optional CanWrite and CanDelete predicates. Predicates receive a ContentEvaluationContext containing the current CovePrincipal, entity kind and id, and request service provider. Name policy ids with the extension id so logs and inspection remain unambiguous.
This contract is reserved for a later enforcement stage. Cove currently registers contributed policies for inspection and audit, but they are not composed into the EF query authorization filters, and no general request consumer calls their predicates. Do not claim that an IContentPolicyContributor hides or protects content today. Use Cove’s built-in role content rules for supported content visibility, and enforce extension-owned access explicitly in the extension’s server-side operations.
Review checklist
Section titled “Review checklist”Before release:
- Keep each contributed key inside the extension namespace and separate read, write, run, and destructive capabilities where administrators need that control.
- Mark destructive or external side-effect permissions as
Dangerous. - Declare a Cove access convention on every extension endpoint, including endpoints intentionally limited only to authentication or explicitly anonymous.
- Test an owner, a role with only the new permission, an authenticated role without it, an API token with a narrower scope, and an anonymous request.
- Describe any requested runtime host or capability in the manifest while still honoring the per-request provider permissions.