| <?xml version="1.0" encoding="UTF-8"?> |
| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> |
| <channel> |
| <title>Eclipse Ditto Blog</title> |
| <description>Announcements, tutorials and examples around Eclipse Ditto and Digital Twins</description> |
| <link>https://www.eclipse.org/ditto/</link> |
| <atom:link href="https://www.eclipse.org/ditto/feed.xml" rel="self" type="application/rss+xml"/> |
| <pubDate>Fri, 25 Jun 2021 11:35:48 +0000</pubDate> |
| <lastBuildDate>Fri, 25 Jun 2021 11:35:48 +0000</lastBuildDate> |
| <generator>Jekyll v3.6.2</generator> |
| |
| <item> |
| <title>Support for HMAC-SHA256 signing for connections</title> |
| <description><p>With the upcoming release of Eclipse Ditto <strong>version 2.1.0</strong> it will be possible to use HMAC-SHA256 signing for |
| connections. The currently implemented algorithms support you in authenticating requests against:</p> |
| <ul> |
| <li>Azure IoT Hub REST API</li> |
| <li>Azure IoT Hub AMQP 1.0</li> |
| <li>Azure HTTP Monitor Data Collector API</li> |
| <li>Azure Service Bus REST API</li> |
| <li>Amazon Simple Notification Service (Amazon SNS)</li> |
| <li>other AWS services supporting Signature Version 4 signing (see external AWS documentation on |
| <a href="https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html">Signing AWS API requests</a>)</li> |
| </ul> |
| |
| <p>Detailed information can be found at <a href="connectivity-hmac-signing.html">Connectivity API &gt; HMAC request signing</a>.</p> |
| |
| <p>This blog post shows different configurations with the <code class="highlighter-rouge">az-sasl</code> algorithm, that allow you to sign requests |
| against Azure IoT Hub (HTTP Push and AMQP 1.0) as well as Azure Service Bus (HTTP Push).</p> |
| |
| <h1 id="prerequisites">Prerequisites</h1> |
| |
| <p>For the examples you’ll need a running instance of Eclipse Ditto (see <a href="installation-running.html">Running Ditto</a>). |
| Additionally, a simple Thing is required, to which messages can be sent to. |
| Create the Thing <code class="highlighter-rouge">ditto:thing</code> with a <code class="highlighter-rouge">PUT</code> request to <code class="highlighter-rouge">&lt;ditto&gt;/api/2/things/ditto:thing</code> and content</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thingId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ditto:thing"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w"> |
| </span><span class="s2">"_policy"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"entries"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"DEFAULT"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"subjects"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"{{ request:subjectId }}"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"the creator"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"integration:ditto"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"the connections"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"resources"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"policy:/"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"READ"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"WRITE"</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"thing:/"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"READ"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"WRITE"</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"message:/"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"READ"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"WRITE"</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h1 id="azure-iot-hub-rest-api">Azure IoT Hub REST API</h1> |
| |
| <p>This example shows how to invoke a <a href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-direct-methods">direct method</a> |
| on a device using Eclipse Ditto and Azure IoT Hub.</p> |
| |
| <p>For creating signed requests against the Azure IoT Hub REST API, you’ll need the following information from your |
| Azure IoT Hub instance:</p> |
| <ul> |
| <li>The hostname (e.g. <code class="highlighter-rouge">my-hub.azure-devices.net</code>).</li> |
| <li>The name of a (Azure IoT Hub) shared access policy, which has the <code class="highlighter-rouge">Service connect</code> permission. By default, there |
| should be a policy named <code class="highlighter-rouge">service</code> which provides this permission.</li> |
| <li>The primary or secondary key of above policy.</li> |
| <li>A device in Azure IoT Hub with the ID <code class="highlighter-rouge">ditto:thing</code> (for the example this needs to be the same ID as the Thing created in prerequisites).</li> |
| </ul> |
| |
| <p>What follows is a sample connection JSON for a connection named <code class="highlighter-rouge">Azure IoT Hub HTTP</code>, using hostname <code class="highlighter-rouge">my-hub.azure-devices.net</code> and |
| shared access policy <code class="highlighter-rouge">service</code> with key <code class="highlighter-rouge">theKey</code>. You can set the correct values from your Azure IoT Hub subscription |
| in the fields <code class="highlighter-rouge">uri</code>, <code class="highlighter-rouge">credentials.parameters.sharedKeyName</code>, <code class="highlighter-rouge">credentials.parameters.sharedKey</code> and |
| <code class="highlighter-rouge">credentials.parameters.endpoint</code>.</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"60d193e3-2639-415b-af29-0e337741141d"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Azure IoT Hub HTTP"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http-push"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://my-hub.azure-devices.net:443"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sources"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> |
| </span><span class="s2">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> |
| </span><span class="s2">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"POST:/twins/{{ thing:id }}/methods?api-version=2018-06-30"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"topics"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"_/_/things/live/messages"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"authorizationContext"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"integration:ditto"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"issuedAcknowledgementLabel"</span><span class="p">:</span><span class="w"> </span><span class="s2">"live-response"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headerMapping"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w"> |
| </span><span class="s2">"payloadMapping"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"javascript"</span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"clientCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"failoverEnabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"validateCertificates"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"processorPoolSize"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"specificConfig"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"parallelism"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"mappingDefinitions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"javascript"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"mappingEngine"</span><span class="p">:</span><span class="w"> </span><span class="s2">"JavaScript"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"options"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"incomingScript"</span><span class="p">:</span><span class="w"> </span><span class="s2">"function mapToDittoProtocolMsg(</span><span class="se">\n</span><span class="s2"> headers,</span><span class="se">\n</span><span class="s2"> textPayload,</span><span class="se">\n</span><span class="s2"> bytePayload,</span><span class="se">\n</span><span class="s2"> contentType</span><span class="se">\n</span><span class="s2">) {</span><span class="se">\n\n</span><span class="s2"> if (contentType === 'application/vnd.eclipse.ditto+json') {</span><span class="se">\n</span><span class="s2"> return JSON.parse(textPayload);</span><span class="se">\n</span><span class="s2"> } else if (contentType === 'application/octet-stream') {</span><span class="se">\n</span><span class="s2"> try {</span><span class="se">\n</span><span class="s2"> return JSON.parse(Ditto.arrayBufferToString(bytePayload));</span><span class="se">\n</span><span class="s2"> } catch (e) {</span><span class="se">\n</span><span class="s2"> return null;</span><span class="se">\n</span><span class="s2"> }</span><span class="se">\n</span><span class="s2"> }</span><span class="se">\n</span><span class="s2"> return null;</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"outgoingScript"</span><span class="p">:</span><span class="w"> </span><span class="s2">"function mapFromDittoProtocolMsg(</span><span class="se">\n</span><span class="s2"> namespace,</span><span class="se">\n</span><span class="s2"> name,</span><span class="se">\n</span><span class="s2"> group,</span><span class="se">\n</span><span class="s2"> channel,</span><span class="se">\n</span><span class="s2"> criterion,</span><span class="se">\n</span><span class="s2"> action,</span><span class="se">\n</span><span class="s2"> path,</span><span class="se">\n</span><span class="s2"> dittoHeaders,</span><span class="se">\n</span><span class="s2"> value,</span><span class="se">\n</span><span class="s2"> status,</span><span class="se">\n</span><span class="s2"> extra</span><span class="se">\n</span><span class="s2">) {</span><span class="se">\n\n</span><span class="s2"> let headers = dittoHeaders;</span><span class="se">\n</span><span class="s2"> let payload = {</span><span class="se">\n</span><span class="s2"> </span><span class="se">\"</span><span class="s2">methodName</span><span class="se">\"</span><span class="s2">: action,</span><span class="se">\n</span><span class="s2"> </span><span class="se">\"</span><span class="s2">responseTimeoutInSeconds</span><span class="se">\"</span><span class="s2">: parseInt(dittoHeaders.timeout),</span><span class="se">\n</span><span class="s2"> </span><span class="se">\"</span><span class="s2">payload</span><span class="se">\"</span><span class="s2">: value</span><span class="se">\n</span><span class="s2"> };</span><span class="se">\n</span><span class="s2"> let textPayload = JSON.stringify(payload);</span><span class="se">\n</span><span class="s2"> let bytePayload = null;</span><span class="se">\n</span><span class="s2"> let contentType = 'application/json';</span><span class="se">\n\n</span><span class="s2"> return Ditto.buildExternalMsg(</span><span class="se">\n</span><span class="s2"> headers, // The external headers Object containing header values</span><span class="se">\n</span><span class="s2"> textPayload, // The external mapped String</span><span class="se">\n</span><span class="s2"> bytePayload, // The external mapped byte[]</span><span class="se">\n</span><span class="s2"> contentType // The returned Content-Type</span><span class="se">\n</span><span class="s2"> );</span><span class="se">\n</span><span class="s2">}</span><span class="se">\n</span><span class="s2">"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"loadBytebufferJS"</span><span class="p">:</span><span class="w"> </span><span class="s2">"false"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"loadLongJS"</span><span class="p">:</span><span class="w"> </span><span class="s2">"false"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"credentials"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hmac"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"algorithm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"az-sasl"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"parameters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"sharedKeyName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"service"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sharedKey"</span><span class="p">:</span><span class="w"> </span><span class="s2">"theKey"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"endpoint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my-hub.azure-devices.net"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>This connection configuration sends live messages to the endpoint at |
| <code class="highlighter-rouge">https://my-hub.azure-devices.net:443/twins/{{ thing:id }}/methods?api-version=2018-06-30</code> |
| and signs each request with a <a href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-dev-guide-sas?tabs=node">Shared Access Signature</a>. |
| The configuration also contains an outgoing JavaScript payload mapping, which extracts the subject of the live message, |
| its timeout and its payload and uses this to construct a JSON message in the required direct method format. |
| You’ll see that format below.</p> |
| |
| <h2 id="listen-for-direct-method-on-the-device">Listen for direct method on the device</h2> |
| |
| <p>Your Azure IoT Hub device needs to listen to the direct method you’re calling. Imagine you want to call a |
| device method <code class="highlighter-rouge">getDeviceLog</code> on a device. You can use the Nodejs sample <a href="https://github.com/Azure/azure-iot-sdk-node/blob/f526f203ddc9ee32e3c92066312417d2ef6303de/device/samples/device_methods.js">device_methods.js from azure-iot-sdk-node/device/samples</a> |
| as a starter. You’ll need to set the <code class="highlighter-rouge">DEVICE_CONNECTION_STRING</code> environment variable to a connection string of the |
| device you are using and can run the device with <code class="highlighter-rouge">npm install &amp;&amp; node device_methods.js</code>.</p> |
| |
| <h2 id="send-a-live-message-to-the-thing">Send a live message to the Thing</h2> |
| |
| <p>To invoke the <code class="highlighter-rouge">getDeviceLog</code> method on the device, you should now be able to send a live message to the Thing. The message |
| will be forwarded through your connection <code class="highlighter-rouge">Azure IoT Hub HTTP</code> to the Azure IoT Hub, which will route it to the device |
| and respond with its response.</p> |
| |
| <p><code class="highlighter-rouge">POST</code> a message to <code class="highlighter-rouge">&lt;ditto&gt;/api/2/things/ditto:thing/inbox/messages/getDeviceLog?timeout=5s</code> with content:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"service"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my-microservice"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"amount"</span><span class="p">:</span><span class="w"> </span><span class="mi">9000</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>The payload mapping of the connection will turn this into a direct method:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"methodName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"getDeviceLog"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"responseTimeoutInSeconds"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"payload"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"service"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my-microservice"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"amount"</span><span class="p">:</span><span class="w"> </span><span class="mi">9000</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>If you didn’t change the sample code of the device, you should get a response containing <code class="highlighter-rouge">example payload</code>.</p> |
| |
| <h1 id="azure-iot-hub-amqp">Azure IoT Hub AMQP</h1> |
| |
| <p>For using the Azure IoT Hub AMQP endpoint, basically the same prerequisites as for the HTTP endpoint apply. |
| The difference is, that it’s not possible to invoke direct methods using the AMQP endpoint. Instead, you can |
| send a Cloud To Device (C2D) message, on which the device listens.</p> |
| |
| <div class="alert alert-info" role="alert"><i class="fa fa-info-circle"></i> <b>Note:</b> Instead of signing each request like in HTTP push connections, the connection itself is |
| established with signed connection information. For this, the <code class="highlighter-rouge">ttl</code> parameter of the <code class="highlighter-rouge">az-sasl</code> algorithm applies |
| (see <a href="connectivity-hmac-signing.html#az-sasl">documentation of the az-sasl algorithm</a>).</div> |
| |
| <p>You can use the Nodejs sample <a href="https://github.com/Azure/azure-iot-sdk-node/blob/f526f203ddc9ee32e3c92066312417d2ef6303de/device/samples/simple_sample_device.js">simple_sample_device.js from azure-iot-sdk-node/device/samples</a> |
| to listen on C2D messages as a device. You’ll need to set the <code class="highlighter-rouge">DEVICE_CONNECTION_STRING</code> environment variable to a connection string of the |
| device you are using and can run the device with <code class="highlighter-rouge">npm install &amp;&amp; node simple_sample_device.js</code>.</p> |
| |
| <p>What follows is a sample connection JSON for a connection named <code class="highlighter-rouge">Azure IoT Hub AMPQ</code>, using hostname <code class="highlighter-rouge">my-hub.azure-devices.net</code> and |
| shared access policy <code class="highlighter-rouge">service</code> with key <code class="highlighter-rouge">theKey</code>. You can set the correct values from your Azure IoT Hub subscription |
| in the fields <code class="highlighter-rouge">uri</code>, <code class="highlighter-rouge">credentials.parameters.sharedKeyName</code>, <code class="highlighter-rouge">credentials.parameters.sharedKey</code> and |
| <code class="highlighter-rouge">credentials.parameters.endpoint</code>.</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8caca8c6-10d1-4886-a61f-3ea6270f9d8e"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Azure IoT Hub AMQP"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqp-10"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqps://my-hub.azure-devices.net:5671"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sources"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> |
| </span><span class="s2">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> |
| </span><span class="s2">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/messages/devicebound"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"topics"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"_/_/things/live/messages"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"authorizationContext"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"integration:ditto"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"headerMapping"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"iothub-ack"</span><span class="p">:</span><span class="w"> </span><span class="s2">"full"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"to"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/devices/{{thing:id}}/messages/devicebound"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"clientCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"failoverEnabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"validateCertificates"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"processorPoolSize"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"credentials"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hmac"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"algorithm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"az-sasl"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"parameters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"sharedKeyName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"service"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sharedKey"</span><span class="p">:</span><span class="w"> </span><span class="s2">"theKey"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"endpoint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my-hub.azure-devices.net"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>Instead of applying a payload mapping like in the HTTP example, the connection just sends the ditto protocol live message |
| and you should see its content on the device.</p> |
| |
| <p><code class="highlighter-rouge">POST</code> a message to <code class="highlighter-rouge">&lt;ditto&gt;/api/2/things/ditto:thing/inbox/messages/C2DMessage?timeout=0</code> with content</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"Hello"</span><span class="p">:</span><span class="w"> </span><span class="s2">"from Ditto"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| <p>and see it arrive at the device.</p> |
| |
| <div class="alert alert-info" role="alert"><i class="fa fa-info-circle"></i> <b>Note:</b> Using the query parameter <code class="highlighter-rouge">timeout=0</code>, you can tell Ditto to not wait for an answer, since |
| devices can’t respond to C2D messages.</div> |
| |
| <h1 id="azure-service-bus">Azure Service Bus</h1> |
| |
| <p>This example shows how to send a message to an Azure Service Bus via Eclipse Ditto.</p> |
| |
| <p>For creating signed requests against the Azure Service Bus, you’ll need the following information from your |
| Azure IoT Hub instance:</p> |
| <ul> |
| <li>A queue (e.g. <code class="highlighter-rouge">my-queue</code>).</li> |
| <li>The hostname (e.g. <code class="highlighter-rouge">my-bus.servicebus.windows.net</code>).</li> |
| <li>The name of a (Azure Service Bus) shared access policy, which has the <code class="highlighter-rouge">Send</code> and <code class="highlighter-rouge">Listen</code> permissions. By default, there |
| should be a policy named <code class="highlighter-rouge">RootManageSharedAccessKey</code> which provides this permission (but shouldn’t be used in production |
| scenarios).</li> |
| <li>The <code class="highlighter-rouge">Base64</code> encoded primary or secondary key of above policy. The signing will only work if you encode the key with |
| <code class="highlighter-rouge">Base64</code> (although it already has <code class="highlighter-rouge">Base64</code> encoding). E.g. if the primary key is <code class="highlighter-rouge">theKey</code>, you need to use its encoded |
| version <code class="highlighter-rouge">dGhlS2V5</code>.</li> |
| </ul> |
| |
| <p>What follows is a sample connection JSON for a connection named <code class="highlighter-rouge">Azure Service Bus HTTP</code>, using hostname <code class="highlighter-rouge">my-bus.servicebus.windows.net</code> and |
| shared access policy <code class="highlighter-rouge">RootManageSharedAccessKey</code> with encoded key <code class="highlighter-rouge">dGhlS2V5</code> (<code class="highlighter-rouge">theKey</code>). You can set the correct values |
| from your Azure Service Bus subscription in the fields <code class="highlighter-rouge">uri</code>, <code class="highlighter-rouge">credentials.parameters.sharedKeyName</code>, |
| <code class="highlighter-rouge">credentials.parameters.sharedKey</code> and |
| <code class="highlighter-rouge">credentials.parameters.endpoint</code> (which is a combination of the hostname and queue name).</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"adec2846-4d11-4e0a-b456-d8bfc2192fc6"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Azure Service Bus HTTP"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http-push"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://my-bus.servicebus.windows.net:443"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sources"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> |
| </span><span class="s2">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> |
| </span><span class="s2">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"POST:/my-queue/messages"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"topics"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"_/_/things/live/messages"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"authorizationContext"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"integration:ditto"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"issuedAcknowledgementLabel"</span><span class="p">:</span><span class="w"> </span><span class="s2">"live-response"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headerMapping"</span><span class="p">:</span><span class="w"> </span><span class="p">{}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"clientCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"failoverEnabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"validateCertificates"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"processorPoolSize"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"specificConfig"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"parallelism"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"credentials"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hmac"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"algorithm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"az-sasl"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"parameters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"sharedKeyName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"RootManageSharedAccessKey"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sharedKey"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dGhlS2V5"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"endpoint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://my-bus.servicebus.windows.net/my-queue"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>This connection configuration sends live messages to the endpoint at <code class="highlighter-rouge">https://my-hub.servicebus.windows.net.net:443/my-queue/messages</code> |
| and signs each request with a <a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-sas#overview-of-sas">Shared Access Signature</a>.</p> |
| |
| <h2 id="listen-for-the-message">Listen for the message</h2> |
| |
| <p>To listen for the message you send into the Service Bus, you can use the Nodejs sample |
| <a href="https://github.com/Azure/azure-sdk-for-js/blob/d7d67ccc865318f3dc6395e0db6997b5af8cf5e8/sdk/servicebus/service-bus/samples/v7/javascript/receiveMessagesStreaming.js">receiveMessagesStreaming.js from azure-sdk-for-js/sdk/servicebus/service-bus/samples/v7/javascript</a>. |
| You’ll need to set the <code class="highlighter-rouge">SERVICEBUS_CONNECTION_STRING</code> environment variable to the connection string of the used |
| shared access policy. Also, you’ll need to set the <code class="highlighter-rouge">QUEUE_NAME</code> environment variable to the |
| used queue (e.g. <code class="highlighter-rouge">my-queue</code>). You should be able to run the sample using <code class="highlighter-rouge">npm install &amp;&amp; node receiveMessagesStreaming.js</code>.</p> |
| |
| <h2 id="send-a-live-message-to-the-thing-1">Send a live message to the Thing</h2> |
| |
| <p>To send a message to Service Bus, you can simply send a live message to a Thing. The message |
| will be forwarded through your connection <code class="highlighter-rouge">Azure Service Bus HTTP</code> to the Azure Service Bus, from which the sample |
| app can read the message.</p> |
| |
| <p><code class="highlighter-rouge">POST</code> a message to <code class="highlighter-rouge">&lt;ditto&gt;/api/2/things/ditto:thing/inbox/messages/HelloServiceBus</code> with content:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"Hello"</span><span class="p">:</span><span class="w"> </span><span class="s2">"from Ditto"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>If you didn’t change the sample code you should see the message arriving there.</p> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Find details on the different algorithms and their parameters at |
| <a href="connectivity-hmac-signing.html">Connectivity API &gt; HMAC request signing</a>.</p> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions regarding this new functionality.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Thu, 17 Jun 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-06-17-hmac-credentials.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-06-17-hmac-credentials.html</guid> |
| |
| <category>blog</category> |
| |
| <category>architecture</category> |
| |
| <category>connectivity</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Announcing Eclipse Ditto Release 2.0.0</title> |
| <description><p>Today, ~1.5 years after release <a href="2019-12-12-release-announcement-100.html">1.0.0</a>, the Eclipse Ditto team is happy to |
| announce the availability of Eclipse Ditto <a href="https://projects.eclipse.org/projects/iot.ditto/releases/2.0.0">2.0.0</a>.</p> |
| |
| <p>With the major version 2.0.0 the Ditto team removed technical debt and ended support for APIs which were deprecated |
| long ago in order to have a better maintainable codebase. However some awesome new features are included as well.</p> |
| |
| <h2 id="adoption">Adoption</h2> |
| |
| <p>Companies are willing to show their adoption of Eclipse Ditto publicly: <a href="https://iot.eclipse.org/adopters/?#iot.ditto">https://iot.eclipse.org/adopters/?#iot.ditto</a></p> |
| |
| <p>From our various <a href="feedback.html">feedback channels</a> we however know of more adoption.<br /> |
| If you are making use of Eclipse Ditto, it would be great to show this by adding your company name to that list of |
| known adopters.<br /> |
| In the end, that’s one main way of measuring the success of the project.</p> |
| |
| <h2 id="changelog">Changelog</h2> |
| |
| <p>The main improvements and additions of Ditto 2.0.0 are:</p> |
| |
| <ul> |
| <li>Merge/PATCH updates of digital twins</li> |
| <li>Configurable OpenID Connect / OAuth2.0 claim extraction to be used for authorization</li> |
| <li>Establishing connections to endpoints (via AMQP, MQTT, HTTP) utilizing a Ditto managed SSH tunnel</li> |
| <li>Addition of a DevOps API in order to retrieve all known connections</li> |
| <li>Expiring policy subjects + publishing of announcement message prior to expiry</li> |
| <li>Addition of policy actions in order to inject a policy subject based on a provided JWT</li> |
| <li>Built-in acknowledgement for search updates to have the option of twin updates with strong consistency of the search index</li> |
| <li>Restoring active connection faster after a hard restart of the Ditto cluster via automatic prioritization of connections</li> |
| <li>Support for LastWill/Testament + retain flag for MQTT connections</li> |
| </ul> |
| |
| <p>The step to a major version was done because of the following breaking API changes:</p> |
| |
| <ul> |
| <li>Removal of “API version 1” (deprecated in <a href="release_notes_110.html#deprecations">Ditto 1.1.0</a>) |
| from Ditto’s Java APIs + HTTP API</li> |
| <li>Removal of code in Java APIs marked as <code class="highlighter-rouge">@Deprecated</code></li> |
| <li>Binary incompatible changes to Java APIs</li> |
| <li>Restructuring of Ditto’s Maven modules in order to simplify/ease further development</li> |
| </ul> |
| |
| <p>The following non-functional enhancements are also included:</p> |
| |
| <ul> |
| <li>Improvement of stability during rolling updates</li> |
| <li>Addition of sharding concept for Ditto internal pub/sub enabling connection of e.g. tens of thousands Websocket sessions</li> |
| <li>Background cleanup improvements in order to have less impact on DB roundtrip times</li> |
| <li>Update of third party libraries (e.g. Akka)</li> |
| <li>Documentation of deployment via K3S</li> |
| </ul> |
| |
| <p>Please have a look at the <a href="release_notes_200.html">2.0.0 release notes</a> for a more detailed information on the release.</p> |
| |
| <h2 id="artifacts">Artifacts</h2> |
| |
| <p>The new Java artifacts have been published at the <a href="https://repo.eclipse.org/content/repositories/ditto/">Eclipse Maven repository</a> |
| as well as <a href="https://repo1.maven.org/maven2/org/eclipse/ditto/">Maven central</a>.</p> |
| |
| <p>The Ditto JavaScript client release was published on <a href="https://www.npmjs.com/~eclipse_ditto">npmjs.com</a>:</p> |
| <ul> |
| <li><a href="https://www.npmjs.com/package/@eclipse-ditto/ditto-javascript-client-dom">@eclipse-ditto/ditto-javascript-client-dom</a></li> |
| <li><a href="https://www.npmjs.com/package/@eclipse-ditto/ditto-javascript-client-node">@eclipse-ditto/ditto-javascript-client-node</a></li> |
| </ul> |
| |
| <p>The Docker images have been pushed to Docker Hub:</p> |
| <ul> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-policies/">eclipse/ditto-policies</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things/">eclipse/ditto-things</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things-search/">eclipse/ditto-things-search</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-gateway/">eclipse/ditto-gateway</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-connectivity/">eclipse/ditto-connectivity</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-concierge/">eclipse/ditto-concierge</a></li> |
| </ul> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Thu, 06 May 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-05-06-release-announcement-200.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-05-06-release-announcement-200.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Support SSH tunneling for managed connections</title> |
| <description><p>With the upcoming release of Eclipse Ditto <strong>version 2.0.0</strong> it will be possible to</p> |
| |
| <h1 id="ssh-tunneling-for-managed-connections">SSH tunneling for managed connections</h1> |
| <p>With the upcoming release of Eclipse Ditto version 2.0.0, managed connections support establishing |
| an SSH tunnel, which is then used to connect to the actual target endpoint. This is useful when the target endpoint is |
| not directly accessible. Currently, this feature is available for all connection types supported in Ditto, except for |
| Kafka 2.x.</p> |
| |
| <p><img src="images/blog/2021-04-06-ssh-tunnel-for-managed-connections-shema.png" alt="Connection Overview" /></p> |
| |
| <p>For further information, see <a href="https://tools.ietf.org/html/rfc4254#section-7">Secure Shell (SSH) Connection Protocol, RFC4254</a></p> |
| |
| <h2 id="setting-up-connections-with-ssh-tunneling-in-ditto">Setting up connections with SSH tunneling in Ditto</h2> |
| <p>When setting up a tunneled connection, the configuration must specify the <code class="highlighter-rouge">sshTunnel</code> section, which contains |
| the necessary information to establish the SSH port forwarding. For authentication, password and public |
| key are supported. Also, host validation using public key fingerprints are supported. The tunnel configuration does not |
| affect the other parts of your connection configuration. If the feature is enabled the connection will establish an SSH |
| tunnel and afterwards use this tunnel to connect to the desired endpoint. In case you later disable the SSH tunnel |
| feature, the payload will be processed directly to the desired endpoint.</p> |
| |
| <h3 id="basic-authentication">Basic Authentication</h3> |
| <p>When using basic authenticating the <code class="highlighter-rouge">sshTunnel</code> configuration should contain the <code class="highlighter-rouge">credentials.type</code> <code class="highlighter-rouge">plain</code>, as |
| well as the <code class="highlighter-rouge">username</code> and <code class="highlighter-rouge">password</code> fields:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tunneled-connection"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"mqtt"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tcp://mqtt.eclipseprojects.io:1883"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sources"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="err">...</span><span class="w"> </span><span class="p">}],</span><span class="w"> |
| </span><span class="s2">"sshTunnel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ssh://ssh-host:2222"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"credentials"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"plain"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"username"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"password"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"validateHost"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"knownHosts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"MD5:e0:3a:34:1c:68:ed:c6:bc:7c:ca:a8:67:c7:45:2b:19"</span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h3 id="authentication-with-public-key">Authentication with public key</h3> |
| <p>On public key authentication the <code class="highlighter-rouge">credentials.type</code> is <code class="highlighter-rouge">public-key</code>. In addition to the <code class="highlighter-rouge">username</code> the <code class="highlighter-rouge">publicKey</code> and |
| <code class="highlighter-rouge">privateKey</code> have to be provided. The public key must be provided as PEM-encoded key in <code class="highlighter-rouge">X.509</code> format. The private |
| key must be provided as PEM-encoded key in unencrypted <code class="highlighter-rouge">PKCS8</code> format as specified by <a href="https://tools.ietf.org/html/rfc7468">RFC-7468</a>.</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tunneled-connection"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"mqtt"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tcp://mqtt.eclipseprojects.io:1883"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sources"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="err">...</span><span class="w"> </span><span class="p">}],</span><span class="w"> |
| </span><span class="s2">"sshTunnel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ssh://ssh-host:2222"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"credentials"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"public-key"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"username"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"publicKey"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-----BEGIN PUBLIC KEY-----</span><span class="se">\n</span><span class="s2">MIIBIjANBgkqhkiG9.....</span><span class="se">\n</span><span class="s2">-----END PUBLIC KEY-----"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"privateKey"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-----BEGIN PRIVATE KEY-----</span><span class="se">\n</span><span class="s2">MIIEvAIBADANBgkqhki....</span><span class="se">\n</span><span class="s2">-----END PRIVATE KEY-----"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"validateHost"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"knownHosts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"MD5:e0:3a:34:1c:68:ed:c6:bc:7c:ca:a8:67:c7:45:2b:19"</span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| <p>The following command can be used to convert a standard OpenSSL key in PKCS1 format to the PKCS8 format accepted by Ditto:</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl pkcs8 -topk8 -nocrypt -in client-private.pem.key -out client-private.pem.pk8 |
| </code></pre></div></div> |
| |
| <h3 id="host-validation-using-public-key-fingerprints">Host validation using public key fingerprints</h3> |
| <p>When <code class="highlighter-rouge">validateHost</code> is enabled, the host public key fingerprints are validated. They can be provided |
| in the format the standard command line tool <code class="highlighter-rouge">ssh-keygen</code> produces them. The fingerprints are |
| prefixed with an alias of the hash algorithm that was used to calculate the fingerprint. Ditto supports the following |
| hash algorithms for public key fingerprints: <code class="highlighter-rouge">MD5</code>, <code class="highlighter-rouge">SHA1</code>, <code class="highlighter-rouge">SHA224</code>, <code class="highlighter-rouge">SHA256</code>, <code class="highlighter-rouge">SHA384</code> and <code class="highlighter-rouge">SHA512</code>. To generate a |
| valid fingerprint with an <code class="highlighter-rouge">MD5</code> hash algorithm from the public key, following can be used:</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen -lf id_rsa.pub -E md5 |
| </code></pre></div></div> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions regarding this new functionality.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Wed, 31 Mar 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-03-31-ssh-tunneling-for-managed-connections.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-03-31-ssh-tunneling-for-managed-connections.html</guid> |
| |
| <category>blog</category> |
| |
| <category>architecture</category> |
| |
| <category>connectivity</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Use Eclipse Ditto with Azure IoT Hub as message broker</title> |
| <description><p>This blogpost is based upon Eclipse Ditto Version <strong>1.5.0</strong>, the Azure IoT Suite as of |
| <strong>2021-03-19</strong> and the azure-iot-device-client version <strong>1.29.2</strong>.</p> |
| |
| <h1 id="connecting-devices-to-eclipse-ditto-via-azure-iot-hub">Connecting devices to Eclipse Ditto via Azure IoT Hub</h1> |
| <p>This blog post elaborates on connecting and managing devices in Eclipse Ditto by using the Azure IoT Hub |
| as a message broker.</p> |
| |
| <p><img src="images/blog/2021-03-22-azure-iot-hub-integration-overview.png" alt="Connection Overview" /></p> |
| |
| <p>The basic functionality that can be used at the time of creating this blogpost are:</p> |
| |
| <ul> |
| <li>[D2C] Sending telemetry data from the device to update its Ditto digital-twin representation.</li> |
| <li>[D2C] Same ID enforcement based on the Azure IoT Hub device-id to prevent spoofing other digital-twins.</li> |
| <li>[C2D] Sending live-messages to the device.</li> |
| <li>[D2C] Sending feedback to live messages to the service.</li> |
| </ul> |
| |
| <h2 id="setting-up-connections-in-ditto">Setting up connections in Ditto</h2> |
| <p>The features described above will work with an “out-of-the-box” Azure IoT Hub subscription, |
| so no additional configuration is needed in the IoT Hub. In order to connect Ditto to the IoT Hub you have to set up |
| two AMQP 1.0 connections. One for receiving telemetry data, the other for sending live-messages and receiving |
| live-message feedback.</p> |
| |
| <h3 id="telemetry-connection">Telemetry Connection</h3> |
| |
| <p>This connection subscribes to telemetry messages, published by the Azure IoT Hub built-in “Event Hub like” endpoint.</p> |
| |
| <p>Adding an enforcement for the <code class="highlighter-rouge">{{ thing:id }}</code> based on the <code class="highlighter-rouge">{{ header:iothub-connection-device-id }}</code> prevents |
| applying a digital-twin update to the twin of another device (Device Spoofing).</p> |
| |
| <p>To establish this connection the placeholders below have to be substituted by:</p> |
| |
| <ul> |
| <li> |
| <p><code class="highlighter-rouge">{{userName}}</code>: The <code class="highlighter-rouge">SharedAccessKeyName</code> in your Event Hub-compatible endpoint (i.e. service).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{password}}</code>: The <code class="highlighter-rouge">SharedAccessKey</code> in your Event Hub-compatible endpoint.</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{endpoint}}</code>: The <code class="highlighter-rouge">Endpoint</code> in your Event Hub-compatible endpoint (Cut leading “sb://” and trailing slash, |
| e.g.. ihsuprodblres055dednamespace.servicebus.windows.net).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{entityPath}}</code>: The <code class="highlighter-rouge">EntitiyPath</code> in your Event Hub-compatible endpoint (e.g.. hubname-8584619-2e72252706).</p> |
| </li> |
| </ul> |
| |
| <p><em>Note: You can use the “service” IoT Hub policy instead of the “iothubowner” policy, since this is more restricitve, |
| and represents the actual use of Ditto as a northbound service.</em></p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"azure-example-connection-telemetry"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqp-10"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"failoverEnabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqps://{{userName}}:{{password}}@{{endpoint}}:5671"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"source"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"addresses"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"{{entityPath}}/ConsumerGroups/$Default/Partitions/0"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"{{entityPath}}/ConsumerGroups/$Default/Partitions/1"</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"authorizationContext"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"ditto"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"enforcement"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"input"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{ header:iothub-connection-device-id }}"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"{{ thing:id }}"</span><span class="w"> |
| </span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| <ul> |
| <li> |
| <p><a href="https://www.eclipse.org/ditto/connectivity-manage-connections.html">Further information on creating connections</a></p> |
| </li> |
| <li> |
| <p><a href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messages-d2c">Further information on D2C messaging capabilities of Azure IoT Hub</a></p> |
| </li> |
| <li> |
| <p><a href="https://docs.microsoft.com/de-de/azure/iot-hub/iot-hub-devguide-messages-read-builtin">Further information on the built-in “event-hub like” endpoint of Azure IoT Hub</a></p> |
| </li> |
| </ul> |
| |
| <h3 id="message-connection">Message connection</h3> |
| |
| <p>This connection enables forwarding live messages to the Azure IoT Hub (which forwards it to the device) |
| and receiving feedback to these live-messages from the device.</p> |
| |
| <p>Adding the header-mapping <code class="highlighter-rouge">"message_id": "{{header:correlation-id}}"</code> enables Azure IoT Hub to correlate messages. |
| Adding the header-mapping <code class="highlighter-rouge">"to": "/devices/{{ header:ditto-message-thing-id }}/messages/deviceInbound"</code> is |
| necessary for correct message routing by Azure IoT Hub. The header <code class="highlighter-rouge">ditto-message-thing-id</code> will be set as a |
| default header by Ditto.</p> |
| |
| <p>To establish this connection the placeholders below have to be substituted:</p> |
| |
| <ul> |
| <li> |
| <p><code class="highlighter-rouge">{{userName}}</code>: The name of the chosen IoT Hub policy + “@sas.root.” + the name of your IoT Hub |
| (i.e. service@sas.root.my-hub).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{hostName}}</code>: The Hostname of your IoT Hub (i.e. my-hub.azure-devices.net).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{encodedSasToken}}</code>: An URL encoded SAS token. Information on how to generate a token can be found at |
| <a href="https://docs.microsoft.com/en-us/cli/azure/ext/azure-iot/iot/hub?view=azure-cli-latest#ext_azure_iot_az_iot_hub_generate_sas_token">az iot hub generate-sas-token.</a> |
| The generated token has to be additionally URL encoded (browser console -&gt; <code class="highlighter-rouge">encodeURI('{{generatedToken}}')</code>).</p> |
| </li> |
| </ul> |
| |
| <p><em>Note: The generated SAS token has a maximum TTL of 365 days, so the token has to be changed to a newly generated before expiry. |
| Otherwise, the connection tries to reconnect or closes automatically, when <code class="highlighter-rouge">failoverEnabled</code> is set to <code class="highlighter-rouge">false</code>.</em></p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"azure-example-connection-messages"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqp-10"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"failoverEnabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqps://{{userName}}:{{encodedSasToken}}@{{hostName}}:5671"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"target"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="p">{</span><span class="s2">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/messages/devicebound"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"topics"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"_/_/things/live/messages"</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"authorizationContext"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"ditto"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"headerMapping"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"message_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{header:correlation-id}}"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"to"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/devices/{{ header:ditto-message-thing-id }}/messages/deviceInbound"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p><strong>The java azure-iot-device-client currently can not be used to receive messages with JSON as body. |
| Thus, the messages’ payload has to be byte-encoded.</strong></p> |
| |
| <p>This can be achieved by configuring an outgoing JavaScript payload mapper in the message connection:</p> |
| <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">mapFromDittoProtocolMsg</span><span class="p">(</span> |
| <span class="nx">namespace</span><span class="p">,</span> |
| <span class="nx">name</span><span class="p">,</span> |
| <span class="nx">group</span><span class="p">,</span> |
| <span class="nx">channel</span><span class="p">,</span> |
| <span class="nx">criterion</span><span class="p">,</span> |
| <span class="nx">action</span><span class="p">,</span> |
| <span class="nx">path</span><span class="p">,</span> |
| <span class="nx">dittoHeaders</span><span class="p">,</span> |
| <span class="nx">value</span><span class="p">,</span> |
| <span class="nx">status</span><span class="p">,</span> |
| <span class="nx">extra</span> |
| <span class="p">)</span> <span class="p">{</span> |
| |
| <span class="kd">let</span> <span class="nx">headers</span> <span class="o">=</span> <span class="nx">dittoHeaders</span><span class="p">;</span> |
| <span class="kd">let</span> <span class="nx">textPayload</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> |
| <span class="kd">let</span> <span class="nx">bytePayload</span> <span class="o">=</span> <span class="nx">Ditto</span><span class="p">.</span><span class="nx">stringToArrayBuffer</span><span class="p">(</span><span class="nx">Ditto</span><span class="p">.</span><span class="nx">buildDittoProtocolMsg</span><span class="p">(</span><span class="nx">namespace</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">group</span><span class="p">,</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">criterion</span><span class="p">,</span> <span class="nx">action</span><span class="p">,</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">dittoHeaders</span><span class="p">,</span> <span class="nx">value</span><span class="p">).</span><span class="nx">toString</span><span class="p">());</span> |
| <span class="kd">let</span> <span class="nx">contentType</span> <span class="o">=</span> <span class="s1">'application/octet-stream'</span><span class="p">;</span> |
| |
| <span class="k">return</span> <span class="nx">Ditto</span><span class="p">.</span><span class="nx">buildExternalMsg</span><span class="p">(</span> |
| <span class="nx">headers</span><span class="p">,</span> |
| <span class="nx">textPayload</span><span class="p">,</span> |
| <span class="nx">bytePayload</span><span class="p">,</span> |
| <span class="nx">contentType</span> |
| <span class="p">);</span> |
| <span class="p">}</span> |
| </code></pre></div></div> |
| |
| <ul> |
| <li> |
| <p><a href="https://www.eclipse.org/ditto/connectivity-manage-connections.html">Further information on creating connections</a></p> |
| </li> |
| <li> |
| <p><a href="https://www.eclipse.org/ditto/connectivity-mapping.html">Further information on payload-mapping</a></p> |
| </li> |
| <li> |
| <p><a href="https://docs.microsoft.com/de-de/azure/iot-hub/iot-hub-devguide-messages-c2d">Further information on C2D messaging capabilities of Azure IoT Hub</a></p> |
| </li> |
| </ul> |
| |
| <h2 id="possible-improvements">Possible improvements</h2> |
| |
| <p>Some features of Ditto could be used in combination with Azure IoT Hub with some adjustments. These include:</p> |
| |
| <ul> |
| <li>Using the <code class="highlighter-rouge">ImplicitThingCreationMapper</code> to implicitly create a new thing when a new device is registered in Azure IoT Hub.</li> |
| <li>Using the ConnectionStatusMapper to update the ConnectionStatus of things, when their devices disconnect from Azure IoT Hub.</li> |
| <li>[C2D] Directly invoke methods on the device (Direct Method Invocation).</li> |
| </ul> |
| |
| <h3 id="using-the-implicitthingcreation-and-connectionstatus-features-based-on-azure-iot-hub-events">Using the ImplicitThingCreation and ConnectionStatus features based on Azure IoT Hub events</h3> |
| |
| <p>Azure IoT Hub has the possibility to publish events for status changes of device connections and the creation/removal of new devices. |
| These events are published via an Azure EventGrid to another chosen Azure application. |
| By publishing these events to an Azure Event Hub, a Ditto AMQP connection can subscribe for them.</p> |
| |
| <p>The payload-mappers for <code class="highlighter-rouge">ImplicitThingCreation</code> and <code class="highlighter-rouge">ConnectionStatus</code> could be adjusted to handle such event messages and |
| create new things or update the <code class="highlighter-rouge">ConnectionStatus</code> feature depending on the received messages.</p> |
| |
| <ul> |
| <li><a href="https://docs.microsoft.com/de-de/azure/event-grid/event-schema-iot-hub?tabs=event-grid-event-schema">Further information on the events published by Azure IoT Hub</a></li> |
| </ul> |
| |
| <h3 id="using-direct-method-invocation">Using Direct Method Invocation</h3> |
| |
| <p>Azure IoT Hub provides an endpoint for directly invoking methods on a device. This can be compared to live-commands. |
| Direct Method Invocation can only be done via HTTP. For authentication SAS has to be used. |
| This authentication mechanism, however, is not yet implemented for HTTP Push of Eclipse Ditto |
| connections.</p> |
| |
| <ul> |
| <li><a href="https://docs.microsoft.com/de-de/azure/iot-hub/iot-hub-devguide-direct-methods">Further information on direct method invocations</a></li> |
| </ul> |
| |
| <h3 id="implementing-an-automatic-refresh-mechanism-for-sasl-tokens">Implementing an automatic refresh mechanism for SASL tokens</h3> |
| |
| <p>The <code class="highlighter-rouge">connectionString</code> provided by an Azure IoT Hub device’s policy could be used to generate and refresh a SASL token. |
| This would require a new connection setting, which could store such a <code class="highlighter-rouge">connectionString</code>, and an algorithm, which can |
| generate a SAS token out of that string.</p> |
| |
| <h2 id="getting-started">Getting started</h2> |
| |
| <p>To get started using Azure IoT Hub as a message broker for Eclipse Ditto, the |
| <a href="https://github.com/eclipse/ditto-examples/tree/master/azure/azure-iot-hub-device-simulator">Azure IoT Hub Device Simulator Example</a> |
| is a good entry point.</p> |
| </description> |
| <pubDate>Mon, 22 Mar 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-03-22-azure-iot-hub-integration.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-03-22-azure-iot-hub-integration.html</guid> |
| |
| <category>blog</category> |
| |
| <category>architecture</category> |
| |
| <category>connectivity</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Support merge functionality for things resources</title> |
| <description><p>With the upcoming release of Eclipse Ditto <strong>version 2.0.0</strong> it will be possible to merge existing things and their subresources.</p> |
| |
| <h2 id="merge-functionality-for-things-resources">Merge functionality for things resources</h2> |
| <p>Ditto now supports merging of existing things and all of its subresources with the provided payload in the request. |
| This can be done by using the HTTP API with the PATCH method, via the Ditto protocol, and also by using the Ditto Java Client. |
| For all three ways there is an example provided in this blog post.</p> |
| |
| <p>In contrast to the already existing PUT resource, this new functionality <strong>allows partial updates</strong> on a thing and all its subresources. |
| To get more into detail, from now on it is possible to add or update attributes, and a feature property at the same time, |
| without overwriting the complete thing. Another use case might be to update several feature properties within a single request |
| and let all other parts of the thing untouched.</p> |
| |
| <p>Ditto uses the <a href="https://tools.ietf.org/html/rfc7396">JSON Merge Patch</a> semantics to merge the request body |
| with the existing thing. In short, a JSON merge patch resembles the original JSON structure of a thing, and |
| the fields provided in the patch are added, updated, or deleted in the existing thing.</p> |
| |
| <p>Please be aware that <code class="highlighter-rouge">null</code> values have a special meaning when applying a merge patch. A <code class="highlighter-rouge">null</code> value indicates |
| the removal of existing fields in the updated thing. |
| For more details and examples, please refer to <a href="https://tools.ietf.org/html/rfc7396">RFC-7396</a>.</p> |
| |
| <h3 id="permissions-to-merge-things-and-things-subresources">Permissions to merge things and things subresources</h3> |
| <p>In order to execute such a merge operation, the authorized subject needs to have WRITE permission at all resources |
| that should change by the merge. Consequently, if the permission is missing for some part of the merge, |
| the merge is rejected and <strong>not</strong> applied at all.</p> |
| |
| <h2 id="examples">Examples</h2> |
| |
| <p>To demonstrate the new merge feature, we assume that the following thing already exists:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thingId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer-policy"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ACME demo corp."</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Berlin, main floor"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"serialno"</span><span class="p">:</span><span class="w"> </span><span class="s2">"42"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"model"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Speaking coffee machine"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"brewingTemp"</span><span class="p">:</span><span class="w"> </span><span class="mi">87</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">44</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timeoutSeconds"</span><span class="p">:</span><span class="w"> </span><span class="mi">6000</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"waterAmount"</span><span class="p">:</span><span class="w"> </span><span class="mi">731</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"temperature"</span><span class="p">:</span><span class="w"> </span><span class="mi">44</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h3 id="permissions-to-execute-the-example">Permissions to execute the example</h3> |
| <p>For this example, the authorized subject needs to have unrestricted WRITE permissions on all affected paths |
| of the JSON merge patch: <em>attributes/manufacturingYear</em>, <em>features/water-tank/properties/configuration/smartMode</em>, and |
| <em>features/water-tank/properties/configuration/tempToHold</em>. |
| The WRITE permission must not be revoked on any level further down the hierarchy. |
| Consequently, it is also sufficient for the authorized subject to have unrestricted WRITE permission at root level or |
| unrestricted WRITE permission at /attributes and /features etc.</p> |
| |
| <p>The following subparts will show how to use the merge feature via the HTTP API, the Ditto protocol |
| and the Ditto Java Client.</p> |
| |
| <h3 id="merge-via-http-api">Merge via HTTP API</h3> |
| <p>An existing thing can be merged via the HTTP API using the <em>PATCH</em> method with the following request body. |
| Notice that this request will add the “manufacturingYear” to the attributes, update the “tempToHold” to 50 and |
| delete the “smartMode” key from the feature property “water-tank”.</p> |
| |
| <p>The <code class="highlighter-rouge">Content-Type</code> header for this request must be <em>application/merge-patch+json</em>.</p> |
| |
| <p>PATCH /things/com.acme:coffeebrewer</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturingYear"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>After the request was successfully performed the thing will look like this:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thingId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer-policy"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ACME demo corp."</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"manufacturingYear"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Berlin, main floor"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"serialno"</span><span class="p">:</span><span class="w"> </span><span class="s2">"42"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"model"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Speaking coffee machine"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewingTemp"</span><span class="p">:</span><span class="w"> </span><span class="mi">87</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timeoutSeconds"</span><span class="p">:</span><span class="w"> </span><span class="mi">6000</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"waterAmount"</span><span class="p">:</span><span class="w"> </span><span class="mi">731</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"temperature"</span><span class="p">:</span><span class="w"> </span><span class="mi">44</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>It is also possible to apply the <em>PATCH</em> method to all subresources of a thing, e.g. merging only the attributes of a thing. <br /> |
| Check out the newly added <em>PATCH</em> resources in our <a href="http-api-doc.html">HTTP API</a>.</p> |
| |
| <h3 id="merge-via-ditto-protocol">Merge via Ditto protocol</h3> |
| <p>It is also possible to merge the existing thing via the Ditto protocol. |
| Applying the following Ditto merge command to the existing thing will lead to the same result as in the above HTTP example.</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/coffeebrewer/things/twin/commands/merge"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/merge-patch+json"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thingId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturingYear"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>Another Ditto protocol example to merge a feature property:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/coffeebrewer/things/twin/commands/merge"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/merge-patch+json"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features/coffee-brewer/properties/brewed-coffees"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h3 id="using-the-ditto-client-to-merge-things">Using the ditto-client to merge things</h3> |
| <p>The merge functionality is also supported via the <a href="client-sdk-java.html">Ditto Java Client</a> |
| with the upcoming (<strong>Ditto Java Client version 2.0.0</strong>).</p> |
| |
| <p>Example for merging a thing with the Ditto Java Client:</p> |
| |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="n">String</span> <span class="n">THING_ID</span> <span class="o">=</span> <span class="s">"com.acme:coffeebrewer"</span><span class="o">;</span> |
| <span class="kd">final</span> <span class="n">String</span> <span class="n">FEATURE_ID</span> <span class="o">=</span> <span class="s">"water-tank"</span><span class="o">;</span> |
| <span class="kd">final</span> <span class="n">JsonPointer</span> <span class="n">ATTRIBUTE_KEY</span> <span class="o">=</span> <span class="n">JsonFactory</span><span class="o">.</span><span class="na">newPointer</span><span class="o">(</span><span class="s">"manufacturingYear"</span><span class="o">);</span> |
| <span class="kd">final</span> <span class="n">String</span> <span class="n">ATTRIBUTE_VALUE</span> <span class="o">=</span> <span class="s">"2020"</span><span class="o">;</span> |
| <span class="kd">final</span> <span class="n">Feature</span> <span class="n">FEATURE</span> <span class="o">=</span> <span class="n">ThingsModelFactory</span><span class="o">.</span><span class="na">newFeatureBuilder</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">withId</span><span class="o">(</span><span class="n">FEATURE_ID</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">properties</span><span class="o">(</span><span class="n">ThingsModelFactory</span><span class="o">.</span><span class="na">newFeaturePropertiesBuilder</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="s">"smartMode"</span><span class="o">,</span> <span class="kc">false</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="s">"tempToHold"</span><span class="o">,</span> <span class="mi">50</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">();</span> |
| |
| <span class="kd">final</span> <span class="n">Thing</span> <span class="n">THING</span> <span class="o">=</span> <span class="n">ThingsModelFactory</span><span class="o">.</span><span class="na">newThingBuilder</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">setId</span><span class="o">(</span><span class="n">THING_ID</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">setAttribute</span><span class="o">(</span><span class="n">ATTRIBUTE_KEY_NEW</span><span class="o">,</span> <span class="n">JsonFactory</span><span class="o">.</span><span class="na">newValue</span><span class="o">(</span><span class="n">ATTRIBUTE_VALUE</span><span class="o">))</span> |
| <span class="o">.</span><span class="na">setFeature</span><span class="o">(</span><span class="n">FEATURE</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">();</span> |
| |
| <span class="c1">// initialize the ditto-client</span> |
| <span class="kd">final</span> <span class="n">DittoClient</span> <span class="n">dittoClient</span> <span class="o">=</span> <span class="o">...</span> <span class="o">;</span> |
| |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">().</span><span class="na">merge</span><span class="o">(</span><span class="n">THING_ID</span><span class="o">,</span> <span class="n">THING</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(((</span><span class="n">adaptable</span><span class="o">,</span> <span class="n">throwable</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> |
| <span class="k">if</span> <span class="o">(</span><span class="n">throwable</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"Received error while sending MergeThing: '{}' "</span><span class="o">,</span> <span class="n">throwable</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> |
| <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Received response for MergeThing: '{}'"</span><span class="o">,</span> <span class="n">adaptable</span><span class="o">);</span> |
| <span class="o">}</span> |
| <span class="o">}));</span> |
| </code></pre></div></div> |
| |
| <p>After running this code snippet, the existing thing should look like the above result for the HTTP example.</p> |
| |
| <p>More examples for merging an attribute, all attributes and a feature property via Ditto Java Client.</p> |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// initialize the ditto-client</span> |
| <span class="kd">final</span> <span class="n">DittoClient</span> <span class="n">dittoClient</span> <span class="o">=</span> <span class="o">...</span> <span class="o">;</span> |
| |
| <span class="c1">// merge attribute</span> |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">forId</span><span class="o">(</span><span class="s">"com.acme:coffeebrewer"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">mergeAttribute</span><span class="o">(</span><span class="s">"manufacturingYear"</span><span class="o">,</span> <span class="s">"2021"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(...);</span> |
| |
| <span class="c1">// merge attributes</span> |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">forId</span><span class="o">(</span><span class="s">"com.acme:coffeebrewer"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">mergeAttributes</span><span class="o">(</span><span class="n">JsonObject</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">set</span><span class="o">(</span><span class="s">"manufacturingYear"</span><span class="o">,</span> <span class="s">"2021"</span><span class="o">).</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(...);</span> |
| |
| <span class="c1">// merge feature property</span> |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">forFeature</span><span class="o">(</span><span class="s">"com.acme:coffeebrewer"</span><span class="o">,</span> <span class="s">"water-tank"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">mergeProperty</span><span class="o">(</span><span class="s">"configuration/smartMode"</span><span class="o">,</span> <span class="kc">false</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(...);</span> |
| </code></pre></div></div> |
| |
| <h2 id="merge-events">Merge events</h2> |
| <p>In this section we want to cover the new <code class="highlighter-rouge">ThingMerged</code> event which will be emitted after successfully applying an <code class="highlighter-rouge">MergeThing</code> command. |
| For every HTTP request or Ditto protocol message which performs a merge operation on a thing there will be sent out |
| exactly one <code class="highlighter-rouge">ThingMerged</code> event. This event contains the <strong>path</strong> and the <strong>value</strong> of the merge operation. |
| The <strong>path</strong> describes on which level of the thing the <strong>value</strong> was merged.</p> |
| |
| <h3 id="merge-event-example">Merge event example</h3> |
| <p>Let’s assume we want to patch/merge multiple feature properties at once. |
| PATCH /things/com.acme:coffeebrewer/features</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>The following <code class="highlighter-rouge">ThingMerged</code> event is emitted:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/coffeebrewer/things/twin/events/merged"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/merge-patch+json"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"revision"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-02-04T09:42:39Z"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new functionality.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| |
| </description> |
| <pubDate>Thu, 04 Feb 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-02-04-merge-feature.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-02-04-merge-feature.html</guid> |
| |
| <category>blog</category> |
| |
| <category>http</category> |
| |
| <category>protocol</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Policy actions: token based subject activation</title> |
| <description><p>The upcoming version of Eclipse Ditto <strong>2.0.0</strong> will be enhanced with the ability to |
| <a href="basic-policy.html#actions">alter policies based on policy actions</a>.</p> |
| |
| <h2 id="policy-actions">Policy actions</h2> |
| |
| <p>This new concept of <a href="basic-policy.html#actions">Policy actions</a> allows upfront defined modifications to policies without |
| the need for the one invoking the action to have “WRITE” permissions granted on the policy.</p> |
| |
| <h2 id="token-based-activation-of-subject">Token based activation of subject</h2> |
| |
| <p>Together with the concept of actions, a first action named |
| <a href="basic-policy.html#action-activatetokenintegration"><code class="highlighter-rouge">activateTokenIntegration</code></a> is added.<br /> |
| This action</p> |
| <ul> |
| <li>only works when using <a href="#" data-toggle="tooltip" data-original-title="JSON Web Token (JWT)">JWT</a> |
| based authentication issued by Google or other OpenID Connect providers as |
| <a href="installation-operating.html#openid-connect">documented in the installation/operation guide</a></li> |
| <li>checks whether the <a href="basic-auth.html#authenticated-subjects">authenticated subjects</a> which invoked the action have the |
| permission to <code class="highlighter-rouge">EXECUTE</code> the action on a policy entry</li> |
| <li>checks whether the <a href="basic-auth.html#authenticated-subjects">authenticated subjects</a> which invoked the action have at |
| least some kind of <code class="highlighter-rouge">READ</code> permission to any <code class="highlighter-rouge">thing:/</code> resource in a policy entry</li> |
| </ul> |
| |
| <p>When all the conditions were met for a policy entry, the action will inject a new <a href="basic-policy.html#subjects">subject</a> |
| into the matched policy entry which by default (the |
| <a href="basic-policy.html#action-activatetokenintegration">pattern is configurable</a>) is the following. |
| This syntax uses <a href="basic-placeholders.html">placeholders</a> in order to extract information from the authenticated JWT and |
| the policy entry:</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| integration:{{policy-entry:label}}:{{jwt:aud}} |
| |
| </code></pre></div></div> |
| |
| <p>The value of the injected subject will contain the <a href="basic-policy.html#expiring-policy-subjects">expiry</a> timestamp |
| copied from the JWT <code class="highlighter-rouge">"exp"</code> (the expiration time of the token) claim.</p> |
| |
| <h2 id="example-use-case">Example use case</h2> |
| |
| <p>Assuming that you have configured a custom OpenID Connect provider <code class="highlighter-rouge">some-openid-connect-provider</code> as |
| <a href="installation-operating.html#openid-connect">documented in the installation/operation guide</a>:</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ditto.gateway.authentication { |
| oauth { |
| openid-connect-issuers = { |
| some-openid-connect-provider = "https://some-openid-connect-provider.com" |
| } |
| } |
| } |
| </code></pre></div></div> |
| |
| <p>Let’s describe our scenario:</p> |
| <ul> |
| <li>It is required to enable that a Ditto <a href="basic-connections.html">connection</a> (e.g. an |
| <a href="connectivity-protocol-bindings-http.html">HTTP connection</a> invoking an HTTP webhook) shall receive events whenever |
| the temperature of a twin is modified</li> |
| <li>For security reasons however, the webhook shall not receive events longer than the expiration time of the JWT which |
| was used in order to activate the webhook</li> |
| <li>The webhook can be extended by invoking the action again before the “expiry” time was reached</li> |
| </ul> |
| |
| <p>The underlying <a href="basic-policy.html">policy</a> shall be the following one:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my.namespace:policy-a"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"entries"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"owner"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"subjects"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"some-openid-connect-provider:some-admin-id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"authenticated via OpenID connect provider &lt;some-openid-connect-provider&gt;"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"resources"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thing:/"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">,</span><span class="w"> </span><span class="s2">"WRITE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"policy:/"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">,</span><span class="w"> </span><span class="s2">"WRITE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"temperature-observer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"subjects"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"some-openid-connect-provider:some-user-id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"authenticated via OpenID connect provider &lt;some-openid-connect-provider&gt;"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"resources"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thing:/features/temperature"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"policy:/entries/temperature-observer/actions/activateTokenIntegration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"EXECUTE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>The policy entry <code class="highlighter-rouge">"temperature-observer"</code> above describes that:</p> |
| <ul> |
| <li>the user “some-user-id” may <code class="highlighter-rouge">READ</code> the <code class="highlighter-rouge">"temperature"</code> feature of things using this policy</li> |
| <li>is allowed to <code class="highlighter-rouge">EXECUTE</code> the <code class="highlighter-rouge">activateTokenIntegration</code> action in order to inject a subject derived from his provided |
| JWT</li> |
| </ul> |
| |
| <p>Let’s assume that the authenticated JWT used for executing the action contained the following claims:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"iss"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://some-openid-connect-provider.com"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sub"</span><span class="p">:</span><span class="w"> </span><span class="s2">"some-user-id"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"exp"</span><span class="p">:</span><span class="w"> </span><span class="mi">1622802633</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"aud"</span><span class="p">:</span><span class="w"> </span><span class="s2">"some-specific-audience-0815"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>The “exp” field contains the token expiry timestamp (seconds since epoch) and resolves to: |
| <code class="highlighter-rouge">Friday, June 4, 2021 10:30:33 AM</code>.</p> |
| |
| <p>Once the HTTP API |
| <a href="/http-api-doc.html#/Policies/post_policies__policyId__entries__label__actions_activateTokenIntegration">POST /api/2/policies/{policyId}/entries/{label}/actions/activateTokenIntegration</a>, with <code class="highlighter-rouge">policyId=my.namespace:policy-a</code> and <code class="highlighter-rouge">label=temperature-observer</code>,<br /> |
| is invoked (without any payload), a new subject will be injected when the |
| <a href="basic-policy.html#action-activatetokenintegration">described prerequisites</a> were enforced successfully.</p> |
| |
| <p>As a simplification, all possible policy entries may be injected with the subject by invoking the top level action<br /> |
| <a href="/http-api-doc.html#/Policies/post_policies__policyId__actions_activateTokenIntegration">POST /api/2/policies/{policyId}/actions/activateTokenIntegration</a>, with <code class="highlighter-rouge">policyId=my.namespace:policy-a</code>.</p> |
| |
| <p>The value of the injected subject will contain the expiration timestamp from the JWT, so the injected policy subject |
| <code class="highlighter-rouge">integration:temperature-observer:some-specific-audience-0815</code> will result in a modified policy:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my.namespace:policy-a"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"entries"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"owner"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">unchanged</span><span class="w"> </span><span class="err">...</span><span class="w"> </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"temperature-observer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"subjects"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"some-openid-connect-provider:some-user-id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"authenticated via OpenID connect provider &lt;some-openid-connect-provider&gt;"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"integration:temperature-observer:some-specific-audience-0815"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"added via action &lt;activateTokenIntegration&gt;"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"expiry"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-06-04T10:30:33Z"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"resources"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thing:/features/temperature"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"policy:/entries/temperature-observer/actions/activateTokenIntegration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"EXECUTE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>When we now have a |
| managed HTTP connection which <a href="basic-connections.html#authorization">configures the <code class="highlighter-rouge">authorizationContext</code></a> to include |
| the subject <code class="highlighter-rouge">integration:temperature-observer:some-specific-audience-0815</code> for a |
| <a href="basic-connections.html#targets">connection target</a>, this connection is allowed to publish changes to the temperature of |
| all things using the above policy until the <code class="highlighter-rouge">"expiry"</code> timestamp was reached.<br /> |
| Afterwards, publishing changes automatically stops, unless the action is invoked again with a JWT having a longer “exp” |
| time prolonging the injected policy subject.</p> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new token based subject activation |
| for policies.<br /> |
| Or do you have other use cases in mind you might be able to solve with this feature? Please let us know.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Fri, 22 Jan 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-01-22-policy-subject-activate-token-integration.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-01-22-policy-subject-activate-token-integration.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Announcing Eclipse Ditto Release 1.5.0</title> |
| <description><p>Wrapping up this crazy year, the Ditto team is happy to announce the next feature update of Ditto <code class="highlighter-rouge">1.x</code>: |
| <strong>Eclipse Ditto 1.5.0</strong></p> |
| |
| <p>1.5.0 focuses on:</p> |
| |
| <ul> |
| <li>Desired properties management (CRUD)</li> |
| <li>Addition of “cloudevents” HTTP endpoint</li> |
| <li>Ditto internal pub/sub supports using a “grouping” concept which improves Ditto’s scalability capabilities</li> |
| <li>Issuing “weak Acknowledgements” when a command requesting acks was filtered out by Ditto (improvement of “at least once” delivery scenarios)</li> |
| <li>Feature ID may be used in header mappings of connections</li> |
| </ul> |
| |
| <p>Please have a look at the <a href="release_notes_150.html">1.5.0 release notes</a> for a more detailed information on the release.</p> |
| |
| <h2 id="artifacts">Artifacts</h2> |
| |
| <p>The new Java artifacts have been published at the <a href="https://repo.eclipse.org/content/repositories/ditto/">Eclipse Maven repository</a> |
| as well as <a href="https://repo1.maven.org/maven2/org/eclipse/ditto/">Maven central</a>.</p> |
| |
| <p>Also the <a href="client-sdk-java.html">Ditto Java client</a>’s artifacts were published to Maven central.</p> |
| |
| <p>The Docker images have been pushed to Docker Hub:</p> |
| <ul> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-policies/">eclipse/ditto-policies</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things/">eclipse/ditto-things</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things-search/">eclipse/ditto-things-search</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-gateway/">eclipse/ditto-gateway</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-connectivity/">eclipse/ditto-connectivity</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-concierge/">eclipse/ditto-concierge</a></li> |
| </ul> |
| |
| <h2 id="kubernetes-ready-helm-chart">Kubernetes ready: Helm chart</h2> |
| |
| <p>In order to run Eclipse Ditto in a Kubernetes environment, best rely on the official |
| <a href="https://hub.helm.sh/charts/eclipse-iot/ditto">Helm chart</a> and deploy Ditto via the Helm package manager.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Thu, 10 Dec 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-12-10-release-announcement-150.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-12-10-release-announcement-150.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Weak acknowledgments to decouple signal publishers and subscribers</title> |
| <description><h2 id="motivation">Motivation</h2> |
| |
| <p><a href="2020-08-31-release-announcement-120.html">Ditto 1.2.0</a> introduced at-least-once delivery via |
| <a href="basic-acknowledgements.html">acknowledgement requests</a>.<br /> |
| It increased coupling between the publisher and the subscriber of signals in that the subscriber is no longer at the |
| liberty to filter for signals it is interested in. Instead, the subscriber must consume all signals in order to |
| fulfill acknowledgement requests and prevent endless redelivery.</p> |
| |
| <p>To combat the problem, |
| <a href="2020-10-28-release-announcement-140.html">Ditto 1.4.0</a> made acknowledgement labels unique and introduced the requirement |
| to manage <a href="basic-acknowledgements.html#issuing-acknowledgements"><em>declared acknowledgements</em></a>, identifying of each |
| subscriber.<br /> |
| It is now possible for Ditto to issue |
| <a href="basic-acknowledgements.html#weak-acknowledgements-wacks"><em>weak acknowledgements</em></a> on behalf of the subscriber |
| whenever it decides to not consume a signal. That allows subscribers to configure RQL and namespace filters freely |
| without causing any futile redelivery.</p> |
| |
| <div class="alert alert-info" role="alert"><i class="fa fa-info-circle"></i> <b>Note:</b> Weak acknowledgements are available since Ditto 1.5.0.</div> |
| |
| <h2 id="what-it-is">What it is</h2> |
| |
| <p>A <a href="basic-acknowledgements.html#weak-acknowledgements-wacks"><em>weak acknowledgement</em></a> is issued by Ditto for any |
| <a href="basic-acknowledgements.html#requesting-acks">acknowledgement request</a> that will not be fulfilled now or ever without |
| configuration change.<br /> |
| A weak acknowledgement is identified by the header <code class="highlighter-rouge">ditto-weak-ack: true</code>.</p> |
| |
| <p>The status code of weak acknowledgements is <code class="highlighter-rouge">200 OK</code>; it signifies that any redelivery is not to be made on their |
| account.</p> |
| |
| <p>A weak acknowledgement may look like this in Ditto protocol:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/xdk_53/things/twin/acks/my-mqtt-connection:my-mqtt-topic"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"ditto-weak-ack"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Acknowledgement was issued automatically, because the subscriber is not authorized to receive the signal."</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">200</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h2 id="how-it-works">How it works</h2> |
| |
| <p>Since Ditto 1.4.0, subscribers of <em>twin events</em> or <em>live signals</em> are required to declare unique acknowledgement labels |
| they are allowed to send. The labels of acknowledgement requests are then identifying the intended subscribers.<br /> |
| If the intended subscriber exists but does not receive the signal for non-transient reasons, Ditto issues |
| a weak acknowledgement for that subscriber.<br /> |
| Such reasons may be:</p> |
| <ul> |
| <li>The intended subscriber <strong>is not authorized</strong> to receive the signal by policy;</li> |
| <li>The intended subscriber did not subscribe for the signal type (<em>twin event, live command, live event or live message</em>);</li> |
| <li>The intended subscriber filtered the signal out by its <a href="basic-changenotifications.html#filtering">namespace or RQL filter</a>;</li> |
| <li>The intended subscriber dropped the signal because its <a href="connectivity-mapping.html">payload mapper</a> produced nothing.</li> |
| </ul> |
| |
| <h2 id="limitation">Limitation</h2> |
| |
| <p>The distributed nature of cluster pub/sub means that weak acknowledgements are not always issued correctly.<br /> |
| They are only <em>eventually correct</em> in the sense that some time after a change to the publisher-subscriber pair, |
| the issued weak acknowledgements will reflect the change.<br /> |
| Such changes include:</p> |
| <ul> |
| <li>Opening and closing of Websocket or other connections acting as the subscriber;</li> |
| <li>Subscribing and unsubscribing for different signal types via Websocket;</li> |
| <li>Modification of connections via the <a href="connectivity-manage-connections.html">connectivity API</a>;</li> |
| <li>Migration of a connection from one Ditto cluster member to another due to load balancing.</li> |
| </ul> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new concept of weak |
| acknowledgements.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Mon, 16 Nov 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-11-16-weak-acknowledgements.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-11-16-weak-acknowledgements.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Desired Feature Properties</title> |
| <description><h2 id="desired-feature-properties-added-to-things-model">Desired feature properties added to things model</h2> |
| <p>With the upcoming release of Eclipse Ditto <strong>version 1.5.0</strong> |
| <a href="basic-feature.html#feature-desired-properties">desired feature properties</a> are introduced to the things model for |
| <strong>API versions later than 1</strong>. The <em>desired properties</em> for features are added on the same level of the model as |
| the feature properties and can reflect for example feature property updates ,which are intended, but not yet applied.</p> |
| |
| <div class="alert alert-info" role="alert"><i class="fa fa-info-circle"></i> <b>Note:</b> Further logics for desired feature properties might be implemented in future Ditto |
| versions.</div> |
| |
| <p>A fully-fledged JSON representation of a feature with desired properties is shown below:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"lamp"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"com.mycompany.fb:Lamp:1.0.0"</span><span class="w"> </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"on"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"location"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"longitude"</span><span class="p">:</span><span class="w"> </span><span class="mf">34.052235</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"latitude"</span><span class="p">:</span><span class="w"> </span><span class="mf">-118.243683</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"on"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"color"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"red"</span><span class="p">:</span><span class="w"> </span><span class="mi">128</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"green"</span><span class="p">:</span><span class="w"> </span><span class="mi">255</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"blue"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"desiredProperties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"on"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h2 id="operations-on-desired-feature-properties">Operations on desired feature properties</h2> |
| |
| <ul> |
| <li><strong>CRUD operations</strong> |
| <ul> |
| <li>You can create multiple desired properties of a feature or just single ones.</li> |
| <li>You can retrieve all desired properties of a feature or just single ones.</li> |
| <li>You can modify all desired properties of a feature or just single ones.</li> |
| <li>You can delete all desired properties of a feature or just single ones.</li> |
| </ul> |
| </li> |
| <li><strong>Search</strong> |
| <ul> |
| <li>You can <a href="httpapi-search.html">search</a> for things with specific desired properties with <a href="basic-rql.html">RQL-functions</a>.</li> |
| <li>You can search for things, which have <a href="basic-rql.html#exists">existent</a> desired properties for a feature.</li> |
| </ul> |
| </li> |
| <li><strong>Get notified on changes</strong> |
| <ul> |
| <li>You can <a href="basic-signals-event.html">receive events</a> for changes done to the desired properties of things |
| you’re authorized to read.</li> |
| <li>You can <a href="basic-enrichment.html">enrich</a> and <a href="basic-changenotifications.html#filtering">filter</a> the |
| events you want to receive, for changes done to the desired properties.</li> |
| </ul> |
| </li> |
| </ul> |
| |
| <h3 id="executing-crud-operations-on-desired-feature-properties">Executing CRUD operations on desired feature properties</h3> |
| <p>CRUD operations can be executed either via the <a href="httpapi-concepts.html">Ditto HTTP API</a> <strong>versions later than 1</strong> or via |
| <a href="protocol-overview.html">ditto-protocol</a> messages.</p> |
| |
| <p><em>Possible CRUD operations for desired feature properties via ditto-protocol</em>:</p> |
| |
| <ul> |
| <li><a href="protocol-examples-retrievedesiredproperties.html">Retrieve all desired properties of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-retrievedesiredproperty.html">Retrieve a single desired property of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-modifydesiredproperties.html">Create/Modify all desired properties of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-modifydesiredproperty.html">Create/Modify a single desired property of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-deletedesiredproperties.html">Delete all desired properties of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-deletedesiredproperty.html">Delete a single desired property of a feature via ditto-protocol</a></li> |
| </ul> |
| |
| <h3 id="using-the-ditto-client-to-manage-desired-feature-properties">Using the ditto-client to manage desired feature properties</h3> |
| <p>The desired feature properties can also be retrieved, modified and deleted via the <a href="client-sdk-java.html">Ditto Java Client</a>. |
| With the upcoming (<strong>Ditto Java Client version 1.5.0</strong>), no special CRUD operations for |
| desired feature properties are implemented in the client. Thus, the operations have to be executed via creating |
| <strong>ditto-protocol messages</strong> manually in the client.</p> |
| |
| <p>Example for creating/modifying desired feature properties of a thing via the ditto-client:</p> |
| |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="n">Adaptable</span> <span class="n">modifyFeatureDesiredProperties</span> <span class="o">=</span> |
| <span class="n">Adaptable</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span><span class="n">TopicPath</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span><span class="n">ThingId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"com.mycompany.fb:Car:1.0.0"</span><span class="o">))</span> |
| <span class="o">.</span><span class="na">things</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">commands</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">modify</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">withPayload</span><span class="o">(</span><span class="n">Payload</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span> |
| <span class="n">JsonPointer</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"/features/lamp/desiredProperties"</span><span class="o">))</span> |
| <span class="o">.</span><span class="na">withValue</span><span class="o">(</span><span class="n">JsonObject</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">set</span><span class="o">(</span><span class="s">"on"</span><span class="o">,</span> <span class="kc">false</span><span class="o">).</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">()).</span><span class="na">build</span><span class="o">();</span> |
| |
| <span class="n">client</span><span class="o">.</span><span class="na">sendDittoProtocol</span><span class="o">(</span><span class="n">modifyFeatureDesiredProperties</span><span class="o">).</span><span class="na">whenComplete</span><span class="o">(((</span><span class="n">adaptable</span><span class="o">,</span> <span class="n">throwable</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> |
| <span class="k">if</span> <span class="o">(</span><span class="n">throwable</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"Received error while sending ModifyFeatureDesiredProperties: '{}' "</span><span class="o">,</span> |
| <span class="n">throwable</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> |
| <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Received response for ModifyFeatureDesiredProperties: '{}'"</span><span class="o">,</span> <span class="n">adaptable</span><span class="o">);</span> |
| <span class="o">}</span> |
| <span class="o">}));</span> |
| </code></pre></div></div> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new concept of desired properties.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Wed, 11 Nov 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-11-11-desired-properties.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-11-11-desired-properties.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Announcing Eclipse Ditto Release 1.4.0</title> |
| <description><p>Today, the Ditto team is happy to announce the next feature update of Ditto <code class="highlighter-rouge">1.x</code>: <strong>Eclipse Ditto 1.4.0</strong></p> |
| |
| <p>1.4.0 focuses on:</p> |
| |
| <ul> |
| <li>Declaration of acknowledgement labels unique to each subscriber</li> |
| </ul> |
| |
| <p>Please have a look at the <a href="release_notes_140.html">1.4.0 release notes</a> for a more detailed information on the release.</p> |
| |
| <p>Also, some bugs were fixed which are not backported to Ditto 1.3.0 - it is recommended to update to Ditto 1.4.0 right |
| away and skip 1.3.0.</p> |
| |
| <h2 id="artifacts">Artifacts</h2> |
| |
| <p>The new Java artifacts have been published at the <a href="https://repo.eclipse.org/content/repositories/ditto/">Eclipse Maven repository</a> |
| as well as <a href="https://repo1.maven.org/maven2/org/eclipse/ditto/">Maven central</a>.</p> |
| |
| <p>Also the <a href="client-sdk-java.html">Ditto Java client</a>’s artifacts were published to Maven central.</p> |
| |
| <p>The Docker images have been pushed to Docker Hub:</p> |
| <ul> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-policies/">eclipse/ditto-policies</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things/">eclipse/ditto-things</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things-search/">eclipse/ditto-things-search</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-gateway/">eclipse/ditto-gateway</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-connectivity/">eclipse/ditto-connectivity</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-concierge/">eclipse/ditto-concierge</a></li> |
| </ul> |
| |
| <h2 id="kubernetes-ready-helm-chart">Kubernetes ready: Helm chart</h2> |
| |
| <p>In order to run Eclipse Ditto in a Kubernetes environment, best rely on the official |
| <a href="https://hub.helm.sh/charts/eclipse-iot/ditto">Helm chart</a> and deploy Ditto via the Helm package manager.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Wed, 28 Oct 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-10-28-release-announcement-140.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-10-28-release-announcement-140.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| </channel> |
| </rss> |