-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specification for Graph Permissions Model #190
base: master
Are you sure you want to change the base?
Changes from 4 commits
2bc1a19
fd5f461
95a3711
fb5ed70
e382eee
f6a3f96
d684ba9
e16b7cd
052d84d
0b7c116
ae03c13
d710812
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,340 @@ | ||||||
# Permissions for HTTP APIs | ||||||
|
||||||
## Abstract | ||||||
This document defines a "permission" object and "permissions" document that describes identifiers that can be used by security claims to grant access to HTTP resources. | ||||||
|
||||||
## Introduction | ||||||
|
||||||
## Notational Conventions | ||||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. | ||||||
|
||||||
## <a name="permissionsObject"></a> Permissions JSON Object | ||||||
The canonical model for a permissions document is a JSON [JSON] object. When serialized in a JSON document, that format is identified with the "application/permissions+json" media type. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we have a url for [JSON]? |
||||||
|
||||||
```json | ||||||
{ | ||||||
"permissions": { | ||||||
"PrintSettings.Read.All": { | ||||||
"schemes": { | ||||||
"DelegatedWork": { | ||||||
"type": "DelegatedWork", | ||||||
"description": "Allow signed in user to read print settings" | ||||||
} | ||||||
}, | ||||||
"pathSets": [{ | ||||||
"schemes": ["DelegatedWork"], | ||||||
"methods": ["GET"], | ||||||
"paths": { | ||||||
"/print/settings": {} | ||||||
} | ||||||
}] | ||||||
} | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
In this example, the claim "PrintSettings.Read.All" is required when using the "DelegatedWork" security scheme to access the resource "/print/settings" using the "GET" method. | ||||||
|
||||||
### permissions | ||||||
The "permissions" member is a JSON object whose members permission objects. The key of each member is the claim identifier used for the [Permission Object](#permissionObject) | ||||||
|
||||||
|
||||||
## <a name="permissionObject"></a>Permission Object | ||||||
|
||||||
### note | ||||||
The "note" member is a freeform string that provides additional details at about the permission that cannot be determined from the other members of the permission object. | ||||||
|
||||||
### implicit | ||||||
The "implicit" member is a boolean value that indicates that the current permission object is implied. The default value is "false". This member us usually set to "true" in combination with a "alsoRequires" expression. | ||||||
|
||||||
> Note: This member enables support for the Microsoft Graph create subscription endpoint and the Search query endpoint. | ||||||
|
||||||
### schemes | ||||||
The "schemes" member is a REQUIRED JSON object whose members are [Scheme objects](#schemeObject) supported by the permission. The key of each member is an identifier of the scheme and the value is a [Scheme object](#schemeObject) that contains descriptions of the permission within the scheme. | ||||||
|
||||||
### pathSets | ||||||
The "pathSets" member is a REQUIRED JSON Array. Each element of the array is a [pathSet object](#pathSetObject). | ||||||
|
||||||
### isHidden | ||||||
The "isHidden" member is a boolean value that indicates if a permission should be publicly usable in the API. | ||||||
|
||||||
### requiredEnvironments | ||||||
The "requiredEnvironments" member is an array of strings that identifies the deployment environments in which the permission SHOULD be supported. When this member is not present, support for all environments is implied. | ||||||
|
||||||
### privilegeLevel | ||||||
The "privilegeLevel" member value provides a hint as to the risks of consenting this permissions. Valid values include: low, medium and high. | ||||||
|
||||||
### appIdForConditionalAccessChecks | ||||||
TBD | ||||||
|
||||||
### ownerEmail | ||||||
The "ownerEmail" member is a REQUIRED string that provides a contact mechanism for communicating with the owner of the permission. It is important that owners of permissions are aware when new paths are added to an existing permission. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Require this to be a DL/Mail enabled security group |
||||||
|
||||||
## <a name="pathSetObject"></a>PathSet Object | ||||||
A pathSet object identifies a set of paths that are accessible have a common set of security characteristics, such as HTTP methods and schemes. Ideally, a permission object contains a single pathSet object. This indicates that all paths protected by the permission support the same characteristics. In practice there are cases where support is not uniform. Distinct pathSet objects can be created to separate the paths with varying characteristics. | ||||||
darrelmiller marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
> Note: The design chosen was intentional to encourage permission creators to ensure support for methods and schemes is as consistent as possible. This produces a better developer experience for API consumers. | ||||||
|
||||||
```json | ||||||
"pathSets": [{ | ||||||
"schemeKeys": ["DelegatedWork"], | ||||||
"methods": ["GET"], | ||||||
"paths": { | ||||||
"/print/settings": {} | ||||||
} | ||||||
}, | ||||||
{ | ||||||
"schemeKeys": ["Application"], | ||||||
"methods": ["GET,POST"], | ||||||
"paths": { | ||||||
"/print/settings": {} | ||||||
} | ||||||
} | ||||||
] | ||||||
``` | ||||||
|
||||||
### schemeKeys | ||||||
The "schemeKeys" member is a REQUIRED array of strings that reference the schemes defined in the [permission object](#permissionObject) that are supported by the paths in this pathSet object. Each value in this array MUST match one of the keys of the "schemes" member in the [permission object](#permissionObject). | ||||||
|
||||||
### methods | ||||||
The "methods" member is a REQUIRED array of strings that represent the HTTP methods supported by the paths in this pathSet object. | ||||||
|
||||||
### paths | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should provide an example of how the URI template works |
||||||
The "paths" member is a REQUIRED object whose keys contain a simplified URI template to identify the resources protected by this permission object. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do we distinguish between Read and Read.All for paths permissions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Read and Read.All is a convention that Microsoft Graph has that indicates whether you can access any resource of the specified type, or just "yours". It is a scoping mechanism that is opaque to this spec. The model this spec describes currently does not have the capability to map permissions to caller identity. At this time we are only trying to map claims to a URI template that we are primarily using to verify a user has consented an application to access data. If you have any ideas how to describe user authZ as well, I am all ears. |
||||||
|
||||||
### alsoRequires | ||||||
The "alsoRequires" member is logical expression of permissions that must be presented as claims alongside the current permission. | ||||||
|
||||||
``` | ||||||
(User.Read | User.Read.All) & Group.Read | ||||||
``` | ||||||
|
||||||
### includedProperties | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May want to call out the behavior when includeProperties and excludeProperties are not specified. I assume includeProperties defaults to all properties if not specified, and excludeProperties defaults to no properties if not specified |
||||||
The "includedProperties" member is an array of strings that identify properties of the resource representation returned by the path, that are accessible with the permission. | ||||||
|
||||||
### excludedProperties | ||||||
The "includedProperties" member is an array of strings that identify properties of the resource representation returned by the path, that are not accessible with the permission. | ||||||
darrelmiller marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
|
||||||
## <a name="schemeObject"></a>Scheme Object | ||||||
The scheme object has members that describe the permission within the context of the scheme. Additional members provide behavioral constraints of the permission when used with the scheme. | ||||||
|
||||||
```json | ||||||
"schemes": { | ||||||
"DelegatedWork": { | ||||||
"adminDisplayName": "Read and write app activity to users'activity feed", | ||||||
"adminDescription": "Allows the app to read and report the signed-in user's activity in the app.", | ||||||
"consentDisplayName": "Read and write app activity to users'activity feed", | ||||||
"consentDescription": "Allows the app to read and report the signed-in user's activity in the app.", | ||||||
"requiresAdminConsent": true | ||||||
}, | ||||||
"DelegatedPersonal": { | ||||||
"consentDisplayName": "Read and write app activity to users'activity feed", | ||||||
"consentDescription": "Allows the app to read and report the signed-in user's activity in the app." | ||||||
}, | ||||||
"Application": { | ||||||
"adminDisplayName": "Read and write app activity to users' activity feed", | ||||||
"adminDescription": "Allows the app to read and report the signed-in user's activity in the app.", | ||||||
} | ||||||
``` | ||||||
|
||||||
### adminDisplayName | ||||||
The "adminDisplayName" member is a string that provides a short permission name that considers the current scheme and the perspective of a resource administrator. | ||||||
|
||||||
### adminDescription | ||||||
The "adminDescription" member is a string that describes the permission considering the current scheme from the perspective of a resource administrator. | ||||||
|
||||||
### consentDisplayName | ||||||
The "consentDisplayName" member is a REQUIRED string that provides a short permission name that considers the current scheme and the perspective of the user consenting an application. | ||||||
|
||||||
### consentDescription | ||||||
The "consentDescription" member is a REQUIRED string that describes the permission considering the current scheme from the perspective of the user consenting an application. | ||||||
|
||||||
### requiresAdminConsent | ||||||
The "requiresAdminConsent" member is a boolean value with a default value of false. When true, this permission can only be consented by an adminstrator. | ||||||
|
||||||
## <a name="pathObject"></a>Path Object | ||||||
The path object contains properties that affect how the permission object controls access to resource identified by the key of the path object. | ||||||
|
||||||
```json | ||||||
"paths": { | ||||||
"/me/activities/{id}": { | ||||||
"leastPrivilegePath": ["DelegateedWork", "DelegatedPersonal"], | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
"includedProperties": ["id","displayName"], | ||||||
"excludedProperties": ["cost"] | ||||||
} | ||||||
``` | ||||||
|
||||||
### leastPrivilegePath | ||||||
The "leastPrivilegePath" member is an array of strings that identify the schemes for which this permission is the least privilege permission for accessing the path. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: can this not be derived automatically based on the set of paths and the privilegeLevel property? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is what I originally thought and then someone convinced me that is not the case. Let me try and remember what the reasoning was. Nope, can't remember. I will go back to the person and get them to remind me and I'll update this doc. Thanks for the call out. |
||||||
|
||||||
## Appendix A. Model Diagram | ||||||
|
||||||
```mermaid | ||||||
classDiagram | ||||||
|
||||||
class Permission{ | ||||||
note: string | ||||||
implicit: bool | ||||||
requiredEnvironments: string[] | ||||||
ownerEmail:string | ||||||
isHidden: bool | ||||||
privilegeLevel: string | ||||||
} | ||||||
Permission "1" --> "*" PathSet:pathSets | ||||||
Permission "1" --> "*" Scheme:schemes | ||||||
|
||||||
class PathSet{ | ||||||
schemeKeys: string[] | ||||||
methods: string[] | ||||||
alsoRequires: stringExpression | ||||||
includedProperties: string[] | ||||||
excludedProperties: string[] | ||||||
} | ||||||
PathSet "1" --> "*" Path:paths | ||||||
|
||||||
class Path{ | ||||||
leastPrivilegePath: string | ||||||
} | ||||||
|
||||||
class Scheme{ | ||||||
adminDisplayName: string | ||||||
adminDescription: string | ||||||
userDisplayName: string | ||||||
userDescription: string | ||||||
requiresAdminConsent: string | ||||||
} | ||||||
|
||||||
``` | ||||||
|
||||||
## Appendix B. JSON Schema for HTTP Problem | ||||||
```json | ||||||
{ | ||||||
"$schema": "http://json-schema.org/draft-07/schema", | ||||||
"title": "Schema for OAuth Permissions", | ||||||
"type": "object", | ||||||
"properties": { | ||||||
"additionalProperties": false, | ||||||
"permissions": { | ||||||
"title": "Map of permission name to definition", | ||||||
"additionalProperties": false, | ||||||
"type": "object", | ||||||
"patternProperties": { | ||||||
"[\\w]+\\.[\\w]+[\\.[\\w]+]?": { | ||||||
"$ref": "#/definitions/permission" | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
}, | ||||||
"definitions": { | ||||||
"permission": { | ||||||
"type": "object", | ||||||
"title": "Permission definition", | ||||||
"additionalProperties": false, | ||||||
"properties": { | ||||||
"note": {"type": "string"}, | ||||||
"implicit": {"type": "boolean"}, | ||||||
"isHidden": {"type": "boolean"}, | ||||||
"ownerEmail": {"type": "string"}, | ||||||
"privilegeLevel": { | ||||||
"type":"string", | ||||||
"enum":["low","medium","high"] | ||||||
} | ||||||
"requiredEnvironments": { | ||||||
"type": "array", | ||||||
"items": { | ||||||
"type": "string" | ||||||
} | ||||||
}, | ||||||
"alsoRequires": { | ||||||
"type": "string", | ||||||
"pattern": "[\\w]+\\.[\\w]+[\\.[\\w]+]?" | ||||||
}, | ||||||
"schemes": { | ||||||
"type": "object", | ||||||
"patternProperties": { | ||||||
".*": { | ||||||
"$ref": "#/definitions/scheme" | ||||||
} | ||||||
} | ||||||
} | ||||||
}, | ||||||
"pathSets": { | ||||||
"type": "array", | ||||||
"items": { | ||||||
"$ref": "#/definitions/pathSet" | ||||||
} | ||||||
} | ||||||
} | ||||||
}, | ||||||
"pathSet": { | ||||||
"type": "object", | ||||||
"additionalProperties": false, | ||||||
"properties": { | ||||||
"schemeKeys": { | ||||||
"type": "array", | ||||||
"items": { | ||||||
"type": "string" | ||||||
} | ||||||
}, | ||||||
"methods": { | ||||||
"type": "array", | ||||||
"items": { | ||||||
"type": "string", | ||||||
"enum": ["GET","PUT","POST","DELETE","PATCH","HEAD","OPTIONS","<WriteMethods>","<ReadMethods>" | ||||||
] | ||||||
} | ||||||
}, | ||||||
"paths": { | ||||||
"type": "object", | ||||||
"patternProperties": { | ||||||
".*": { | ||||||
"$ref": "#/definitions/path" | ||||||
} | ||||||
} | ||||||
} | ||||||
}, | ||||||
"includeProperties": { | ||||||
"type": "array", | ||||||
"items": { | ||||||
"type": "string" | ||||||
} | ||||||
}, | ||||||
"excludeProperties": { | ||||||
"type": "array", | ||||||
"items": { | ||||||
"type": "string" | ||||||
} | ||||||
} | ||||||
}, | ||||||
"path": { | ||||||
"type": "object", | ||||||
"properties": { | ||||||
"leastPrivilegePath": { | ||||||
"type": "array", | ||||||
"items": { "type":"string"} | ||||||
} | ||||||
}, | ||||||
"scheme": { | ||||||
"type": "object", | ||||||
"properties": { | ||||||
"requiresAdminConsent": { | ||||||
"type": "boolean" | ||||||
}, | ||||||
"adminDisplayName": { | ||||||
"type": "string" | ||||||
}, | ||||||
"adminDescription": { | ||||||
"type": "string" | ||||||
}, | ||||||
"consentDisplayName": { | ||||||
"type": "string" | ||||||
}, | ||||||
"consentDescription": { | ||||||
"type": "string" | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have urls to the RFCs provided here?