Closed brian-comply0 closed 1 year ago
Clean up spec to clarify the use of oAuth2 tokens within the header of API calls.
NOTE: The implementation will handle the connection between an oAuth2 token and an identity as well as the access rights of that identity.
Handling of versions:
4/7/23 Sprint Planning - Moved from Sprint 44 to Sprint 45
Pushing to Sprint 46
We worked on the following yesterday:
When Valid | 200 | Return | 201 | Return | 400 Bad Request |
401 Unauthorized |
403 Forbidden |
404 Not Found |
409 Conflict |
410 Gone |
415 Unsupported Media Type |
422 Unprocessable Content |
|
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
GET | Any endpoint | X | Object | X | X | X | X | X | |||||
PUT | Any endpoint | X | Object | X | Object, Location Header | X | X | X | X | X | X | X | X |
DELETE | Any endpoint, where cardinality is not exactly 1 | X | Object | X | X | X | X | X | |||||
PATCH | Any endpoint that can unambigulously point to a single object | X | Object | X | X | X | X | X | X | X | X | ||
POST | Any endpoint that can unambiguously point to a single object | X | Object, Location Header | X | X | X | X | X | X | X | X |
We updated the above guidance as follows: | Node Cardinality | Node's ID/UUID Cardinality | Description Template | |
---|---|---|---|---|
GET by identifier (1 object returned) |
n/a | 0 | [Not possible - do not generate] | |
GET by identifier (1 object returned) |
n/a | 0 or 1 | When ["an id" | "a uuid"] is available, returns the [node name] represented by the identifier. | |
GET by identifier (1 object returned) |
n/a | 1 | Returns the [node name] represented by the identifier. | |
GET no identifier (1 or more objects returned) |
n/a | n/a | Returns every relevant [node name]. | |
PUT | n/a | 0 | [Not possible - do not generate] | |
PUT | n/a | 0 or 1 | When ["an id" | "a uuid"] is available, updates the [node name] specified by the identifier, and returns the updated [node name]. | |
PUT | n/a | 1 | Updates the [node name] specified by the identifier. | |
DELETE | exactly 1 | n/a | [Not allowed - do not generate] | |
DELETE | other than exactly 1 | 0 | [Not possible - do not generate] | |
DELETE | other than exactly 1 | 0 or 1 | When ["an id" | "a uuid"] is available, deletes the [node name] specified by the identifier. | |
DELETE | other than exactly 1 | 1 | Deletes the [node name] specified by the identifier. | |
PATCH | n/a | 0 | [Not possible - do not generate] | |
PATCH | n/a | 0 or 1 | When ["an id" | "a uuid"] is available, updates the [node name] specified by the identifier. | |
PATCH | n/a | 1 | Updates the [node name] specified by the identifier. | |
POST | 0 or 1 | n/a | When a [node name] does not exist, adds a new [node name]. | |
POST | exactly 1 | n/a | [Not allowed - do not generate] | |
POST | 0/1 or more | n/a | Adds a new [node name]. |
NOTES:
and broke the return codes out into a separate table: | 200 | Return | 201 | Return | 400 Bad Request |
401 Unauthorized |
403 Forbidden |
404 Not Found |
409 Conflict |
410 Gone |
415 Unsupported Media Type |
422 Unprocessable Content |
|
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
GET | X | Object | X | X | X | X | X | ||||||
PUT | X | Object | X | Object, Location Header | X | X | X | X | X | X | X | X | |
DELETE | X | Object | X | X | X | X | X | ||||||
PATCH | X | Object | X | X | X | X | X | X | X | X | |||
POST | X | Object, Location Header | X | X | X | X | X | X | X | X |
@brian-easyd Thanks for posting these updates here.
@mpemy - As discussed, this issue is morphing into "OSCAL REST API Basic" and focuses on file-level operations. A separate issue will be created for "OSCAL REST API Complete" with the more detailed operations.
For every model, we should have:
PRIMARY
GET /[model-name]
POST /[model-name]
GET /[model-name]/{identifier}
PUT /[model-name]/{identifier}
PATCH /[model-name]/{identifier}
DELETE /[model-name]/{identifier}
SNAPSHOTS
GET /[model-name]/{identifier}/snapshot
POST /[model-name]/{identifier}/snapshot
- Creates a newGET /[model-name]/{identifier}/snapshot/{identifier}
PATCH /[model-name]/{identifier}/snapshot/{identifier}
For updating Metadata PropertiesDELETE /[model-name]/{identifier}/snapshot/{identifier}
NOTE: Due to the nature of snapshots, there would not be a PUT. We would never replace a snapshot. We might patch it.ATTACHMENTS
GET /[model-name]/{identifier}/attachment
POST /[model-name]/{identifier}/attachment
GET /[model-name]/{identifier}/attachment/{identifier}
PUT /[model-name]/{identifier}/attachment/{identifier}
DELETE /[model-name]/{identifier}/attachment/{identifier}
NOTE: No PATCH as attachments are often binary and patch would be invalid. Attachments should always be replaced, not updated.PROFILE RESOLUTION For Profiles, we also need to handle resolved profiles. In addition the above, Profile endpoints include:
GET /profile/{identifier}/resolved
- returns a resolved profile catalog based on current contentPOST /profile/{identifier}/resolved-snapshot
- caches a resolved profile catalog at a point in timeGET /profile/{identifier}/resolved-snapshot
- retrieves the list of cached resolved profile catalogsGET /profile/{identifier}/resolved-snapshot{identifier}
- retrieves a specific resolved profile catalog snapshotDELETE /profile/{identifier}/resolved-snapshot{identifier}
- removes a cached resolved profile catalog6/8/23 Sprint Planning - Moved from Sprint 48 to Sprint 49
6/22/23 Sprint Planning - Moved from Sprint 49 to Sprint 50
7/6/23 Sprint Planning - Moved from Sprint 50 to Sprint 51
GET /*
GET /catalog
should be catalog-list
POST /*
POST /catalog
should return:
{
"file-id": "string",
"title": "string",
"published": "2023-07-07T00:36:33.367Z",
"last-modified": "2023-07-07T00:36:33.367Z",
"version": "string",
"oscal-version": "string",
"document-ids": {
"document-ids": [
{
"document-id": {
"scheme": "string",
"identifier": "string"
}
}
]
},
"markings": [
"string"
],
"remarks": "string"
}
GET /*/{id}
good
PUT /*
PATCH /*
good
DELETE /*
good
GET /*/{id}/attachment
GET /catalog/{id}/attachment
should be attachment-list
base64
fieldPOST /*/{id}/attachment/{id}
good
GET /*/{id}/attachment/{id}
PUT /*/{id}/attachment
base64
fieldGET /*/{id}/attachment/{id}
responses:
'200':
description: A binary file in any type
content:
multipart/form-data: # Media type
schema: # Request payload
type: object
properties: # Request parts
id: # Part 1 (string value)
type: string
format: uuid
name: # Part2 (object)
type: string
attachment-binary: # Part 3 (an image)
type: string
format: binary
PUT /*/{id}/attachment/{id}
DELETE /*/{id}/attachment/{id}
GET /*/{id}/snapshot
GET /catalog/{id}/snapshot
should be snapshot-list
POST /*/{id}/snapshot
POST /catalog/{id}/shapshot
should return:
{
"file-id": "string",
"title": "string",
"published": "2023-07-07T00:36:33.367Z",
"last-modified": "2023-07-07T00:36:33.367Z",
"version": "string",
"oscal-version": "string",
"document-ids": {
"document-ids": [
{
"document-id": {
"scheme": "string",
"identifier": "string"
}
}
]
},
"markings": [
"string"
],
"remarks": "string"
}
GET /*/{id}/snapshot/{id}
good
PATCH /*/{id}/snapshot/{id}
good
DELETE /*/{id}/snapshot/{id}
good
GET /profile/{id}/resolved
good
GET /profile/{id}/resolved-snapshot
{
"resolved-snapshot-list": [
{
"file-id": "string",
"title": "string",
"published": "2023-07-07T01:38:58.912Z",
"last-modified": "2023-07-07T01:38:58.912Z",
"version": "string",
"oscal-version": "string",
"document-ids": {
"document-ids": [
{
"document-id": {
"scheme": "string",
"identifier": "string"
}
}
]
},
"markings": [
"string"
],
"remarks": "string"
}
]
}
POST /profile/{id}/resolved-snapshot
good
GET /profile/{id}/resolved-snapshot/{id}
good
DELETE /profile/{id}/resolved-snapshot/{id}
good
PUT
- use 204 No Content https://swagger.io/docs/specification/describing-responses/#headers
PATCH - Use 200
and return the document for the "level" where the request was made.
PATCH /catalog/{catalogId}
should return the full catalog regardless of what fields were included in the response. PATCH /catalog/metadata/parties/{partyId}
would return the entire modified party (note not the catalog) regardless of what field was modified. If the client needs the UUID/last-modified, they can GET
for the full document
Brian's note: PATCH /catalog/metadata/parties/{partyId}
is not part of the basic spec. This will be relevant in the complete spec later.
GET /catalog/{catalogId}/attachment
This should return an object with:
{
"attachment-list": [
{
"id": "string",
"media-type": "string",
"name": "string"
}
]
}
GET /catalog/{catalogId}/attachment/{attachmentId}
Content-Disposition: attachment; filename=NAME
(where NAME is the name
attribute from the JSON)Content-Type: TYPE
(TYPE is the media-type
from the JSON)We may need to, for OpenAPI reasons, specify the content type as application/octet-stream
however we should CLEARLY AND THOROUGHLY document that it will be variable based on the specified media type and that octet-stream
is the FALLBACK
POST /[MODEL-NAME]/
return root should be [MODEL-NAME]-list
instead of simply [MODEL-NAME]
, which will have exactly one item in it.
Descriptions in general
attachment
endpoints that end in "... represented by the id" should say "... reprented by the attachment ID" (add "attachment")snapshot
endpoints that end in "... represented by the id" should say "... reprented by the snapshot ID" (add "snapshot") resolved-snapshot
endpoints that end in "... represented by the id" should say "... reprented by the resolved snapshot ID" (add "resolved snapshot") PUT /[MODEL-NAME]/{id}
PATCH /[MODEL-NAME]/{id}
description should say "Updates a portion of the [model name] represented by the ID." (inserting "a portion of")
PUT /[MODEL-NAME]/{id}/attachment/{id}
DELETE endpoints
DELETE /[MODEL-NAME]/{id}
DELETE /[MODEL-NAME]/{id}/attachment/{id}
DELETE /[MODEL-NAME]/{id}/snapshot/{id}
DELETE /[MODEL-NAME]/{id}/resolved-snapshot/{id}
GET
before a DELETE
if they want to offer an undo feature.POST /[MODEL-NAME]/{id}/snapshot
[MODEL-NAME]-list
instead of simply [MODEL-NAME]
, which will have exactly one item in it.PATCH /[MODEL-NAME]/{id}/snapshot/{id}
description should say "Updates a portion of the snapshot represented by the Snapshot ID." (inserting "a portion of")
User Story
As a developer of OSCAL-enabled tools, I want a REST API specification that - when implemented as a service - enables me to view and manipulate OSCAL files contained within any organization's repository with this specification implemented.
Acceptance Criteria
Background
With increased understanding of OSCAL within the team, and a more refined understanding of how entities will interact with OSCAL content, we need to revise the REST API specification such that it more completely addresses the user story above.
This includes, but is not limited to: