It is a truth universally acknowledged, that a single namespace in possession of a good tag bundle must be in want of a good privacy policy.
Over recent weeks I’ve written quite a lot about the Fluidinfo permissions system, concentrating on what permissions Fluidinfo maintains, how we might represent those, list them and change them. Today I want to look at how they work, in practical terms, from a user’s perspective. There were some surprises for me as I tried them out. If you don’t want to read all the detail you can cut the chase by just reading the Take-aways
In what follows, I will assume you are familiar with the structure of Fluidinfo permissions and how fdb represents them and sets them.
If you haven’t already read the post on listing and setting permissions, you will probably find it helpful to do so before continuing.
In what follows, I’m generally going to change permissions in one namespace, as user njr0 and explore the implications of this using the user miro, which I also control. If you want to do the same exploration and only have access to a single account, bear in mind that you can always user the test user (username test, password test) to try things out.
Let’s set things up first:
$ fdb su njr0
Credentials set to user njr0.
$ fdb -F tag -a http://google.com njr0/secret/password="very secret"
$ fdb -F ls -ld njr0/secret njr0/secret/password
nrwcr--r-- njr0/secret/
trwcr--r-- njr0/secret/password
$ fdb -F perms private secret
$ fdb -F ls -ld njr0/secret
nrwc------ njr0/secret/
So here we
- set our credentials to njr0
- Tag http://google.com with a password using a tag called secret/password
- Check the permissions on the namespace (njr0/secret) and the password tag (njr0/secret/password)
- See that they’re open, so change the permissions on the namespace to private. (In raw Fluidinfo-terms, means setting all permissions to closed with exception lists consisting of the owner—njr0—only.
- Verify that only I as the owner (njr0) have any permissions on my secret namespace.
[If you’re wondering what’s up with all the -F flags, these tell fdb to use full, Fluidinfo-style paths; I have fdb configured to use unix-style relative paths that, by default, I don’t need to put in the leading njr0/; if you don’t change the default, you can omit the -F.]
Now let’s switch to Miró, and see how things look.
$ fdb su miro
Credentials set to user miro.
$ fdb -F ls -ld njr0/secret
$ fdb -F show -a http://google.com njr0/secret/password
Object with about="http://google.com":
njr0/secret/password = "very secret"
There is no output from the first ls command because Miró does not have read permission (“list” permission, as it is officially known) on the namespace. But the last command (the show command) might come as a bit of a surprise.
Despite the fact that njr0 removed read permission on the namespace njr0/secret, miro can still read it.
Fluidinfo permissions are NOT heirarchical.
If you want to stop tags being read, you have to remove read permission on each one individually.
It’s not that the permissions system doesn’t work; it’s just a bit more—er—permissive than you might expect. (Terry calls it “bloody-minded”.) In effect, Fluidinfo’s read/list permission on namespaces is a bit like execute permission on a directory in Unix, with the difference that there isn’t another permission to close up the namespace completely.
Let’s round this part of the discussion off by closing down the permission on the password tag.
$ fdb su njr0
Credentials set to user njr0.
$ fdb -F perms private njr0/secret/password
$ fdb -F ls -ld njr0/secret/password
trwc------ njr0/secret/password
$ fdb su miro
Credentials set to user miro.
$ fdb -F show -a http://google.com njr0/secret/password
Object with about="http://google.com":
(error code 401 (UNAUTHORIZED) attempting to read tag njr0/secret/password)
You might wonder whether this UNAUTHORIZED error is confirming that the tag is present on this object, but it isn’t. We get the same error if we try look looking for the tag on http://yahoo.com:
$ fdb -F show -a http://yahoo.com njr0/secret/password
Object with about="http://yahoo.com":
(error code 401 (UNAUTHORIZED) attempting to read tag njr0/secret/password)
$ fdb su njr0
fdb su njr0
Credentials set to user njr0.
$ fdb -F show -a http://yahoo.com njr0/secret/password
fdb -F show -a http://yahoo.com njr0/secret/password
Object with about="http://yahoo.com":
(tag njr0/secret/password not present)
The current situation is plainly unsatisfactory.
A key reason for not wanting to make permissions hierarchical in Fluidinfo is that this would require checking up the hierarchy when reading them, which could be slow. We are, however, discussing a proposal that whenever a tag or namespace is created, the hierarchy be checked and the permissions be set in a way that is consistent with the enclosing namespace hierarchy. If that were the case, most of the practical problems would dissipate. Under this proposal, if you were to create a namespace, and then make it private, everything created under it would then become private. And if you decided to make an existing namespace private, you would only have to change the permissions on the subnamespaces and tags already in it to ensure that the whole namespace would be private. Indeed, if I were to add a -R option to the perms command in fdb, to descend the hierarchy recursively, this would be a single command. I will probably do that.
I hope this proposed change, or something similar, that will be made in Fluidinfo.
Let’s now turn our attention to write permissions.
Fluidinfo actually maintains a plethora of write permissions, but fdb normally shows and maniplates them together for simplicity. Let’s see what happens if njr0 gives write permission on a namespace to miro. Specifically, let’s set things so that miro can read and write the secret namespace
$ fdb su njr0
Credentials set to user njr0.
$ fdb -F perms group miro njr0/secret
$ fdb -F ls -gd njr0/secret
nrwcrw---- miro njr0/secret/
$ fdb -F ls -l njr0/secret
trwc------ njr0/secret/password
As you can see, njr0 retains read, write and control permissions on the namespace njr0/secret, but is now allowing miro to read and write the namespace as well. We also see that this hasn’t changed the permissions on the njr0/secret/password tag, which remains private to njr0.
So let’s get Miró to tag something for us. In particular, let’s get it to give Amsterdam an njr0/secret/rating of 2.
$ fdb su miro
Credentials set to user miro.
$ fdb -F tag -a Amsterdam njr0/secret/rating=2
$ fdb -F ls -l njr0/secret/rating
tr--rwcr-- njr0/secret/rating
Woah! There’s a permissions structure you don’t expect to see very often. Let’s expand it using -g
$ fdb -F ls -g njr0/secret/rating
tr--rwcr-- r:(world) w:miro njr0/secret/rating
Let’s read across the permissions and see what they say.
The leading t just means that njr0/secret/rating is a tag.
The next block of three (r--) shows the permissions that the owner—njr0—has for this tag. The owner only has read permission—no write permissions on the tag, and no control permissions. (The owner is not a happy owner at this point.)
Skipping to the final block of three (r--), this shows that the world (everyone, barring any group exceptions) has read permission on the tag, but no write or control permissions.
The “middle” block of three (rwc) shows the group permissions, and when listed used the -g opion, shows us that the read group is world (shown as (world)) and the write group is miro.
So the read permission is as expected. But the write permissions are set so that miro can write the tag, but the owner, njr0, cannot.
That’s worth saying again:
When someone other than the owner (the person in whose top-level namespace the tag exists) creates a tag, by default the write permissions are set so that the creator has write permissions, and the owner does not. The same goes for namespaces created by someone other than the owner.
If that weren’t surprising enough, look at the control permissions. The owner doesn’t have control permissions but the group does. The ls -g command in fdb doesn’t even show you who, because, to be honest, I wasn’t expecting control to be given away very much. I should probably make it so that it lists the control group, when there is a group exception.) The suspicion would have to be that the creator also gained control permissions, but let’s see by using ls -L, to give the longer Fluidinfo view of the permissions, in all their glory.
fdb -F ls -L njr0/secret/rating njr0/secret/rating: ABSTRACT TAG (/tags) Write update (metadata): policy: closed; exceptions = [miro] delete (delete): policy: closed; exceptions = [miro] Control control (control): policy: closed; exceptions = [miro] TAG (/tag-values) Read read (read): policy: open; exceptions = [] Write create (tag): policy: closed; exceptions = [miro] delete (untag): policy: closed; exceptions = [miro] Control control (control): policy: closed; exceptions = [miro]As suspected, miro has expropriated not only write permission, but also control permissions (the bounder!). Miró now has it, and there’s not a thing njr0 can do about it, other than appealing to Miró’s better nature.
Since Miró has control and write access, and njr0 doesn’t, njr0 can’t take back control of or delete the tag. You might think njr0 could take the drastic step of deleting the njr0/secret namespace, but he can’t even do that because a namespace has to be empty before it can be deleted, and njr0 can’t make it empty.
Again, this is worth emphasizing:
Be very careful granting write permissions on namespaces in Fluidinfo at the moment. If you let someone else write in your namespace, they will gain exclusive write and control permissions over any tags and namespaces they create under the namespace for which you gave them write permissions, and you can’t take them back.
“You give an inch, they take a mile.”
Obviously, when I say you can’t take them back, I mean that the Fluidinfo API won’t let you take them back. There really is a superuser in Fluidinfo (fluidddb) and if you were to explain your predicament to the Fluidinfo team, I’m guessing they’d be sympathetic. But even so . . .
This is an extraordinary and bad situation which I’m sure will be remedied. Clearly, it can’t be right that miro can end up have complete and exclusive control of a tag or subnamespace in my namespace without my ever having granted miro control permissions on anything.
In effect, at the moment, the creator of the tag is becoming its virtual owner. Indeed, the word “owner” becomes slightly problematical here. In the unix-like ls -l way of showing permissions, I have used the first group of three control indicators to show the permissions for the person whose Fluidinfo username is the first part of the full path to the tag or namespace—i.e., njr0 in the case of the tag njr0/secret/rating. I think this is natural and right, and I don’t plan to change it. I would even argue that it is consistent with Fluidinfo’s view, because Fluidinfo doesn’t maintain any separate notion of the owner of a tag or namespace. In fact, in some sense, it doesn’t have any notion of an owner at all: tags and namespaces only have permissions, and those consist of a policy (open or closed) and an exception list (a list of users to whom the policy is reversed).
Fluidinfo is perfectly content for no one to have any permissions for a tag or namespace (000). Or for everyone in the world except the person in whose top-level namespace it resides to have control permissions (policy: open; exceptions [njr0]—es exemplified by the James Bond 007 permission, or even 001 permissions).
“Yes, I do do see your predicament, old boy, but I’m afraid you really only have yourself to blame. I suggest you be more careful next time. Toodle pip!”
Bloody-minded isn’t the half of it. The team is aware of the situation, and again, I’m confident things will change.
To round this off, let’s just confirm that Miró can change things back.
$ fdb su miro
Credentials set to user miro.
$ fdb -F perms default njr0/secret/rating
$ fdb su njr0
Credentials set to user njr0.
$ fdb -F ls -g njr0/secret/rating
trwcr--r-- (world) njr0/secret/rating
Sanity restored. Unlike Fluidinfo, when you set the permissions to default with the command
fdb perms default njr0/secret/rating
fdb takes default to be the default for the owner of the tag (whoever is root namespace the tag lives in), not the creator of the tag. Therefore, permissions revert to the default. (In this case, private might have made more sense.)
This has has the side effect of removing Miró’s write permission, since the perms command doesn’t have a way, at the moment, of saying give back control and write, but keep the exception on write. I might add such a thing, since it’s clearly useful in the present situation, or I might just implement chmod, which would probably let you do it by saying fdb chmod 760 njr0/secret/rating. To achieve full resporation, njr0 now has to re-grant the appropriate permissions, and indeed remove world read. In this case, the relevant commands are
$ fdb su njr0
Credentials set to user njr0.
$ fdb -F perms group miro njr0/secret/rating
$ fdb -F ls -g njr0/secret/rating
trwcrw---- miro njr0/secret/rating
Here, we’re back where we expected to be. But it shouldn’t have been that hard, even though the last step was only necessary because the perms command doesn’t offer a way to hand back control permissions to the owner while keeping write permissions (because, when I implemented perms, I didn’t know just how bloody-minded Fluidinfo was.)
Here are the key take-aways:
Permissions in Fluidino are entirely non-hierarchical.
As a consequence of point 1, removing read permission from a namespace does not stop people reading tags or namespace under it. Any existing tags and namespaces have to be changed explicitly to remove read access, and any new tags or namespace created also have to be protected individually.
(You can change your defaults for new tags and namespaces using /policies if you like; but /policies affect all new tags and namespaces, not just a subset. It’s a bit like an non-inverted version of a Unix umask.)
When a new tag is created, its permissions are set according to the default permissions the creator, not the owner. This includes control permissions.
The direct consequence of point 3 is that, in normal circumstances, if someone else creates a tag or namespace somewhere in your namespace (which they can, of course, do only if you grant them the appropriate write permissions), they not you will end up with exclusive write and control permissions unless they explicitly change the permissions to that you have access.
Practically speaking, I think the consequence of this that you want to be very cautious granting write permissions on namespaces. Granting them on individual tags is much safer, because permissions are at the level of the whole tag (the abstract tag, if you will), rather than at the level of a particular tag on an object. As far as I can see, letting someone else write one of your tags only allows them exactly what you would expect: they can write that tag, delete it, change it and so forth, but not wrest control from you when you’re not looking.