How To: Create and Manage API Security Metadata using Declarative Security Policies
Introduction
The Ed-Fi API security metadata is managed by the configuration service and is logically organized into a taxonomy. The security metadata is authored as a JSON document that is loaded into the Configuration Service at startup or at runtime via the Configuration Service API.
Choosing an Approach
The Configuration Service provides several mechanisms for managing claims, each suited to different scenarios:
| Scenario | Approach |
|---|---|
| Create or replace a custom claim set with specific resource permissions | POST /v3/claimSets/import |
| Override authorization strategies for a specific claim set on a resource | POST /v3/claimSets/import |
| Change one resource claim's actions or overrides on an existing claim set | /v3/claimSets/{id}/resourceClaimActions endpoints |
| Change the default authorization strategy for a resource or domain (dev/test) | POST /management/upload-claims |
| Add extension resources to the claims taxonomy (dev/test) | POST /management/upload-claims |
| Restructure domain groupings or remove claims from the taxonomy (dev/test) | POST /management/upload-claims |
| Change the default authorization strategy for a resource or domain (production) | Deployment-time loading |
| Add extension resources to the claims taxonomy (production) | Deployment-time loading |
| Restructure domain groupings or remove claims from the taxonomy (production) | Deployment-time loading |
POST /v3/claimSets/import operates at the claim set level. It defines
which resources a claim set can access and with what actions. If no claim set
has the name, it creates one. If a claim set that isn't system-reserved already
has the name, the import replaces its grants. Built-in claim sets such as
SISVendor are system-reserved and can't be imported; copy one first. Only the
imported claim set is affected, and the unrestricted claims loading flag isn't
required. Resource claims that aren't in the taxonomy are skipped and listed in
the response's warnings, so check them after every import. See
How To: Configure Claim Sets for examples.
The /v3/claimSets/{id}/resourceClaimActions endpoints change one resource
claim on an existing claim set that isn't system-reserved: grant or remove
actions, or override or reset its authorization strategies. You don't need to
re-import the whole claim set. See
Managing a Claim Set's Resource Claims.
Claim set import and the resourceClaimActions endpoints accept only built-in
authorization strategies. To use a
custom view-based authorization
strategy, change the claims document instead, with upload-claims or
deployment-time loading.
POST /management/upload-claims replaces the entire claims hierarchy
atomically. Use it when the change involves the taxonomy itself: default
authorization strategies, adding or removing resource claims, or domain-level
restructuring. This requires
CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true and
should only be used in non-production environments.
Deployment-time loading is the production-safe way to make taxonomy-level
changes. The Configuration Service loads claims from an embedded resource, a
directory of fragment files, or a complete claims file — controlled by the
CONFIGURATION_SERVICE_CLAIMS_SOURCE environment variable. No dangerous flag is
required. Claims are loaded only when the Configuration Service database is
first initialized; see When Claims Are Loaded before
changing the claims of an existing deployment. See
Loading Modes for details.
Both API approaches write only to the
Configuration Service database;
the configured claims source (such as the embedded Claims.json resource) is
not modified. Changes survive redeployment as long as the database persists, but
will be lost if the database is recreated or cleared, since startup seeding
restores only what the configured claims source contains. For changes that must
survive a fresh database, update the claims source directly and use
deployment-time loading.
Security Metadata Overview
In this section, the building blocks for defining a security policy are introduced, along with their JSON representations.
The Security Metadata Format
The claims hierarchy is a JSON array of domain claim objects. Each domain claim can contain child resource claims, default authorization metadata, and per-claim-set permission assignments:
[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"defaultAuthorization": { ... },
"claimSets": [ ... ],
"claims": [ ... ]
}
]
The examples throughout this section all use this array format. A complete
claims document wraps the array as its claimsHierarchy property, next to a
claimSets catalog; see
Building a Security Policy. How the document is
delivered to the Configuration Service depends on the loading mode; see
Loading Your Security Policy.
Managing the Claims Taxonomy
Individual API resources are represented as "leaf nodes" (aka resource claims) that are grouped under higher-level domain claims, as depicted in the table below.
| people | http://ed-fi.org/identity/claims/domains/people |
| ⌊_ contact | http://ed-fi.org/identity/claims/ed-fi/contact |
| ⌊_ staff | http://ed-fi.org/identity/claims/ed-fi/staff |
| ⌊_ student | http://ed-fi.org/identity/claims/ed-fi/student |
The JSON representation of this structure uses the claims array to nest
resource claims under their parent domain claim:
[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"claims": [
{ "name": "http://ed-fi.org/identity/claims/ed-fi/contact" },
{ "name": "http://ed-fi.org/identity/claims/ed-fi/staff" },
{ "name": "http://ed-fi.org/identity/claims/ed-fi/student" }
]
}
]
In general, when adding new claims to the taxonomy, it is best to define domain claims that organize the resource claims according to their business-oriented domains rather than according to the similarities in the authorization approach.
Claim URIs in Ed-Fi API v8 use the pattern
http://ed-fi.org/identity/claims/..., without the /ods/ segment used in
previous ODS/API versions. Update any existing security policies accordingly.
Default Authorization Metadata
Each claim may have default authorization metadata associated with it, which tells the API which authorization strategy (or strategies) to use when authorizing API requests that are performing specific actions (i.e. CRUD operations). This default authorization metadata applies to all descendants in the taxonomy unless overridden for a specific claim set (aka "role") and domain/resource claim.
The out-of-the-box default authorization metadata for the people domain claim
is represented in JSON as follows:
[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"defaultAuthorization": {
"actions": [
{
"name": "Create",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"name": "Read",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Update",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Delete",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"name": "ReadChanges",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeopleIncludingDeletes" }
]
}
]
}
}
]
This tells the API that unless otherwise specified, it should use the
NoFurtherAuthorizationRequired strategy for Create and Delete operations, the
RelationshipsWithEdOrgsAndPeople strategy for Read and Update operations, and
RelationshipsWithEdOrgsAndPeopleIncludingDeletes for the Change Queries
/deletes and /keyChanges endpoints (the ReadChanges action).
Once established and in use by descendant resource claims, great care should be taken when modifying the default authorization strategies. Changes at this level will affect all descendant resource claims that haven't specified explicit authorization strategies. The intent of default authorization metadata is to reduce unnecessary redundancy, so you must be careful not to introduce unintended side-effects when changing defaults already in use.
Claim Set Permissions
Claim sets are the "roles" of the API, to which API clients are assigned. You can assign permissions to claim sets on either domain or resource claims, but the goal is to organize the taxonomy so that most assignments are made to domain claims because those permissions will then apply to all the descendant resource claims as well.
The out-of-the-box SISVendor claim set is granted full CRUD permissions for
all people (including the descendant resource claims of contact, staff, and
student). This is represented in JSON as follows:
[
{
"name": "http://ed-fi.org/identity/claims/domains/people",
"claimSets": [
{
"name": "SISVendor",
"actions": [
{ "name": "Create" },
{ "name": "Read" },
{ "name": "Update" },
{ "name": "Delete" }
]
}
]
}
]
Claim Set Authorization Strategy Overrides
In some cases, it may be necessary to override the authorization strategies used to authorize specific requests for API clients assigned to a particular claim set.
For example, consider an SEA-level implementation where multiple vendors may
create data that is visible to other vendors, but only the creator should be
able to update or delete it. In this case, the OwnershipBased authorization
strategy can be used as an override on the Update and Delete actions.
The out-of-the-box default authorization metadata for the
relationshipBasedData domain applies the general-purpose relationship-based
strategy to the CRUD actions, and its IncludingDeletes variant to
ReadChanges. The following JSON adds the SISVendor claim set with
ownership-based overrides on Update and Delete:
[
{
"name": "http://ed-fi.org/identity/claims/domains/relationshipBasedData",
"defaultAuthorization": {
"actions": [
{
"name": "Create",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Read",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Update",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "Delete",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeople" }
]
},
{
"name": "ReadChanges",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsAndPeopleIncludingDeletes" }
]
}
]
},
"claimSets": [
{
"name": "SISVendor",
"actions": [
{ "name": "Create" },
{ "name": "Read" },
{
"name": "Update",
"authorizationStrategyOverrides": [
{ "name": "RelationshipsWithEdOrgsAndPeople" },
{ "name": "OwnershipBased" }
]
},
{
"name": "Delete",
"authorizationStrategyOverrides": [
{ "name": "RelationshipsWithEdOrgsAndPeople" },
{ "name": "OwnershipBased" }
]
}
]
}
]
}
]
Notice that the Create and Read actions rely on the defaultAuthorization
defined for the domain claim rather than repeating the strategy locally, which
would be redundant.
The OwnershipBased override works only after the vendors' API clients are
assigned ownership tokens. It denies access to records that have no owner, such
as records created before the creating client had a creator token. See
Ownership-Based Authorization
for setup.
Building a Security Policy
Each of the examples in the previous sections is a partial view. A complete policy is a claims document with two top-level properties:
{
"claimSets": [
{ "claimSetName": "SISVendor", "isSystemReserved": true }
],
"claimsHierarchy": [ ... ]
}
claimSetsregisters every claim set name and whether it is system-reserved (the claim set endpoints can't change or delete it). List every claim set that the hierarchy grants access to. A grant to a claim set that isn't listed here has no effect.claimsHierarchyis the array described in The Security Metadata Format. It contains every domain and resource claim you want to be active, with alldefaultAuthorization,claimSets, andclaimsentries in place.
Any claim, claim set assignment, or authorization strategy omitted from the document will be absent from the active configuration. The Filesystem loading mode and the runtime upload endpoint both expect this complete document; the embedded resource uses the same format.
There are two practical starting points for a complete policy:
- The
Claims.jsonshipped with the Configuration Service for your data standard version, inClaims/Standards/ds{version}/. Use the file from the release you deploy. - The active document downloaded from
GET /management/current-claims(see Runtime Upload via Configuration Service).
If you are migrating from a previous Ed-Fi ODS/API deployment, the
Ed-Fi API Security Metadata Tools
converts ODS/API XML security metadata into a complete document with top-level
claimSets and claimsHierarchy properties. Upload the generated file
unchanged to POST /management/upload-claims, using the administrative token
and unrestricted-claims-loading setting described in
Runtime Upload via Configuration Service.
The converter emits editable imported sets (isSystemReserved: false); an
already-present reserved set with the same name retains its stored reserved
status during upload.
To add extension resources without maintaining a complete document, use claims fragments instead; see Introducing Extension Resources into the Taxonomy.
To review the result of your fragments before you deploy them, build the
complete document offline with the merge command of the
Ed-Fi API Security Metadata Tools.
It applies the fragments in a directory to the bundled base claims for a data
standard version. Use the same version as the Configuration Service's
CONFIGURATION_SERVICE_DATA_STANDARD_VERSION. The output folder must already
exist:
New-Item -ItemType Directory -Force ./policy-output | Out-Null
security-metadata-tools merge --fragments ./fragments `
--data-standard-version 5.2 --output ./policy-output/Claims.json
Use the version of the tool that matches the Configuration Service you deploy. The result is a complete document that you can deploy with Filesystem loading or upload. Don't also deploy the fragments it already includes.
Adding New Claims
When the hierarchy is loaded, the Configuration Service automatically derives a resource claim record for every claim name in the array and registers it in its internal catalog (insert-only, idempotent). No separate registration step is needed.
Adding Authorization Strategies and Actions
Actions can't be defined through the policy document; use the actions built into
the service. An authorization strategy must be either a built-in strategy or the
name of a custom authorization view. The Ed-Fi API treats a name that isn't
built in as a view in the auth schema. See
How To: Use Custom View-Based Authorization.
Introducing Extension Resources into the Taxonomy
To add a new resource under an existing domain claim, include it nested under
its intended parent in the claims array:
[
{
"name": "http://ed-fi.org/identity/claims/domains/someDomain",
"claims": [
{
"name": "http://ed-fi.org/identity/claims/myext/myNewResource"
}
]
}
]
The new claim inherits the domain's defaultAuthorization unless you specify
its own. Include any claimSets entries needed to grant access to it.
Alternatively, write a claims fragment and let the Configuration Service merge it into the base claims at startup using the Hybrid loading mode. You don't need to produce a merged document yourself. For complete worked examples, see Step 5d of How To: Extend the Ed-Fi API - Student Transcript Example and How To: Extend the Ed-Fi API - Alternative Education Program Example.
Omitting Claims and Claim Sets
Because loading a policy fully replaces the active hierarchy, any claim, claim set assignment, or authorization strategy omitted from the final security metadata file will be removed from the active configuration. Ensure the array represents the complete intended state before loading it.
The entire claims hierarchy is replaced atomically. Partial updates are not supported. Always start from the current hierarchy and modify it rather than authoring a document from scratch.
Claims Fragments
A claims fragment is a small JSON file that adds to the claims the Configuration Service already has, rather than replacing them. Use fragments to add extension resources, add a new domain, or give a claim set access to existing claims, without maintaining a complete claims document. The Configuration Service merges fragments into its base claims when it loads claims in the Hybrid or Filesystem loading mode.
Before deployment, use the Security Metadata Tools'
merge command
to check your fragments against the bundled base for the same data standard. It
fails on incomplete composition and policy that the service silently ignores,
including policy on matched parent entries and retired *-claimset.json files.
The Configuration Service's loading behavior is unchanged; these are additional
offline checks.
Fragment Structure
A fragment has a top-level name and a resourceClaims array. Each entry in
resourceClaims is one of two kinds:
- A parent entry has
"isParent": true. It adds new claims to the hierarchy: resource claims under an existing domain, or a new domain. - A grant entry has no
isParent(or"isParent": false). It gives the fragment's claim set access to a claim that already exists, for the actions listed in itsauthorizationStrategyOverridesForCRUD.
On a grant entry, actions and its enabled values are informational. The
requested grants come only from authorizationStrategyOverridesForCRUD:
enabling an action without an override doesn't grant it, and an override
requests its action even if actions is absent or marks it disabled. These
flags don't deny or revoke existing or inherited access. This rule applies to
grant entries, not the claimSets[].actions on a new parent entry.
The top-level name is the claim set that the fragment's grant entries give
access to. Always set it on a fragment with grant entries. If it's missing, the
service uses the file name as the claim set name (for example,
003-sisvendor-claims) and creates a new system-reserved claim set with that
name, which the claim set endpoints can't change or delete. In a fragment with
only parent entries, name is just a label.
Fragments use different property names from the claims hierarchy described in The Security Metadata Format. Use the fragment names in a fragment file:
| Purpose | Claims hierarchy | Fragment |
|---|---|---|
| Child claims | claims | children |
| Default authorization | defaultAuthorization.actions[].name | _defaultAuthorizationStrategiesForCrud[].actionName |
| Claim set permissions | claimSets | claimSets on a parent entry, or a grant entry |
| Claim set strategy overrides | authorizationStrategyOverrides in claimSets | authorizationStrategyOverrides in a parent entry's claimSets, or authorizationStrategies in a grant entry's authorizationStrategyOverridesForCRUD |
File Names and Order
The service applies every file whose name ends in -claims.json anywhere under
the configured claims directory, including subdirectories, one at a time, sorted
by file path. Start each file name with a number such as 001- or 002- to
control the order, and keep all your fragments in one folder: a number prefix
only orders files within the same folder. Order matters when one fragment refers
to a claim that an earlier fragment adds.
The suffix must be lowercase. The Configuration Service runs on Linux, where the
match is case-sensitive, so the Configuration Service skips a file ending in
-Claims.json without a warning.
Before v8.1, fragment file names ended in -claimset.json. The service no
longer reads those files, so rename each one to end in -claims.json. The
service logs a warning for each -claimset.json file it finds, but only when it
reads fragments (see When Claims Are Loaded), so an
in-place upgrade of an existing database logs no warning at startup.
The Docker Compose files changed too. In v8.0 they read the fragment folder from
DMS_CONFIG_CLAIMS_MOUNT_SOURCE, which is now
CONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCE, and their default folder moved from
Deploy/AdditionalClaimsets to Deploy/AdditionalClaims. If you set
DMS_CONFIG_CLAIMS_MOUNT_SOURCE, rename it to
CONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCE. Otherwise Compose mounts the
default folder instead of yours, and the service logs no warning.
Renaming needs care for a fragment with grant entries but no top-level name.
This applies only to fragments the Configuration Service reads directly, because
prepare-dms-claims.ps1 rejects such fragments. The service attaches that
fragment's grants to a claim set named after the file. In v8.0, applications
could use those grants only if a claim set with that name was created separately
through the claim set endpoints. Renaming the file moves the grants to a new
name, so those applications lose their access. To keep their access, set the
fragment's top-level name to the old file name without .json, for example
"003-sisvendor-claimset", before you rename the file. From v8.1, that name
becomes a system-reserved claim set (see
Choosing Claim Set Names).
Referring to Existing Claims
To point at a claim that already exists, use its short form: the last two
segments of its URI, such as domains/systemDescriptors or ed-fi/student.
Case doesn't matter. A full URI never matches an existing claim. Use full URIs
only for claims you are adding, such as a new domain or the children you add
under a domain.
Adding Resource Claims to an Existing Domain
To add resource claims under an existing domain, write a parent entry that names
the domain by its short form and lists the new resource claims as children.
The following fragment adds an extension descriptor under the out-of-the-box
systemDescriptors domain and an extension association under the
relationshipBasedData domain:
{
"name": "MyExtensionClaims",
"resourceClaims": [
{
"isParent": true,
"name": "domains/systemDescriptors",
"children": [
{ "name": "http://ed-fi.org/identity/claims/my-extension/MyDescriptor" }
]
},
{
"isParent": true,
"name": "domains/relationshipBasedData",
"children": [
{
"name": "http://ed-fi.org/identity/claims/my-extension/MyAssociation"
}
]
}
]
}
This tells the Configuration Service to add MyDescriptor and MyAssociation
as resource claims under those domains. They inherit each domain's default
authorization and claim set permissions, so every claim set that can already
access the domain gets the same access to the new resource claims.
Any claimSets or _defaultAuthorizationStrategiesForCrud on a parent entry
that names an existing domain are ignored. To give different access to the new
resource claims, put them in a new domain or use grant entries.
Adding a New Domain
To add a domain, write a parent entry whose name is the full URI of the new domain, with its default authorization, its resource claims, and the claim sets that can access it:
{
"name": "MyExtensionDomainClaims",
"resourceClaims": [
{
"isParent": true,
"name": "http://ed-fi.org/identity/claims/domains/myExtension",
"_defaultAuthorizationStrategiesForCrud": [
{
"actionName": "Create",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"actionName": "Read",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"actionName": "Update",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
},
{
"actionName": "Delete",
"authorizationStrategies": [
{ "name": "NoFurtherAuthorizationRequired" }
]
}
],
"children": [
{
"name": "http://ed-fi.org/identity/claims/my-extension/MyNewResource"
}
],
"claimSets": [
{
"name": "EdFiSandbox",
"actions": [
{ "name": "Create" },
{ "name": "Read" },
{ "name": "Update" },
{ "name": "Delete" }
]
}
]
}
]
}
This tells the Configuration Service to add a top-level myExtension domain
that contains MyNewResource, to use NoFurtherAuthorizationRequired for every
action unless a claim set overrides it, and to give EdFiSandbox full CRUD
access. No other claim set can access MyNewResource until it is granted.
Granting a Claim Set Access to an Existing Claim
To give a claim set access to a claim that already exists, write a fragment
named after that claim set, with a grant entry for each claim. List each granted
action in authorizationStrategyOverridesForCRUD, optionally with authorization
strategies that override the defaults for that action. The following fragment
grants the out-of-the-box SISVendor claim set access to the resource claim
added by the previous example:
{
"name": "SISVendor",
"resourceClaims": [
{
"name": "my-extension/MyNewResource",
"authorizationStrategyOverridesForCRUD": [
{ "actionName": "Read" },
{
"actionName": "Update",
"authorizationStrategies": [{ "name": "NamespaceBased" }]
}
]
}
]
}
This tells the Configuration Service that SISVendor can read MyNewResource
using the domain's default authorization strategy, and can update it using
NamespaceBased instead. A grant entry can refer to a claim added by an earlier
entry or an earlier fragment, which is why this file is numbered after the one
that adds the domain.
Choosing Claim Set Names
Use only claim sets that the base claims document already declares, both in a
parent entry's claimSets and as the name of a fragment with grant entries.
An undeclared claim set in a parent entry's claimSets is ignored. An
undeclared name on a fragment with grant entries creates a new
system-reserved claim set, which the claim set endpoints can't change or
delete and which stays even if you remove the fragment. prepare-dms-claims.ps1
rejects both.
Rules and Common Mistakes
- Refer to an existing claim by its short form. If you write an existing domain's full URI in a parent entry, the service doesn't find the domain and adds a second, duplicate domain.
- In a parent entry, name a domain, not a resource claim, and list only direct
children. Children under a resource claim, or nested inside another child, are ignored. claimSetsbelongs only on parent entries. On a grant entry the service ignores it, andprepare-dms-claims.ps1rejects the file.- On an existing matched parent,
claimSetsand_defaultAuthorizationStrategiesForCrudare ignored.mergerejects these fields when they contain policy. Put grants in grant entries instead. - Use only a
nameon each child in a parent entry'schildrenlist. The service copies that name, not policy nested on the child;mergerejects unapplied child policy. - Put
authorizationStrategyOverridesForCRUDon grant entries, not parent entries. The service ignores it on parents;mergerejects it when nonempty.
Fragment mistakes don't always produce an error:
-
A grant entry whose claim isn't found is skipped.
-
If a fragment isn't valid JSON, the service logs only a warning and loads the base claims without any fragments, including the valid ones:
- On a first start, only the base claims are stored. Fixing the file and restarting doesn't help, because claims load only into an empty database.
- On a reload, the request succeeds but removes every claim that the fragments added.
To recover, fix the file. In a non-production environment, then call
POST /management/reload-claims(requiresCONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true). In production, start with a new Configuration Service database. -
Other mistakes, including a fragment file the service doesn't have permission to read, stop the claims from loading. On a first start, the service doesn't start. On a reload, the request fails with a
500error and the stored claims don't change.
After deploying fragments, check the Configuration Service log and verify the result as described in Verifying the Active Policy.
Loading Your Security Policy
Loading Modes
The Configuration Service controls how claims are loaded at startup via the
CONFIGURATION_SERVICE_CLAIMS_SOURCE environment variable. Three source modes
are available: Embedded, Hybrid, and Filesystem. Claims can also be
replaced at runtime via the
management API, independently of
the configured source mode.
Whichever mode you choose, the service seeds its database from that source only when the database holds no claims yet. See When Claims Are Loaded.
The variable names on this page are the ones used by the Docker Compose files that ship with the Ed-Fi API. Compose passes each one to the Configuration Service as a setting. If you run the Configuration Service without those Compose files, set the setting directly instead:
| Purpose | Docker Compose variable | Configuration Service setting |
|---|---|---|
| Claims source | CONFIGURATION_SERVICE_CLAIMS_SOURCE | ClaimsOptions__ClaimsSource |
| Claims directory | CONFIGURATION_SERVICE_CLAIMS_DIRECTORY | ClaimsOptions__ClaimsDirectory |
| Data standard version | CONFIGURATION_SERVICE_DATA_STANDARD_VERSION | ClaimsOptions__DataStandardVersion |
| Runtime claims changes | CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING | ClaimsOptions__DangerouslyEnableUnrestrictedClaimsLoading |
| Database setup at startup | CONFIGURATION_SERVICE_DEPLOY_DATABASE | AppSettings__DeployDatabaseOnStartup |
| Folder on your machine to mount as the claims directory | CONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCE | None. Compose mounts it at /app/additional-claims; without Compose, mount the folder yourself |
Embedded (Production Default)
CONFIGURATION_SERVICE_CLAIMS_SOURCE=Embedded
The
Claims/Standards/ds{version}/Claims.json
file in the EdFi.DmsConfigurationService.Claims project is compiled into the
Configuration Service assembly as an embedded resource. On startup, if the
database tables are empty, the service seeds the ClaimSet, ResourceClaim,
and ClaimsHierarchy tables from that embedded file. The embedded file changes
only when the service is rebuilt, and deploying a rebuilt service doesn't update
the claims of an existing database (see
When Claims Are Loaded). The data standard version to
load is selected by CONFIGURATION_SERVICE_DATA_STANDARD_VERSION (default:
5.2).
This is the most secure option for production: no external files, no runtime modifications.
Embedded is what the service uses when no claims source is set. The sample
environment files that ship with the Docker Compose setup (.env.example and
.env.template) set Hybrid instead, so a local environment started from them
uses Hybrid mode.
Hybrid
CONFIGURATION_SERVICE_CLAIMS_SOURCE=Hybrid
CONFIGURATION_SERVICE_CLAIMS_DIRECTORY=/app/additional-claims
Uses the same embedded base as Embedded mode, and applies the claims fragments found in the configured directory on top of it. This is the recommended way to add extension resources and extension claim sets without maintaining a complete claims document.
The fragment directory must be readable by the Configuration Service. The Docker
Compose files mount a folder from your machine into the container at
/app/additional-claims. Set CONFIGURATION_SERVICE_CLAIMS_MOUNT_SOURCE to the
folder that holds your fragments. If you deploy without those Compose files,
mount your folder yourself and set the claims directory to the path inside the
container. For a complete walkthrough, see Step 5d of the extension how-to
guides linked in
Introducing Extension Resources into the Taxonomy.
Filesystem
CONFIGURATION_SERVICE_CLAIMS_SOURCE=Filesystem
CONFIGURATION_SERVICE_CLAIMS_DIRECTORY=/app/additional-claims
The embedded Claims.json is not used. Instead, the service reads a complete
claims document (see Building a Security Policy)
from a file named Claims.json in the configured directory. The service also
looks in subdirectories, and it stops with an error if it finds no Claims.json
or more than one. Any claims fragments in the directory are
applied on top of it. This is for deployments that manage their own complete
claims document outside the service binary.
When Claims Are Loaded
At startup, the Configuration Service seeds its database from the configured
claims source only when the database holds no claims yet, which is normally
the first start against a new database. On every later start it logs
Claims data already exists, skipping initial load and keeps the claims already
stored in the database. Seeding also requires database setup at startup to be on
(CONFIGURATION_SERVICE_DEPLOY_DATABASE, on by default).
This means that for an existing deployment, restarting the service after any of these changes does not change the active claims:
- adding, editing, or removing claims fragments;
- editing the
Claims.jsonused by Filesystem mode; - changing
CONFIGURATION_SERVICE_CLAIMS_SOURCEorCONFIGURATION_SERVICE_DATA_STANDARD_VERSION.
For a new deployment, put the claims source in place (embedded, fragments,
or Claims.json) before the first start, and the service loads it
automatically.
For an existing deployment, the stored claims can only be replaced through
the management API: POST /management/reload-claims reloads from the configured
source, including fragments, and POST /management/upload-claims replaces the
claims with a document you supply. Both require
CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true; see
Runtime Upload via Configuration Service.
Changes limited to individual claim sets can be made with the claim set
endpoints instead; see Choosing an Approach.
A reload or upload deletes every claim set that isn't system-reserved and isn't
in the claims source, including claim sets created through the claim set
endpoints. Before reloading or uploading, export those claim sets
(GET /v3/claimSets/{id}/export) and re-import them afterward
(POST /v3/claimSets/import).
In this release, production deployments have no supported way to change the full claims policy of an existing database. To apply a new full claims policy, provision a new Configuration Service database. A new database doesn't carry over vendors, applications, client credentials, or data store registrations, so plan to recreate them.
Runtime Upload via Configuration Service
Runtime upload is a separate capability that can be combined with any of the three source modes above. It is enabled by:
CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true
When enabled, the following management API endpoints become available:
GET /management/current-claims: retrieve the active claims documentPOST /management/upload-claims: atomically replace the active claimsPOST /management/reload-claims: reload from the configured source
CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING=true
enables runtime modification of security policies and should never be
enabled in production deployments.
The management API works with a container document that wraps the hierarchy array together with a claim set catalog:
{
"claimSets": [
{ "claimSetName": "SISVendor", "isSystemReserved": true }
],
"claimsHierarchy": [ ... ]
}
The claimSets array registers every claim set name and whether it is
system-reserved (the claim set endpoints can't change or delete it). The
claimsHierarchy array uses the same JSON array format described in
The Security Metadata Format. Any new claim set
referenced in the hierarchy must also appear in claimSets.
GET /management/current-claims returns this same two-section document, and
POST /management/upload-claims accepts it unchanged, so a document retrieved
from the service can be edited and posted back exactly as it was downloaded.
These management endpoints require a Configuration Service bearer token. Write
operations (POST /management/upload-claims, POST /management/reload-claims)
require the edfi_admin_api/full_access scope; the read operation
(GET /management/current-claims) accepts edfi_admin_api/readonly_access or
edfi_admin_api/full_access. Obtain a token:
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("<config-admin-key>:<config-admin-secret>"))
$token = Invoke-RestMethod -Method Post -Uri "http://localhost:8081/connect/token" `
-Headers @{ "Authorization" = "Basic $auth" } `
-ContentType "application/x-www-form-urlencoded" `
-Body @{
"grant_type" = "client_credentials"
"scope" = "edfi_admin_api/full_access"
}
Retrieve the current claims to use as a starting point:
Invoke-RestMethod -Uri "http://localhost:8081/management/current-claims" `
-Headers @{ Authorization = "Bearer $($token.access_token)" } |
ConvertTo-Json -Depth 100 |
Set-Content claims.json
Modify the claimsHierarchy array in the downloaded file. Add any new claim
set names to the claimSets catalog as well.
Upload the modified document to replace the active claims hierarchy. The endpoint accepts the two-section document exactly as downloaded, so post the file contents directly as the request body:
Invoke-RestMethod -Method Post `
-Uri "http://localhost:8081/management/upload-claims" `
-Headers @{ Authorization = "Bearer $($token.access_token)" } `
-ContentType "application/json" `
-Body (Get-Content claims.json -Raw)
The uploaded document must include both the claimSets catalog and the
claimsHierarchy array as top-level properties, as
{"claimSets": [...], "claimsHierarchy": [...]}.
An upload or reload replaces the claims hierarchy, including the permissions of system-reserved claim sets. It is not a complete replacement of everything, though:
- Claim sets that aren't system-reserved are replaced by the ones in the document. System-reserved claim sets are never removed, even if the document leaves them out. Their permissions still come only from the uploaded hierarchy, so a system-reserved claim set the document doesn't grant anything to has no access afterward.
- The claim sets and the hierarchy are replaced together, in a single step.
Verifying the Active Policy
To inspect the policy stored by the Configuration Service, call
GET /v3/authorizationMetadata. It is the same data the Ed-Fi API loads to
authorize requests, it reflects the claims stored in the Configuration Service
database, and it is available in every loading mode without
CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING. It
accepts a token with the edfi_admin_api/full_access,
edfi_admin_api/readonly_access, or
edfi_admin_api/authMetadata_readonly_access scope.
Invoke-RestMethod -Uri "http://localhost:8081/v3/authorizationMetadata?claimSetName=SISVendor" `
-Headers @{ Authorization = "Bearer $($token.access_token)" } |
ConvertTo-Json -Depth 10
Omit claimSetName to return every claim set. Check the spelling of the name:
an unknown name returns 200 with an empty array ([]) rather than an error.
An empty array means no claim set has that name. A claim set that exists but has
no access is still listed, with an empty claims list.
The response doesn't repeat your policy document. Instead, for each claim set it lists every resource claim the claim set can access, with the effective actions and authorization strategies after inheritance and defaults are resolved. An abridged example:
[
{
"claimSetName": "SISVendor",
"claims": [
{
"name": "http://ed-fi.org/identity/claims/ed-fi/academicWeek",
"authorizationId": 1
}
],
"authorizations": [
{
"id": 1,
"actions": [
{
"name": "Read",
"authorizationStrategies": [
{ "name": "RelationshipsWithEdOrgsOnly" }
]
}
]
}
]
}
]
Use it to confirm that new resource claims appear for the claim sets you expected, with the actions and strategies you intended. Domain claims don't appear in the response, only the resource claims beneath them.
To compare a complete claims document with that stored metadata, use the
verify command of the
Ed-Fi API Security Metadata Tools.
Use the tool from the matching Configuration Service release. Pass the complete
claims document, not the inspection output from resolve. It reads
GET /v3/authorizationMetadata and reports every claim set, claim, action, or
strategy that differs from the document. Pass the name of an environment
variable that holds a Configuration Service token, not the token itself:
$env:CMS_ACCESS_TOKEN = $token.access_token
security-metadata-tools verify --input ./policy-output/Claims.json `
--cms-url http://localhost:8081 --access-token-env CMS_ACCESS_TOKEN
Remove-Item Env:CMS_ACCESS_TOKEN
For a multi-tenant deployment, add --tenant <tenant-name>. verify compares
every claim set, so system-reserved claim sets kept by an upload but missing
from the document are reported as unexpected. It compares effective permissions,
including the order of strategies within each action. Exit code 0 means all
metadata matches, 2 means valid metadata differs, and 1 means the command
failed, for example because authentication failed or a response was invalid.
A match confirms the Configuration Service metadata at the time of the request.
It doesn't prove that the Ed-Fi API's authorization cache is current or that
live API requests enforce the intended policy. Verify those requests separately;
verify doesn't change policy or refresh caches.
GET /management/current-claims isn't a good substitute. It returns 404 unless
CONFIGURATION_SERVICE_DANGEROUSLY_ENABLE_UNRESTRICTED_CLAIMS_LOADING is set.
It also returns the claims document the service most recently loaded or
received, which isn't guaranteed to match what is stored in the database. For
example, changes made through the claim set endpoints aren't reflected in it.
Use GET /v3/authorizationMetadata to inspect stored effective permissions.