| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <meta name="description" content=""> |
| <meta name="keywords" content="blogarchitectureconnectivity, "> |
| <title> Support for HMAC-SHA256 signing for connections </title> |
| |
| <link rel="stylesheet" href="css/syntax.css"> |
| <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" crossorigin="anonymous"> |
| <link rel="stylesheet" href="css/modern-business.css"> |
| <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" crossorigin="anonymous"> |
| <link rel="stylesheet" href="css/customstyles.css"> |
| <link rel="stylesheet" href="css/boxshadowproperties.css"> |
| <link rel="stylesheet" href="css/theme-ditto.css"> |
| <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700"> |
| |
| <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js" crossorigin="anonymous"></script> |
| <script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" crossorigin="anonymous"></script> |
| <script src="//cdnjs.cloudflare.com/ajax/libs/anchor-js/2.0.0/anchor.min.js" crossorigin="anonymous"></script> |
| <script src="js/toc.js"></script> |
| <script src="js/customscripts.js"></script> |
| |
| <script type="application/ld+json"> |
| { |
| "@context": "http://schema.org", |
| "@type": "Organization", |
| "url": "https://eclipse.org/ditto/", |
| "logo": "https://eclipse.org/ditto/images/ditto.svg" |
| } |
| </script> |
| |
| <link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16"> |
| <link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32"> |
| <link rel="icon" type="image/png" href="images/favicon-96x96.png" sizes="96x96"> |
| |
| <link rel="alternate" type="application/rss+xml" title="Eclipse Ditto Blog" href="https://www.eclipse.org/ditto/feed.xml"> |
| |
| <!-- Eclipse Foundation cookie consent: --> |
| <link rel="stylesheet" type="text/css" href="//www.eclipse.org/eclipse.org-common/themes/solstice/public/stylesheets/vendor/cookieconsent/cookieconsent.min.css" /> |
| <script src="//www.eclipse.org/eclipse.org-common/themes/solstice/public/javascript/vendor/cookieconsent/default.min.js"></script> |
| |
| <script> |
| $(document).ready(function() { |
| $("#tg-sb-link").click(function() { |
| $("#tg-sb-sidebar").toggle(); |
| $("#tg-sb-content").toggleClass('col-md-9'); |
| $("#tg-sb-content").toggleClass('col-md-12'); |
| $("#tg-sb-icon").toggleClass('fa-toggle-on'); |
| $("#tg-sb-icon").toggleClass('fa-toggle-off'); |
| }); |
| }); |
| </script> |
| </head> |
| |
| |
| <script> |
| (function(w,d,s,l,i){ |
| w[l]=w[l]||[]; |
| w[l].push({'gtm.start': |
| new Date().getTime(),event:'gtm.js'}); |
| var f=d.getElementsByTagName(s)[0], |
| j=d.createElement(s), |
| dl=l!='dataLayer'?'&l='+l:''; |
| j.async=true; |
| j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl; |
| f.parentNode.insertBefore(j,f); |
| })(window,document,'script','dataLayer','GTM-5WLCZXC'); |
| </script> |
| |
| |
| |
| <body> |
| <!-- Navigation --> |
| <nav class="navbar navbar-inverse navbar-fixed-top"> |
| <div class="container topnavlinks"> |
| <div class="navbar-header"> |
| <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> |
| <span class="sr-only">Toggle navigation</span> |
| <span class="icon-bar"></span> |
| <span class="icon-bar"></span> |
| <span class="icon-bar"></span> |
| </button> |
| <a class="navbar-ditto-home" href="index.html"> <img src="images/ditto_allwhite_symbolonly.svg" class="ditto-navbar-symbol" alt="Home"> <img src="images/ditto_allwhite_textonly.svg" class="ditto-navbar-symbol-text" alt="Eclipse Ditto™"></a> |
| </div> |
| <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> |
| <ul class="nav navbar-nav navbar-right"> |
| <!-- toggle sidebar button --> |
| <!--<li><a id="tg-sb-link" href="#"><i id="tg-sb-icon" class="fa fa-toggle-on"></i> Nav</a></li>--> |
| <!-- entries without drop-downs appear here --> |
| |
| |
| |
| |
| |
| |
| |
| <li><a href="blog.html">Blog</a></li> |
| |
| |
| |
| <li><a href="intro-overview.html">Documentation</a></li> |
| |
| |
| |
| <li><a href="http-api-doc.html">HTTP API</a></li> |
| |
| |
| |
| <li><a href="sandbox.html">Sandbox</a></li> |
| |
| |
| |
| |
| <li><a href="https://github.com/eclipse/ditto" target="_blank"> |
| <img src="images/GitHub-Mark-Light-32px.png" alt="Sources at GitHub"> |
| </a></li> |
| |
| |
| |
| |
| |
| <li><a href="https://github.com/eclipse/ditto-clients" target="_blank"> |
| <img src="images/GitHub-Mark-Light-32px.png" alt="SDK sources at GitHub">SDKs |
| </a></li> |
| |
| |
| |
| |
| |
| <li><a href="https://github.com/eclipse/ditto-examples" target="_blank"> |
| <img src="images/GitHub-Mark-Light-32px.png" alt="Example sources at GitHub">examples |
| </a></li> |
| |
| |
| |
| |
| <!-- entries with drop-downs appear here --> |
| <!-- conditional logic to control which topnav appears for the audience defined in the configuration file.--> |
| |
| |
| <li class="dropdown"> |
| <a href="#" class="dropdown-toggle" data-toggle="dropdown">Links<b class="caret"></b></a> |
| <ul class="dropdown-menu"> |
| |
| |
| <li><a href="https://projects.eclipse.org/projects/iot.ditto" target="_blank">Eclipse Ditto Project</a></li> |
| |
| |
| |
| <li><a href="https://www.eclipse.org/forums/index.php/f/364/" target="_blank">Forum</a></li> |
| |
| |
| |
| <li><a href="https://ci.eclipse.org/ditto/" target="_blank">Jenkins</a></li> |
| |
| |
| |
| <li><a href="https://dev.eclipse.org/mhonarc/lists/ditto-dev/" target="_blank">Mailing list archives</a></li> |
| |
| |
| |
| <li><a href="https://gitter.im/eclipse/ditto" target="_blank">Gitter.im chat</a></li> |
| |
| |
| </ul> |
| </li> |
| |
| |
| |
| <!--comment out this block if you want to hide search--> |
| <li> |
| <!--start search--> |
| <div id="search-demo-container"> |
| <input type="text" id="search-input" placeholder="search..."> |
| <ul id="results-container"></ul> |
| </div> |
| <script src="//cdnjs.cloudflare.com/ajax/libs/simple-jekyll-search/0.0.9/jekyll-search.js" type="text/javascript"></script> |
| <script type="text/javascript"> |
| SimpleJekyllSearch.init({ |
| searchInput: document.getElementById('search-input'), |
| resultsContainer: document.getElementById('results-container'), |
| dataSource: 'search.json', |
| searchResultTemplate: '<li><a href="{url}" title="Support for HMAC-SHA256 signing for connections">{title}</a></li>', |
| noResultsText: 'No results found.', |
| limit: 10, |
| fuzzy: true, |
| }) |
| </script> |
| <!--end search--> |
| </li> |
| </ul> |
| </div> |
| </div> |
| <!-- /.container --> |
| </nav> |
| |
| <!-- Page Content --> |
| <div class="container"> |
| <div id="main"> |
| <!-- Content Row --> |
| <div class="row"> |
| |
| |
| |
| <!-- Content Column --> |
| <div class="col-md-12" id="tg-sb-content"> |
| <!-- Look the author details up from the site config. --> |
| |
| |
| <!-- Output author details if some exist. --> |
| <!-- Output author details if some exist. --> |
| <!----> |
| <!--<span>--> |
| <!--<!– Mugshot. –>--> |
| <!--<img src="https://www.gravatar.com/avatar/9ca96e4eae3d3cf7df52ece39dc0b2fb?s=135" alt="A photo of Florian Fendt" />--> |
| |
| <!--<!– Personal Info. –>--> |
| <!--Written by <a href="https://github.com/ffendt" target="_blank">Florian Fendt</a>--> |
| <!--</span>--> |
| <!----> |
| |
| <article class="post" itemscope itemtype="http://schema.org/BlogPosting"> |
| |
| <header class="post-header"> |
| <h1 class="post-title" itemprop="name headline">Support for HMAC-SHA256 signing for connections</h1> |
| <p class="post-meta">Published by <img src="https://www.gravatar.com/avatar/9ca96e4eae3d3cf7df52ece39dc0b2fb?s=135" alt="A photo of Florian Fendt" style="width:50px;border-radius:50%;display:inline-block;margin-right:5px;" /><span itemprop="author" itemscope itemtype="http://schema.org/Person"><span itemprop="name"><a href="https://github.com/ffendt" target="_blank">Florian Fendt</a> </span></span> on <time datetime="2021-06-17T00:00:00+00:00" itemprop="datePublished">Jun 17, 2021</time> - Tags: |
| |
| |
| |
| <a href="tag_blog.html">blog</a>, |
| |
| |
| |
| <a href="tag_architecture.html">architecture</a>, |
| |
| |
| |
| <a href="tag_connectivity.html">connectivity</a> |
| |
| |
| |
| |
| </p> |
| |
| |
| </header> |
| |
| <div class="post-content" itemprop="articleBody"> |
| |
| |
| |
| |
| |
| <!-- this handles the automatic toc. use ## for subheads to auto-generate the on-page minitoc. if you use html tags, you must supply an ID for the heading element in order for it to appear in the minitoc. --> |
| <script> |
| $( document ).ready(function() { |
| // Handler for .ready() called. |
| |
| $('#toc').toc({ minimumHeaders: 0, listType: 'ul', showSpeed: 0, headers: 'h2,h3,h4' }); |
| |
| /* this offset helps account for the space taken up by the floating toolbar. */ |
| $('#toc').on('click', 'a', function() { |
| var target = $(this.getAttribute('href')) |
| , scroll_target = target.offset().top |
| |
| $(window).scrollTop(scroll_target - 10); |
| return false |
| }) |
| |
| }); |
| </script> |
| |
| <div id="toc"></div> |
| |
| |
| |
| <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 > 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"><ditto>/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 && 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"><ditto>/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 && 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"><ditto>/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 && 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"><ditto>/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 > 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> |
| |
| </div> |
| |
| |
| |
| </article> |
| |
| <hr class="shaded"/> |
| |
| <footer> |
| <div class="row"> |
| <div class="col-lg-12 footer"> |
| <div class="logo"> |
| <a href="https://eclipse.org"><img src="images/eclipse_foundation_logo.svg" alt="Eclipse logo"/></a> |
| </div> |
| <p class="notice"> |
| ©2021 Eclipse Ditto™. |
| Site last generated: Jul 23, 2021 <br /> |
| </p> |
| <div class="quickLinks"> |
| <a href="https://www.eclipse.org/legal/privacy.php" target="_blank"> |
| > Privacy Policy |
| </a> |
| <a href="https://www.eclipse.org/legal/termsofuse.php" target="_blank"> |
| > Terms of Use |
| </a> |
| <a href="https://www.eclipse.org/legal/copyright.php" target="_blank"> |
| > Copyright Agent |
| </a> |
| <a href="https://www.eclipse.org/legal" target="_blank"> |
| > Legal |
| </a> |
| <a href="https://www.eclipse.org/legal/epl-2.0/" target="_blank"> |
| > License |
| </a> |
| <a href="https://eclipse.org/security" target="_blank"> |
| > Report a Vulnerability |
| </a> |
| </div> |
| </div> |
| </div> |
| </footer> |
| |
| |
| </div> |
| <!-- /.row --> |
| </div> |
| <!-- /.container --> |
| </div> |
| <!-- /#main --> |
| </div> |
| |
| </body> |
| </html> |