telefonicaid / iotagent-json

IoT Agent for a JSON based protocol (with HTTP, MQTT and AMQP transports)
https://fiware-iotagent-json.rtfd.io/
GNU Affero General Public License v3.0
49 stars 89 forks source link

Possible Bug in IoT Agent (tested only with iotagent-json) #401

Open jicarretero opened 5 years ago

jicarretero commented 5 years ago

Let's Imagine I have an office (with a sensor providing humidity and temperature) and a car (with a sensor providing speed and fuel). The default API Key is "1234". So I provision two groups:

curl -X POST \
  http://${IOTAGENT_IP}:4041/iot/services \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'fiware-service: car' \
  -H 'fiware-servicepath: /car_measurement' \
  -d '{
    "services": [
        {
          "apikey":      "1234",
          "entity_type": "thing",
          "resource":    "/iot/car"
        }
    ]
}'
curl -X POST \
  http://${IOTAGENT_IP}:4041/iot/services \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'fiware-service: office' \
  -H 'fiware-servicepath: /office_measurement' \
  -d '{
    "services": [
        {
          "apikey":      "1234",
          "entity_type": "thing",
          "resource":    "/iot/office"
        }
    ]
}'

Each group with one sensor:

curl -X POST \
  http://${IOTAGENT_IP}:4041/iot/devices \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'fiware-service: car' \
  -H 'fiware-servicepath: /car_measurement' \
  -d '{
    "devices": [
        {
            "device_id": "car0001",
            "entity_name": "car0001",
            "entity_type": "thing",
            "attributes": [
                  {"object_id": "s", "name": "speed", "type": "Float"},
                  {"object_id": "f", "name": "fuel", "type": "Float"}
            ]
        }
    ]
}'
curl -X POST \
  http://${IOTAGENT_IP}:4041/iot/devices \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'fiware-service: office' \
  -H 'fiware-servicepath: /office_measurement' \
  -d '{
    "devices": [
        {
            "device_id": "office0001",
            "entity_name": "office0001",
            "entity_type": "thing",
            "attributes": [
                  {"object_id": "h", "name": "humidity", "type": "Float"},
                  {"object_id": "t", "name": "temperature", "type": "Float"}
            ]
        }
    ]
}'

And I POST data to the second device (office) with this error:

curl -X POST \
  "http://${IOTAGENT_IP}:1884/iot/d?i=office0001&k=1234" \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'fiware-service: office' \
  -H 'fiware-servicepath: /office_measurement' \
  -d '{
     "h": 57.0,
     "t": 24.3
}'

The response is: {"name":"DEVICE_NOT_FOUND","message":"No device was found with id:thing:office0001"}

So, I try to find out why looking at the queries in MongoDB: 1. find { find: "groups", filter: { apikey: "1234" }, projection: { __v: 0 }, limit: 1, ...

The answer is (but it shouldn't be this one): { "_id" : ObjectId("5cd3fe60e6e14c017af932a8"), "resource" : "/iot/car", "apikey" : "1234", "type" : "thing", "service" : "car", "subservice" : "/car_measurement", "__v" : 0 }

2. find { find: "devices", filter: { id: "office0001", service: "car", subservice: "/car_measurement" } That query is not the one that should be made, it doesn't correspond to my POST!!!

At this time I have a new Device in IoT Database (That I don't know why it is there), without Objects: And I have a new device in my MongoDB: { "_id" : ObjectId("5cd4016fe6e14c017af932ac"), "subscriptions" : [ ], "creationDate" : ISODate("2019-05-09T10:31:11.301Z"), "id" : "office0001", "type" : "thing", "name" : "thing:office0001", "service" : "car", "subservice" : "/car_measurement", "transport" : "HTTP", "__v" : 0 }

fgalan commented 5 years ago

Could you repeat the test using different api keys for the service? For instance:

        {
          "apikey":      "1234",
          "entity_type": "thing",
          "resource":    "/iot/car"
        }
        {
          "apikey":      "5678",
          "entity_type": "thing",
          "resource":    "/iot/office"
        }

In order to see if the problem also happens in this case.

jicarretero commented 5 years ago

No, in this case, it works. The problem is the "apikey" which is queried before doing anything. If I have 2 groups with same apikey, it will always get the first one, since the 1st query to MongoDB is (as I explained before):

However, May the IoTAgent be using a "Document Database" like MongoDB as some kind of relational database. Why not adding to the documents in the "device" collection the "resource" and "apiKey" fileds from the group collection and perform just one query on the device to MongoDB instead of the 3 that are currently happening (groups, devices, groups again)?

fgalan commented 5 years ago

Which version of the IOTA agent did you use in your tests, please?

AlvaroVega commented 5 years ago

And I POST data to the second device (office) with this error:

curl -X POST \ "http://${IOTAGENT_IP}:1884/iot/d?i=office0001&k=1234" \ -H 'cache-control: no-cache' \ -H 'content-type: application/json' \ -H 'fiware-service: office' \ -H 'fiware-servicepath: /office_measurement' \ -d '{ "h": 57.0, "t": 24.3 }'

The response is: {"name":"DEVICE_NOT_FOUND","message":"No device was found with id:thing:office0001"}

Probably is not found due to that POST does not contain a proper resource (that should be /iot/office), and agent is looking with default resource (/iot/d) and that apikey.

fgalan commented 5 years ago

We have included recently some improvements related to apiKey management in the library (https://github.com/telefonicaid/iotagent-node-lib/blob/master/CHANGES_NEXT_RELEASE#L1):

ADD to use apikey to search group configuration at device registration time

Maybe it is not related with this problem but I'd recommend to use last version (fiware/iota-json:latest should work, as it bring lastest from library also) an test with it.

fgalan commented 5 years ago

Anyway, looking to the queries you cite:

 1. find { find: "groups", filter: { apikey: "1234" }, projection: { __v: 0 }, limit: 1, ...

I think that if the query would include also the resource, i.e:

 1. find { find: "groups", filter: { apikey: "1234", resource: "xxxxx" }, projection: { __v: 0 }, limit: 1, ...

it will work

Note that according to model (https://github.com/telefonicaid/iotagent-node-lib/blob/master/lib/model/Group.js#L47) the combination of apiKey + resource is unique, so that query always return the right document, by definiton.

The problem here is how to get the resource. In theory the URL path can be taken as resource. So if the request is for instance POST http://${IOTAGENT_IP}:1884/iot/d?i=office0001&k=1234 then resource is "/iot/d". However, I think that appart of the default resource (especified in config.js/envars, using to be /iot/d for UL or /iot/json for JSON) current IOTAs doesn't start other endpoint to receive measures in the southbound (and it is not trivial, as it needs dynamically create/remove express routes for each resource as new services are provissioned/removed).

As workaround: ensure that all you services has different apiKeys and it should work.