The user should be identified and authenticated to use the platform.
In one hand, Thing'in should embed a basic auth mechanism to manage its own user (admin, supervisor, or any other client that want a thing'in id).
In other hand, to facilitate the capture of new user, Thing'in should provide interconnection with some OpenId Connect servers (like Facebook, Google or Orange).
Once authenticated and registered in the platform, the user is associated to an internal thing'in id (tiId
). This id would be built from the user info. Example: a hash of (user email address+idpKey
) where the idpKey
is a key define randomly for an idp (idp id in [BasicAuth, Google, facebook, Orange]). Obviously all the allowed idp should be registered in Thing'in and the idpKey
should be defined at the registration step. To ensure distribution and consistency, these informations //(ipd : {Id, name, endpoint, key})// will be stored in a dedicated storage (example //zookeeper//).
Concerning the user informations //(user: {tiId
, idpId, idpUUID
, associated-ROLES})//, they will be stored in the graph data base directly.
Since the usage of Thing'in could be put in place in an enterprise, i.e. an administrator of an enterprise would like to authorize employees to access its own avatar. The global registration will be based on an invitation scheme. The administrator gives an invitation token to a not registered user . This token could contains information and scope (issuer id, applied domain, expiration of the invitation, email of the invited person ...). To begin his enrollment, the user should give his idp token and could give an invitation token. Thing'in validates this last token to complete the registration.
Thing'in provides API to create and validate invitation tokens.
Registration API could take into account invitation tokens.
Thing'in uses public&private keys to built the root of trust. The public key is registered inside the certificate delivered by the federator.
The private keys should be store in a persistent storage, they should be distributed over all the core nodes and the consistency should ensured.
To avoid an authentication at each time or a session mechanism to use the API, we prone the use of Access Key. This key will be secrete and should be prensented at each API call. It may be based on Java Web Token (JWT). It will be signed with a master key (MK) of Thing'in. Everyone could verify the authenticity of an AK with the public key contained in the certificate.
A user, once authenticate, could create/generate Access Keys. These AKs inherit from the user roles and should be limited with an expiration date. We set the maximum duration to 3 months. An AK is never stored in the system. It is given by a requester, the system validates the AK and authorizes the requester to proceed the action.
There are 3 ways to authenticate in Thing'in:
To avoid malicious behaviour, the administraor or the user himself should be able to revoke an access key. This means, the system remember the revoked AK, at least for a period running until its expiration date. As we want a quick valitation of the AK, the revoked AK should be indexed to retrieve one rapidly and may be cached in each API node.
Access to Thing'in is controlled, each user could have different privileges. In one side, an administrator should have all the rights (aka. from management of user to management of avatar, passing by the management of specific platform functions). In other side, a low privileged user could only view public avatar information.
Then, in Thing'in, to access the API, the users should present his access key.
And following the rights associated to this access key, the user could access or not the exposed resources.
To manage the access, we base on a Role Base Access Control. Each user has a set of Roles. With a given role, the user has access or not to a resource. As the rights and the ressources are statically defined, the role could be defined dynamically as simple tag with a description and its semantic could be enforced by a policiy. A policy would be a set of rules setting the right of each role on each ressource.
A ressource denotes a resource of the API. On these resources some action could be done. A resource has a owner and could be associated to a domain.
AVATAR
: to manage the avatars. Possible actions: Create
, Read
, Update
, Delete
, Find
AVATAR/METRICS
: to get the metris of an avatars. Possible actions: Read
TRIGGER
: to manage notification (follow the update of avatars). Possible actions: Create
, Read
, Update
, Delete
METRICS
: to get the metrics information. Possible actions: Read
USER
: to manage the users information and roles. Possible actions: Create
, Read
, Update
, Delete
, List
USER/ROLE
: to manage the users roles. Possible actions: Create
, Delete
USER/METRICS
: to get the users metrics information (number of avatars, iops, request size avg, response size avg, ...). Possible actions: Read
USER/ACCESSKEY
: to manage the Access Keys (AK) of a user. Possible actions: Create
, Read
, Revoke
ACCESSCONTROL/ROLE
: to manage the role naming and their description. Possible actions: Create
, Read
, Delete
ACCESSCONTROL/POLICY
: to manage the api policy by action on rules. Possible actions: Create
, Read
, Update
, Delete
ASPECT
: to manage the processes that optimize the database. Possible actions: Create
, Read
, Delete
TASK
: to manage the background task dedicated to asynchronous update of the database. Possible actions: Create
, Read
, Update
, Delete
LABEL
: to manage the labels that can be attached to avatar, properties or relationships. Possible actions: Create
, Read
The rights give the authoriation of a role on a resource. We enhance the right definition with conditions.
denied
: no right. To simplify the reading, in the table we use an empty space.
allowed
: without restriction. We use ✔ in the table.
owned
: condition on the owner. The user making the request should be the owner of the resource.
self
: condition on the identidity. The user making the request should be the user associate to the resource (a user is allowed to modified his info).
domain
: condition on the domain. The domain of the resource should be inside of the requester domain. If the requester domain is not defined (or equals to /), there is no limitation on domain.
ADMIN
: full right over all the resources.
SUPERVISOR
: observe the platform and user metrics.
SERVICE-ADMIN
: administrator of a 3rd party service.
PROVIDER
: avatar provider.
USER
: standard user.
The current rules can be retreived thanks to the API :
GET <api-endpoint>/accesscontrol/policies/default
Thing'in provide confidentiality at different levels. The first level is the concept of visibility. The second the concept of access control (at the whole avatar or only a part).
Bob does not allow Alice to see the avatar of his camera.
By default, when Bob creates an avatar, only him (the owner) can reach it (aka visibility=255
). If he wants to share it with anyone else, he must set the avatar as visible (aka visibility=0
).
A visible avatar can be seen by external users (i.e. users from others TiPods) if the conditions set by access control can be checked.
In the following sections about ACLs, we assume avatars are visible, i.e. visibility=0
.
Thing'in provides confidentiality at a fine grain level, at the attribute level. One could define dedicated access control for each attribute of an avatar.
Bob does not allow Alice to see the geolocalization attribute of his car, but she can see the brand, the fuel type, ....
As the response in RDF/Turtle
format should be valid, it's impossible to simply drop not allowed attributes for the user, the response would be invalid. To avoid this issue, we introduce obfuscation instead of dropping the attributes. To inform the user which attibutes are obfuscated, we attach an object giving them with a special relation (obfuscation).
The ACL gives the rights of a user on an avatar. The different limitations on the attributes and relations. Is the user capable of reading a given attribute? is he capable of modifying the value of a given attribute? Is he capable of viewing a given relation?
In Thing'in, the definition of the rights/policy are inspired from AWS IAM.
definition of the rights on avatars:
resource is : "."
Read => deny | allow
Delete => deny | allow
If not specified by ACL statement rules, avatar can be read and avatar can not be deleted. It's like if in rules, we have by default:
{
"effect" : "Allow",
"action" : "Read",
"resources": "."
},
{
"effect" : "Deny",
"action" : "Delete",
"resources": "."
}
ACL only apply on visible avatars, we can not use ACL to add visibility on private avatars
definition of the rights on attributes:
attribute resources are like : ".attr" / ".*" / ".attr" / ".{//REGEX//}"
Read => deny | allow | blur
Update (include Read, Modify, Create and Delete) => deny | allow
definition of the rights on relations:
relation resources are like : "-attr->" / "-*->" / "-attr->" / "-{//REGEX//}->"
Read => deny | allow
Update (include Read, Modify, Create and Delete) => deny | allow
definition of the rights on any elements:
To avoid duplication of resources ".", ".*", "-*->", we provide a specal key "*" .
rights = {
"uuid" : "DEA12F",
"desc" : "my super acl",
"timestamp": 123456789,
"statements":[
{
"condition": {"$like" : ["$user.domains", "%http://www.orange.com/%"]},
"rules" : [
{
"effect" : "Allow",
"action" : "Update",
"resources": ".attrkey0"
},
{
"effect" : "Allow",
"action" : "Read",
"resources": [".attrkey1" , ".attrkey2"]
},
{
"effect" : "Allow",
"action" : "Read",
"resources": "-{lbl}->"
}
]
},
{
"condition": {"$inherit" : "http://www/base#c1"},
"rules" : [
{
"effect" : "Allow",
"action" : "Read",
"resources": ".*" // same as ".{.*}"
},
{
"effect" : "Allow",
"action" : "Read",
"resources": ".{.*att.*}" // "regex base"
},
{
"effect" : "Allow",
"action" : "Update",
"resources": "-*->" // same as "-{.*}->"
},
{
"effect" : "Allow",
"action" : "Read",
"resources": ["-{.*att.*}->", "-{.*toto}->"]
}
]
},
{
"condition": {"$inherit" : "http://www/base#c2"},
"rules" : [
{
"effect" : "Deny",
"action" : "Read",
"resources": "*" // any resources of the avatar, same as [".{.*}", "-{.*}->", "."]
}
]
},
{
"condition": {},
"rules" : [
{
"effect" : "Deny",
"action" : "Read",
"resources": ".*"
},
{
"effect" : "Deny",
"action" : "Read",
"resources": "-*->"
}
]
},
]
}
The ACL policy is composed of statements that have 2 parts : condition and rules.
The ACL policy acts like a firewall filter, upon a statement condition is validated, the statement rules are applied, and only that rules. To have a default behaviour, the last condition in the ACL should be be True or empty ({}).
Remark : The winner take it all. i.e. Only the rules of the first condition that is satisfied id applied. If later in the list another condition would be satisfied and allowed more rights on a resource, this will not happen. //(=> perf reasons)//
When an ACL is defined, it applies to every user, including the owner of the avatar. Be carefull to define a special rule to keep visibility on your own avatars when using ACL.
Statement to keep visibility on you avatar:
{
"condition": {"$eq":["$user.uuid","$avatar.owner"]},
"rules" : [
{
"effect" : "Allow",
"action" : "Read",
"resources": "*"
},
{
"effect" : "Allow",
"action" : "Update",
"resources": "*"
},
{
"effect" : "Allow",
"action" : "Delete",
"resources": "*"
}
]
}
The expression of a condition is express in TEL (see Expression Language).
By default, if not specified the right on an attr is set at {"effect" : "Allow", "action":"Write"}.
If right on _id
is set to read denied, all the avatar will not be visible.
The update of an ACL could be done by its owner only.
The requester has an tiId.
We defined the concept of Security Group to add a group level management to improve the specification of ACL.
A SG is :
An Avatar could belong to one and only one SG.
A user could belong to multiple SG.
When getting an Avatar, the system should validate that :
If the avatar has an ACL, the ACL should be applied to filter the avatar attributes and relations.
The SG could be used in ACL to validate conditions like :
"condition" : {"avatar.group.uuid", "$user.groups"]}
Each request could bring, in addition to the user access token, some other optional information relative to the user context. This context should be given by the service to Thing'in.
position : a geojson value (point)
localtime : a ISO 8601 date (e.g. '2012-04-23T18:25:43.511Z')
device : computer | mobile | console | TV | box | other
meta_xxx : custom metadata created by the service
These user context attributes could be used in ACL condition to add more filters.
"condition" : {"and" : [{"near" : ["avatar.www_opengis_net_gml_pos", 200]}, {"context.meta_hint", "hello%"]}]}
metadata (request level, given by the requester)
Here is a small example to demonstrate how to use ACL and Security Group. In our example by default we do not want to give access to our avatars, to some users we give read access and to other users we give read and update access. Suppose we have 3 users: Alice, Bob and Eve. We want to give read and update access to Alice, only read access to Bob and no access to Eve.
First you need to create security groups. In our example we will create two security groups, one group for users allowed to read and update avatars:
{
"avatars": [
],
"users": [
"<AliceUuid>"
],
"desc": "Users allowed to read and update"
}
and a second group for users only allowed to read:
{
"avatars": [
],
"users": [
"<BobUuid>"
],
"desc": "Users allowed to read"
}
To create the security group, you need to perform the following query on Thing’in:
curl -X POST "https://coreapi.thinginthefuture.com/securitygroup/" -H "accept: application/json" -H "content-type: application/json" -H "Authorization: <thinginToken>" -d "{ \"avatars\": [ \"string\" ], \"users\": [ \"string\" ], \"desc\": \"string\"}"
Once created uuids will be assigned to the following groups. You will need those uuids in the second step.
Second, you will create an ACL, it will allow you to define which right to give on avatars. In ACL you define rules, once a rule match it will be applied. Therefore, you need to define first the most specific rules. In our example it will be the users which can read and update avatars (i.e. Alice).
{
"statements": [
{
"rules": [
{
"action": "Update",
"effect": "Allow",
"resources": ".*"
},
{
"action": "Update",
"effect": "Allow",
"resources": "-*->"
},
{
"action": "Read",
"effect": "Allow",
"resources": ".*"
},
{
"action": "Read",
"effect": "Allow",
"resources": "-*->"
}
],
"condition": {"$in" : ["<uuidGroupRead&Update>", "$user.groups" ]}
},
In this first rule we defined the condition upon avatars "ressources": ".*", which mean that the read and update are applied on all the properties of an avatar. And you need also to define condition upon relations "ressources": “-*->”, the user will be able to see all the outgoing relationship of an avatar. A user will be allowed to read or update an avatar’s properties or relationship if he/she is a member of the group <uuidGroupReand&Update>. In the second rule we define user which can only read.
{
"rules":[
{
"action": "Read",
"effect": "Allow",
"resources": ".*"
},
{
"action": "Read",
"effect": "Allow",
"resources": "-*->"
}
],
"condition": {"$in" : ["<uuidGroupRead>", "$user.groups" ]}
},
In the last rule, we defined that users cannot read nor update the avatars. This rule will be applied if the user is not is the group <uuidGroupRead&Update> nor <uuidGroupRead>.
{
"rules" : [
{
"effect" : "Deny",
"action" : "Update",
"resources": "."
},
{
"effect" : "Deny",
"action" : "Read",
"resources": "."
}
],
"condition": {}
}
],
"desc": "ACL for the domain of Bob and Alice"
}
To create an ACL you need to perform the following query on Thing’in:
curl -X POST "https://coreapi.thinginthefuture.com/acl/" -H "accept: application/json" -H "content-type: application/json" -H "Authorization: <thinginToken>" -d "{ \"statements\": [ { \"rules\": [ { \"action\": [ \"Read\" ], \"params\": {}, \"effect\": \"Allow\", \"resources\": [ \"string\" ] } ], \"condition\": {} } ], \"desc\": \"string\"}"
When the ACL will be created a uuid will be assigned to it. You will need this uuid to link the avatars upon which you want to define a security policy to the ACL you just defined.
The last part will be to attach the ACL to the concerned avatars. By default an avatar can be read and update by anyone. To add a security policy on an avatar you need to attach an ACL upon the given avatars. For that you will need to perform the following query:
curl -X POST "https://coreapi.thinginthefuture.com/acl/<uuidACLAlice&Bob>/avatars" -H "accept: application/json" -H "content-type: application/json" -H "Authorization: <thinginToken>" -d "{ \"avatar_iri\": [ \"string\" ], \"avatar_uuid\": [ \"<uuidAvatar1>\", \"<uuidAvatar2>\"]}"