owned this note
owned this note
Published
Linked with GitHub
# Initial Connection Proposal
## Initial Examples and Discussion
Initially, Luca's Idea on separating connection and form defaults
https://github.com/w3c/wot-thing-description/pull/2040#discussion_r1822781609
Security is a special connection setup
- conDef + formDef = valid form
- nothing is mandatory in a Def but valid form has mandatory stuff
- invalid form: no href, no contentType, mismatch between uri scheme and vocabulary (`http://` and `cov:method`)
TODO: Find complex examples in current TD
- combo security
- changing contentType
- not using the default contentType
- multi security
conDef: base, security, binding connection defaults
formDef: op, contentType, binding method terms
```js
{
"securityDefinitions":{
"sec1":{
"scheme":"nosec"
}
},
"connectionDefinitions": {
"no_sec+http3" : {
"base": "https://example.com",
"security":"combo",
"allOf":{
"1":{
"scheme":"apikey"
},
"2":{
"scheme":"basic"
}
},
"htv:version": "3",
"transport": "quick"
},
"no_sec+http3" : {
"base": "https://example.com",
"security":"nosec",
"htv:version": "3",
"transport": "quick"
},
"api_sec+http3" : {
"base": "https://example.com",
"security":"apikey",
"in":"header",
"name":"hue-api",
"htv:version": "3",
"transport": "quick"
},
"broker" : {
"base": "mqtt://www.w3.org/2019/wot/broker",
"contentType": "text/plain",
"security":"no_sc"
}
},
"formDefinitions": {
"read_only_http3_properties": {
"op": "readproperty",
"connection": "no_sec+http3", // if this is not present, defaultConnection is picked
"htv:methodName": "POST",
"contentType":"application/cbor"
},
"writing_http3_properties": {
"op": "writeproperty",
"connection": "api_sec+http3",
"htv:methodName": "PUT",
"contentType":"application/cbor"
},
"observe_mqtt_properties": {
"op": "observeproperty",
"connection": "broker",
"htv:methodName": "POST",
"contentType":"application/cbor"
},
"write-with-embedded-connection-schema":{
"op": "writeproperty",
"connection": { // if not a string, it should be the map itself
"base": "https://example.com",
"security":"apikey",
"in":"header",
"name":"hue-api",
"htv:version": "3",
"transport": "quick"
},
"htv:methodName": "PUT",
"contentType":"application/cbor",
"additionalResponses":{
"schema":{"type":"string"}
}
}
},
"schemaDefinitions": {
"schema1":{"type":"string"}
},
"title": "test",
"defaultForm": "read_only_http3_properties", // this and below can also be the object from above if there is only one default form
"defaultForm":{
"base": "https://example.com",
"security":"nosec"
},
"defaultConnection":"no_sec+http3",
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"defaultForm": "observe_mqtt_properties",
"mqv:topic": "application/devices/thing1/program/commands/reset",
"additionalResponses":{ // can be in form definition?
"schema":"schema1"
}
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"defaultForm": "read_only_http3_properties",
"href": "myDevice/properties/prop2",
},
{
"defaultForm": "writing_http3_properties",
"href": "myDevice/properties/prop2"
}
]
}
}
}
```
## Simple Examples
5. Equivalent Example to above with security objects and inline connections (not equivalent yet)
```js
{
"title": "test",
"connectionDefinitions": {
"base": "https://example.com",
"security": {
"scheme":"apikey",
"in":"header",
"name":"hue-api"
}
},
"formDefinitions":{
"contentType":"application/json"
},
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"defaultConnection":"conn1",
"defaultForm":"form1",
"href": "/props/prop1"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"defaultConnection":"conn1",
"defaultForm":"form1",
"href": "/props/prop2"
}
]
}
}
}
```
6. Explicit linking keyword (not equivalent yet)
```js
{
"title": "test",
"connectionDefinitions": {
"conn1" : {
"base": "https://example.com",
"security": {
"scheme":"apikey",
"in":"header",
"name":"hue-api"
},
"security":"mysec1"
}
},
"formDefinitions":{
"form1": {
"contentType":"application/json"
}
},
"securityDefinitions":{
"mysec1":{
"scheme":"apikey",
"in":"header",
"name":"hue-api"
}
},
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"defaultConnection":"conn1",
"defaultForm":"form1",
"security": {
"scheme":"apikey",
"in":"header",
"name":"hue-api"
}
"href": "/props/prop1"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"defaultConnection":"conn1",
"defaultForm":"form1",
"href": "/props/prop2"
}
]
}
}
}
```
## Documentation
### Introduction
Motivation
#### Basic mechanism
##### Defaultable elements
An element that is defaultable has a container `{element}Def` at the root of the Thing that is a map of element of that kind.
Every element as a term `inherit` that points to a single element in `{element}Def`, if inherit is populated all the fields in the pointed element are used as default for the current element.
##### Inlineable fields
A field may contain either a string pointing to an element in `{element}Def` or may contain the element of that kind itself.
##### Thing-wide default
Fields that override the starting default for all the elements of that kind.
##### Usage in the TD
`Form`, `Connection`, `DataSchema` and `Security` are **Defaultable Elements**. All the fields that can take one are **Inlineable fields**.
#### Keywords and Types
##### Thing Level
###### Thing-wide defaults
These set a default for the whole Thing
- **connection**:
- *type*: String pointing to the `connectionDefinitions` map or Object with type `Connection`.
- *Mandatory*: Optional.
- *Description*: A reference to or an in-place definition of a connection definition, if missing a protocol binding default is in place.
- *Remarks*: If string, it MUST refer to a first-level key in `connectionDefinitions`.
- **security**:
- *type*: String pointing to the `securityDefinitions` map or Object with type `Security` (currently called SecurityScheme)
- *Mandatory*: Optional
- *Description*:
- *Remarks*: The array of strings of the current spec can be dropped
- **schema**: This will be done later to express default data schemas (e.g. lwm2m object wrapper)
- **form**:
- *type*: String pointing to the `formDefinitions` map or Object with type`Form`.
- *Mandatory*: Optional
- *Description*: A reference to or an in-place definition of a Form, if missing the affordance/operation-specific defaults apply.
- *Remarks*: If string, it MUST refer to a first-level key in `formDefinitions`.
###### Definitions/Defaults container
- **connectionDefinitions**:
- *type*: Map of Object with of type `Connection`.
- *Mandatory*: Optional
- *Description*: A set of connections that can be reused in forms to group common connection information such as a base URI or security
- *Remarks*: The first-level keys are free to choose by the TD producer.
- **formDefinitions**:
- *type*: Map of Object with of type `Form`.
- *Mandatory*: Optional
- *Description*: A set of form information that can be referenced in a forms to group common form information such as `contentType`.
- *Remarks*: The first-level keys are free to choose by the TD producer.
- **schemaDefinitions**:
- *type*: Map of Object with of type `DataSchema`.
- *Mandatory*: Optional
- *Description*:
- *Remarks*: The first-level keys are free to choose by the TD producer. Same as now
- **securityDefinitions**:
- *type*: Map of Object with of type ~~`SecurityScheme`~~ `Security`.
- *Mandatory*: Optional
- *Description*:
- *Remarks*: The first-level keys are free to choose by the TD producer. Same as now
Note: Even if a single form of an affordance is not complete, a defaultable element should exist.
##### Elements
###### Overall Rules
- All of them contain `inherit`, which has type `String`
- Form can refer to a connection via `Form::connection`
- Connection can refer to a security `Connection::security`
- Form can refer to a schema via `Form::additionalResponse` (no change)
- Security can be overridden in a form using an inline definition for connection. No `security` term in the form level.
- Thing can refer to some forms in `Thing::forms`
- Security cannot refer to a connection, form or schema
- Connection cannot refer to a form or schema
- Schema cannot refer to anything beside itself, i.e. inheriting or `oneOf` etc.
###### Connection
- **base**:
- *type*: String of URI
- *Mandatory*: Optional
- *Description*: The base URI that is used for building an absolute URI together with relative URIs in forms
- *Remarks*: None
- **security**:
- *type*: SecurityDefinition (no change)
- *Mandatory*: Mandatory
- *Description*:
- *Remarks*:
Note: When the security definition moves to the bindings, these terms can be moved a layer up to `connection`
###### Form
- **connection**:
- *type*: String or Object with type `Connection`.
- *Mandatory*: Optional.
- *Description*: A reference to or an in-place definition of a connection definition
- *Remarks*: If string, it MUST refer to a first-level key in `connectionDefinitions`.
- **op**:
- *type*: String or Array of String (no change)
- *Mandatory*: with default
- *Description*:
- *Remarks*:
- **contentType**:
- *type*: String (no change)
- *Mandatory*: with default
- *Description*:
- *Remarks*:
###### Schema
Same as now
###### Security
Same as now
#### Guidelines
hints for designing TDs. When to use this or not.
1. If you have one mechanism (security, connection, form, schema), just inline it.
- Security term at the top level is optional
- McCool not mandatory -> Reducing verbosity, make TDs simpler and shorter. People say it is annoying and does not improve security. Defining a "useless" security at the top and always overwriting it is not clear.
- No resolution: Having no security field at all (none in the forms, none in the top level) -> reverting to defaults
- No resolution: Security defaults:
- McCool auto -> Assuming nosec is wrong assumption
TODO: Documenting why we have multi sec for one Thing. Some properties being public, some not. Reading being public, writing not.
##### Validation Rules
- Reference for an object that doesn't exist: What happens in TD 1.1 is same here.
#### Algorithm
TODO: Discuss flattening, normalization and canonicalization algorithm should take the connection
Each TD form MUST be expanded before using its information in a protocol driver. It does repeat until the there is no populated `inherit`. (single inheritance)
To expand the form in an affordance:
- Check if there is a `connection` or a `form` available. If neither is present, check if there is a top-level `connection` or a `form` available. If neither is present, this form is complete and can be used in a binding driver.
#### Examples
##### TD Examples
1. One Connection, one Form, one security no definitions
1.a Defaults separate
```js
{
"title": "test",
"connection":{
"base": "https://example.com"
},
"form":{
"contentType":"application/json"
},
"security":{
"scheme":"nosec"
},
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"href": "/props/prop1"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"href": "/props/prop2"
}
]
}
}
}
```
1.b Only with form
```js
{
"title": "test",
"form":{
"contentType":"application/json",
"connection":{
"base": "https://example.com",
"security":{
"scheme":"nosec"
}
}
},
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"href": "/props/prop1"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"href": "/props/prop2"
}
]
}
}
}
```
2. Equivalent Example to above, just more verbose
```js
{
"title": "test",
"connectionDefinitions": {
"conn1" : {
"base": "https://example.com"
// "security":"sec1" can be added here instead. We should recommend since there is only one.
}
},
"formDefinitions":{
"form1": {
"contentType":"application/json",
// "connection":"conn1" can be added here instead. We should recommend since there is only one.
}
},
"securityDefinitions":{
"sec1":{
"scheme":"nosec"
}
},
"connection": "conn1",
"form":"form1",
"security":"sec1", // This applies to all connections
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"href": "/props/prop1"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"href": "/props/prop2"
}
]
}
}
}
```
3. Equivalent Example to above without reuse (same as TD 1.0 but with security in form)
```js
{
"title": "test",
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"href": "https://example.com/props/prop1",
"connection":{
"security":{"scheme":"nosec"}
}, // this is possible but we want to flatten
"contentType":"application/json"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"href": "https://example.com/props/prop2",
// applying flattening, we would get this
"security":{"scheme":"basic"},
"contentType":"application/json"
}
]
}
}
}
```
4. Equivalent Example to above without defaults
4.a Completely flat
```js
{
"title": "test",
"securityDefinitions":{
"sec1":{
"scheme":"nosec"
}
},
"connectionDefinitions": {
"conn1" : {
"base": "https://example.com"
}
},
"formDefinitions":{
"form1": {
"contentType":"application/json"
}
},
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"connection":"conn1",
"form":"form1",
"security":"sec1",
"href": "/props/prop1"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"connection":"conn1",
"form":"form1",
"security":"sec1",
"href": "/props/prop2"
}
]
}
}
}
```
4.b Definition-level reusage
```js
{
"title": "test",
"securityDefinitions":{
"sec1":{
"scheme":"nosec"
}
},
"connectionDefinitions": {
"conn1" : {
"security":"sec1",
"base": "https://example.com"
}
},
"formDefinitions":{
"form1": {
"connection":"conn1",
"contentType":"application/json"
}
},
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"form":"form1",
"href": "/props/prop1"
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"form":"form1",
"href": "/props/prop2"
}
]
}
}
}
```
5. Requiring more security for writing
```js
{
"title": "test",
"connectionDefinitions": {
"conn1" : {
"base": "https://example.com"
}
},
"formDefinitions":{
"form1": {
"contentType":"application/json"
}
},
"securityDefinitions":{
"sec1":{
"scheme":"nosec"
},
"sec2":{
"scheme":"basic"
}
},
"connection": "conn1",
"form":"form1",
"security":"sec1",
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"href": "/props/prop1",
"op":"readproperty"
},
{
"href": "/props/prop1",
"op":"writeproperty",
"connection":{
"inherit":"conn1",
"security":"sec2"
}
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"href": "/props/prop2"
}
]
}
}
}
```
```
{
connection: "something",
op: "readproperty"
},
{
connection: "something-strict",
op: "writeproperty"
}
```
##### TD Examples with State Machines for MQTT and WS
TODO
#### How to combine with TMs
TODO
## Archive
### Ege last proposal but without the big container
```js
{
"connectionDefinitions": {
"basichttp1" : {
"href": "https://example.com",
"contentType": "application/cbor",
"security":["basic_sc"],
"htv:methodName":"POST",
"op":"writeproperty"
},
"basichttp2" : {
"connection":"basichttp1",
"htv:methodName":"PUT",
"op":"readproperty"
},
"broker" : {
"href": "mqtt://www.w3.org/2019/wot/broker",
"contentType": "text/plain",
"security":"no_sc"
}
},
"securityDefinitions": {
"no_sec": {
"scheme": "nosec"
},
"basic_sc":{
"scheme": "basic"
}
},
"schemaDefinitions": {
"schema1":{"type":"string"}
},
"title": "test",
"security": "no_sec",
"connection":"basichttp1",
"properties": {
"prop1": {
"type":"string",
"forms": [
{
"connection": "broker",
"op": "readproperty",
"mqv:topic": "application/devices/thing1/program/commands/reset",
"additionalResponses":{
"schema":"schema1"
}
}
]
},
"prop2": {
"type":"string",
"forms": [
{
"connection": "basichttp2",
"href": "myDevice/properties/prop2",
"htv:methodName":"GET"
},
{
"connection": "basichttp1",
"href": "myDevice/properties/prop2"
}
]
}
}
}
```