opentdf / platform

OpenTDF Platform monorepo enabling the development and integration of _forever control_ of data into new and existing applications. The concept of forever control stems from an increasingly common concept known as zero trust.
BSD 3-Clause Clear License
17 stars 5 forks source link

ADR: Add typed Entities #1181

Closed ttschampel closed 3 weeks ago

ttschampel commented 1 month ago

Background

This is a follow on to ADR: Attribute Value Scoping to handle the same problem with a simpler approach through the use of typed Entities.

The Entity type provides context to policy enforcement on how to scope data policy enforcement logic and those derived from obligations. The initial Enum of Entity Types:

Examples

Given a Resource with a data policy: {[cls:topsecret, relto:usa, relto:gbr]}

Then the following policy decisions where the derived obligations are scoped to ENVIRONMENT type entities:

Overall Decision SUBJECT Entity SUBJECT Decision ENVIRONMENT Entity Derived Obligations ENVIRONMENT Decision
ALLOW {[cls:topsecret, relto:usa]} ALLOW
DENY {[cls:topsecret, relto:usa]} ALLOW [relto:usa] DENY
ALLOW {[cls:topsecret, relto:usa]} ALLOW {[relto:usa]} [relto:usa] ALLOW
ALLOW {[cls:topsecret, relto:usa]} ALLOW {[relto:usa]} [relto:usa] ALLOW
ALLOW {[cls:topsecret, relto:usa]} ALLOW {[relto:usa]},{[relto:gbr]} [relto:usa] ALLOW
DENY {[relto:usa]} DENY {[cls:topsecret, relto:usa]} [relto:usa] ALLOW
ALLOW {[cls:topsecret, relto:gbr]} ALLOW {[relto:usa]} [relto:usa] ALLOW
DENY {[cls:topsecret, relto:gbr]} ALLOW {[relto:gbr]} [relto:usa] DENY
DENY {[cls:topsecret, relto:usa]}, {[cls:secret, relto:usa]} (multiple chained subject entities) DENY
ALLOW {[cls:topsecret, relto:usa]} ALLOW {[cls:topsecret, relto:usa]}, {[cls:secret, relto:usa]} (multiple chained NPEs, one meets obligation) {[cls:topsecret, relto:usa]} ALLOW

Policy API Changes

The only Policy API change is the addition of Entity Type enum and type property to the authorization.Entity message

Entity Type Assignment

Logical Decision Requests against the Policy Authorization endpoints can take place as they do now with the default SUBJECT Entity type representing the intent of most use cases today. PEPs will now be able to add additional Environmental Entities to model more advanced request scenarios.

OIDC Token based entity chain resolution is still the responsibility of the Entity Resolution Service and needs to be updated to include the Entity Type while constructing the Entity Chain from the JWT.

See Token Extraction Examples

Subject Mappings

ENVIRONMENT entities can be entitled the same way as SUBJECT entities through the use of Subject Mappings. It is the responsibility of the policy administrator to not entitle SUBJECT entities with entitlements meant ONLY for ENVIRONMENT entities. e.g. a SUBJECT should not be entitled to drm:watermarking.

The addition of a Subject Mapping scope (Policy API addition) may be helpful to scope subject mappings to entities of a specific type.

Policy Enforcement

Today policy enforcement requires that all entities in the chain satisfy the data attribute policies for the set of resource data attributes.

With Entity Types in place; this enforcement logic should be scoped to only SUBJECT type entities

Derived Obligations

Derived attributes from obligations should be enforced in a similar way as data attributes with two major differences:

  1. scoped to ENVIRONMENT type entities.
  2. all the environmental entities do not need to satisfy the obligation attributes; instead any of the environmental entities satisfying the attribute policy is sufficient

To support this, a mechanism to retrieve derived obligations for a Decision Request's Resource Attributes as part of the GetDecisions endpoint is needed. See Obligations ADR. The set of derived obligations can then be cycled through the access.pdp against the environmental type entities.

If derived obligations exist, but no environmental entities exist - the decision should be DENY

Token Entity Chain Extraction Examples

Example JWTs for different OIDC Flows using Keycloak. Note, these are simple examples and more complex extraction or context may be needed to fetch session level information from an IDP to gather things such as IP Address, etc.

Client Credential Flow

In this example, the expected Entity Chain is below; basically creating an environmental entity on the fly for the standalone client:

[{
  "type": "ENVIRONMENT",
  "client_id": "opentdf-sdk",
  "other attrs": ".."
  }, 
  {
    "type": "SUBJECT",
    "client_id": "opentdf-sdk",
    "other attrs": "..."
  }]
{
  "exp": 1715790909,
  "iat": 1715790609,
  "jti": "2ca4399f-8fb5-4928-8dcd-5432f93aa572",
  "iss": "https://local-dsp.virtru.com:8443/auth/realms/opentdf",
  "aud": [
    "http://localhost:8080",
    "account"
  ],
  "sub": "e6eb4ae5-8c05-4274-8fa1-1f0f5fbccbdf",
  "typ": "Bearer",
  "azp": "opentdf-sdk",
  "acr": "1",
  "realm_access": {
    "roles": [
      "opentdf-readonly",
      "default-roles-opentdf",
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid profile email",
  "clientHost": "10.4.3.1",
  "email_verified": false,
  "preferred_username": "service-account-opentdf-sdk",
  "clientAddress": "10.4.3.1",
  "client_id": "opentdf-sdk"
}

Authorization Code Flow

In this example user mmiller has authenticated using the web client cop-web. The expected entity chain:

[
  {"type": "ENVIRONMENT", "client_id":  "cop-web", "other attrs": "..."},
  {"type": "SUBJECT","user_name":  "mmiller"},
]
{
  "exp": 1715794571,
  "iat": 1715794271,
  "jti": "f1ce6b62-8a61-43b8-b3a4-75d6cdd30e2e",
  "iss": "https://platform.virtrudemos.com/auth/realms/tdf",
  "aud": "account",
  "sub": "9bd5aa47-03a1-412a-8c35-311f16a80c0a",
  "typ": "Bearer",
  "azp": "cop-web",
  "session_state": "4407cc73-4ab3-4b95-b4f5-0d8363227984",
  "acr": "1",
  "allowed-origins": [
    "http://ec2-35-89-204-69.us-west-2.compute.amazonaws.com",
    "*",
    "http://127.0.0.1:8000",
    "http://localhost:8000",
    "http://localhost:8001"
  ],
  "realm_access": {
    "roles": [
      "default-roles-tdf",
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "email profile",
  "sid": "4407cc73-4ab3-4b95-b4f5-0d8363227984",
  "email_verified": false,       
  "preferred_username": "mmiller",
  "given_name": "",
  "family_name": ""
}

OBO: On-behalf Of

This is an example on-behalf of flow using Keycloak's Token Exchange by client opentdf for an authenticated opentdf-sdk client.

There is no clear way to understand, without additional context, that this is an OBO flow. Given this, custom logic to understand this an OBO flow will have to live in the ERS until Keycloak creates more standards based claims such as act

Creates an entity chain :

[{"type":"SUBJECT", "client_id": "opentdf-sdk"},{"type":"ENVIRONMENT","client_id":"opentdf"}]
{
  "exp": 1715790910,
  "iat": 1715790610,
  "jti": "61292674-08f9-4fd5-97cc-d873da84df9e",
  "iss": "https://local-dsp.virtru.com:8443/auth/realms/opentdf",
  "aud": [
    "http://localhost:8080",
    "account",
    "opentdf-sdk"
  ],
  "sub": "e6eb4ae5-8c05-4274-8fa1-1f0f5fbccbdf",
  "typ": "Bearer",
  "azp": "opentdf",
  "session_state": "e44c315c-699c-44ad-9645-6dd2b22837be",
  "acr": "1",
  "realm_access": {
    "roles": [
      "opentdf-readonly",
      "default-roles-opentdf",
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid profile email",
  "sid": "e44c315c-699c-44ad-9645-6dd2b22837be",
  "email_verified": false,
  "preferred_username": "service-account-opentdf-sdk"
}
jakedoublev commented 1 month ago

@ttschampel It seems like this clarifies quite a few things conceptually. It seems like, as a platform admin, I can define my own "trusted" and "untrusted" PEPs. I can define an Environmental Entity for a PEP I deem to be less secure for some reason and give it more obligations through Subject Mappings, and I can define another I deem to be trusted and give it no obligations. The accessibility of my platform's PEPs remains configurable through policy how I see fit, and resolutions of entities is the responsibility of my ERS code.

Is this understanding accurate in the examples above access = dataAttributes(subjectEntities) && obligations(environmentEntities)? Since entities are resolved by ERS, there's nothing also stopping me from defining the API such that access = environmentEntity(subject, dataAttributes, obligations) where the environment could theoretically override anything about a subject (including removing it completely from the entity resolution) depending solely on my ERS logic?

strantalis commented 1 month ago

@ttschampel I am probably missing something but why is the second row in the table a deny vs the first row?

ALLOW {[cls:topsecret, relto:usa]}  

vs

DENY {[cls:topsecret, relto:usa]}   [relto:usa]

Is it because the derived obligation or environment is missing the cls:topsecret entitlement?

ttschampel commented 1 month ago

Yes, b/c there is a derived obligation that is not met

jrschumacher commented 1 month ago

During arch meeting today this was approved.