setting-ize permissions



permissions are currently set by card, making them difficult to configure, administrate, query, etc.


We also want as an outcome here the capability to restrict permission to the creator of a card (e.g. for your account card and associated plus cards). Some way other than add Author role.






Settings Cards

We add the following cards:

  • *create
  • *read
  • *update  (*edit is used, and this follows CRUD acronym)
  • *delete
  • *comment (part of me wants to make this *append.  like you could add to a pointer but not remove anything from it....)  

Settings Values

Each setting value is a special type of pointer*.  Stored as list of multiple items, each of which can be

  • a Role card (which I'd advocate renaming to "Group" or something similar)
  • a card with an account
  • one of the following contextual names: _left, _creator

Note:  "_left" is a special case that cannot be used in combination with other values.  This drastically simplifies implementation issues.


*Design question: how does this interface work?  special cardtype?  special *input? Obviously, we don't want users having to type "_left" or "_creator".  Needs to be some sort of dropdown.  More on this later...  Also, what is the value for comment = nobody?

Zero, or whatever will make it possible to use logic like "if foo+*comment then...".


Sets / Set Classes

In order to cover the existing permissions in relatively few settings, we need the following new sets (each of which requires its own new set class)

  • *plusses
  • *stars (refers to simple cards beginning with "*")


I think this is the new hierarchy:  *all, *plusses, *stars, *type.. The relative placement of the new two doesn't matter much, since they're mutually exclusive.


We may want to think more about the name for the set classes.  One the one hand, both *plusses and *plus cards are already in use.  On the other hand, they're both one-set cards like *all, and the simple cards aren't really in use now as anything but placeholders.  


Roles / Groups

As part of this upgrade, we can get rid of the roles extension and the special handling for roles/users.


Basically, I think a Role (henceforth a "Group") should basically just be a pointer.  Whoever has permissions to edit the role basically has permissions to manage the group (which gets rid of one more global permission.  see below).


Design issue: Editing the role directly is then straightforward.  The question is how best to edit it from the users' cards.  This is basically a matter of managing the pointer from the pointee.  There are lots of use cases for this, and there is some infrastructure in place already.  But this particular example needs to work really well, of course.


(John, I know you're going to say "Group" is too common of a name to grab.  I resent that, because it's true.  Let's talk).






For performance reasons, the card containing reader setting values will be optimized in the database as a "read_rule_id".




For any user, we need to come up with a list of all read_rule_ids that include that user. 


WQL Queries

So now this is straightforward.  You just substitute the current reader_id stuff with something similar -- cards where the read_rule_id is in the user's read_rule_ids list


Updating read_rule_ids

Whenever you add, remove, or rename a setting value, you need to update impacted cards. 



( Old terminology.  Still useful?) Note that if the setting value is _left, the rsvid is the rsvid of the left card, not the card with the content "_left".  Lew's pseudo code for this one:

card.reader_setting_value_id = follow_parent( c.setting(:read) ).card_id


The rsvid may need to be updated when:

  1. the card itself is saved and name or type is updated.  Simple on_save behavior
  2. the card's trunk's type is changed (oof). not so simple on_save behavior.
  3. a new setting value is created that overrides its current value. 
  4. the current setting value is changed in any of these ways:
    - either from or to "_left"
    - deleted
    - renamed



In case #3 above, what we do is first find all the distinct rule_ids for all the cards in the set of the new setting value.  Then we examine those rule_ids and update only the ones whose old values are more general than the new one.


In case #4, we follow something very much like the logic of #1, but we also need to update all the old cards.





1. migrate roles into cards (leaving the old roles in tact for now).  If the role card has some special content, then....what?


2. migrate in the new set and setting cards


3. migrate permissions (setting values).  for each permission, follow this pattern:

  1. find the most common setting for all simple cards.  make that your *all value.  comment is off.  
  2. set *plusses to _left (all permissions except comment)
  3. set *stars so that create, update, and delete are limited to administrator.  (read comes from *all)
  4. open a type loop.  for each type, find the most common permission value.  if this is different from what the value would be based on the above setting values, then set the type value.
  5. loop through the minority value cards by tag (right).  repeat the same logic.  If the setting card already exists and the value is different, then we'll need to figure out what the most common value over all for *right is and use that, relegating the minorities to the *self stack
  6. Any remaining cards that are still misfits need *self settings.
note: in the case of *read, the reader_key and reader_setting_value_id should be updated for all relevant cards as soon as the setting is created. probably ok to do this via bulk updates



4. Re-implement initial permissions in cards on en


5. get rid of the old permissions and roles tables.  (Don't actually implement this until the others are proven)



Next Steps


We could also treat this as an opportunity to get one step closer to getting rid of our global permissioning system.  As I see it, the only thing that needs to happen to get rid of it altogether is moving user information into cards.


These two will now be unnecessary  (and, actually, way way improved) thanks to the current refactor:

  1. assign user roles: this one's not really obvious either, but it feels very wrong to have one monolithic setting.
  2. set card permissions: that's accomplished by who can edit the above permissions cards
In fact, we should make sure to remove them.  The following three will no longer be necessary if we have all the account information in cards.
  1. administrate users:  this is about editing emails and passwords and blocking users, all of which will need to be handled in cards
  2. create accounts: this is about creating the same kinds of information in the first place (minus the blocking, I guess)
  3. set global permissions:  don't need it if there are no global permissions!






hoping this fixes permissions changing themselves

Migration is going to result in a *huge* number of edits on any decent-sized Wagn, some of which will expose errors. Probably good to give a big heads-up on this with lead-time for Wagn-owners, so that they know it's an opportunity to review permissions on their site and set things right.

  --John Abbe.....Mon May 10 15:19:23 -0700 2010

edits will only be huge on sites with irregular permissions settings, otherwise all of the permissions on the site will be represented by comparatively few cards.  in any case, reviewing and fixing permissions will be far far easier after the migration -- that's part of the point.

  --Ethan McCutchen.....Mon May 10 15:40:08 -0700 2010

oh, right! we'll be able to use WQL to show all the cards editable by a particular role, etc. Waaay cool...

  --John Abbe.....Mon May 10 16:13:11 -0700 2010

yes, though in some wagns you'll just want to go into the *create, *update cards, etc. Very quick summary view of the permissions of the whole site.

  --Ethan McCutchen.....Mon May 10 16:24:35 -0700 2010

This will satisfy some of my urge for color coding cards (e.g., based on permissions), though that would still be good for that among other uses.

  --John Abbe.....Mon May 10 18:27:06 -0700 2010

The number of tickets getting closed because they'll be handled by this ticket is yet another indication of how awesome this ticket -- and settings in general -- are. W00t!

  --John Abbe.....Wed May 12 19:48:41 -0700 2010

On add Author role you suggested we'd handle the use cases for that via syntax. I'm struggling to imagine what you meant?

  --John Abbe.....Mon Nov 22 09:51:56 -0800 2010

On *plusses and *stars:


Foo+*plusses would be the set of all cards Foo+ ? So that a *plusses setting on the _left part of a card is a setting on that card ?


Or it could be all cards with a plus, (or both '*plusses' is one set like *all, and +*plusses is a pattern). I guess *stars could work the same way, but just limit to tags beginning with * ?


Would we have a way of referring to the negation of these sets as well?

  --Gerry (Not signed in).....Thu Dec 09 13:14:45 -0800 2010

*plusses is just all the plus cards. foo+*plusses already has another meaning. We need this set to be able to make sure all plus cards, by default, inherit from _left.

  --Ethan McCutchen.....Thu Dec 09 13:38:58 -0800 2010

I want to comment on the id of a separate "owner" concept (somewhat related to add Author role). The notion there would be that a card would be set up so that a give permission (read, edit, delete) could be assigned to an owner. This would differ from the creator, in that the owner could change. This has been accompanied by the suggestion that there be an "owner_id" in the cards table.


With the system outlined above, we would be able to set up cards in patterned ways so the creator has a given permission, and we would be able to override that permission with a more narrow set that gives that permission to a different individual. So I don't think the distinction is in any given pattern of permissions that we might be able to represent. However, there may be an interface level argument for simplifying changing the owner versus changing the permission to point to a new person. If so, feel free to make it. I don't see it yet.


I do see an argument against simply adding an owner_id and then adding new interface to change it. This adds a whole lot of newness and uncardiness to support. There'd have to be custom interface, custom model and controller handling, custom permissions, custom WQL, etc.


Now, we could get around many of those if, say, we made a "+*owner" and just used owner_id as an optimization. If we decide to add the owner, I'd probably want to do something like this.


But it still seems like a lot of work for not a huge return. Can anyone make the case for the owner stuff?

  --Ethan McCutchen.....Tue Dec 14 12:24:51 -0800 2010

I take it you are talking about adding _owner which is used like _creator. What about making *owner a setting, then there is no new interface needed. Values would be just like the *CRUD permission settings, but would be the other side of the permission validation (i.e. if any item in the *owner setting can Create(RUD) then Create(RUD) is allowd),

  --Gerry Gleason.....Thu Feb 24 10:37:27 -0800 2011

I would think that would complicate the reader_key generation somewhat, no?

  --Ethan McCutchen.....Thu Feb 24 11:06:22 -0800 2011

Ethan: John, I know you're going to say "Group" is too common of a name to grab. I resent that, because it's true. Let's talk.


I think this is the use I wanted to reserve "Group" for, as long as it's the canonical way to identify collections of, hrm, people. (The hrm is I guess that's the union of User cards and cards with accounts.) Wow, we may want a way to manage long lists of Groups even before that becomes critical for Cardtypes.

  --John Abbe.....Thu Feb 24 11:22:10 -0800 2011

This is a pretty big chunk of coding already. Any reason not to push _owner off to a separate ticket?


The "interface level argument for simplifying" applies to all of the settings. improve interface for editing settings

  --John Abbe.....Thu Feb 24 11:26:26 -0800 2011

so far I'm not looking for an owner solution -- I'm looking for evidence of need. Until the case is made, I'd say it's an Idea. yay for the interface ticket (prolly medium)

  --Ethan McCutchen.....Thu Feb 24 11:29:16 -0800 2011

I don't understand the question about reader_key generation.

  --Gerry Gleason.....Mon Feb 28 04:02:07 -0800 2011

The above solution is highly reliant on the reader_key -- it's how we make this thing something that can perform in WQL. We can't process much permission logic at query time -- the reader_key solution addresses that.


My key response to the *owner idea is that coming up with ways to represent permissions in cards is the easy part -- thinking through a workable implementation is the hard part. So for it to be a complete proposal, we'd need to digest all the implications.


It's quite possible that the hairiest part of implementation will be the reader_key updates (which is really more my concern than the generation, which would still be more or less the same), as it's essentially a database-level caching system, which always has the potential to become, well, hirsute.


Eg, when I change the owner of a Set, it's not as simple as updating the reader_keys for every card in the set -- we have to check each one to make sure it's not overridden somewhere by a narrower set. This may not be a dramatic jump in complexity, because we basically have to do the same thing for all the other permissions settings, but it obviously adds *some* new complexity.


Some of this may just be my confusion. Just to understand what you've written, when you say this:


"Values would be just like the *CRUD permission settings, but would be the other side of the permission validation (i.e. if any item in the *owner setting can Create(RUD) then Create(RUD) is allowd)"


...I don't think I follow. Can you give me an example of what you mean?


By the way, I'm digging into this now because the use case may have emerged now :)

  --Ethan McCutchen.....Mon Feb 28 10:21:37 -0800 2011

Cool - what's the use case?

  --John Abbe.....Thu Mar 03 10:43:57 -0800 2011

re initial permissions, we'll just need to do the default case on en. We've only ever used something other than that once (for rcc), and administrating permissions will be easy enough to change it dynamically now.

  --Ethan McCutchen.....Thu Mar 03 11:37:41 -0800 2011

use case not totally clear yet. will post more if it gets clearer.

  --Ethan McCutchen.....Thu Mar 03 11:54:43 -0800 2011

In the part you quoted, I was just saying that if we used a *owner setting, then that "Setting Values" would be just like the setting values you describe in this ticket for *create ..., A role, card with account or special values. Only it is used differently. With the CRUD settings, you are matching the available Roles/Users, etc. to the setting for the actions, with *owner, you are looking up the setting for another one of the available Roles. I.e. the other side of the permission check.

  --Gerry Gleason.....Thu Mar 03 13:25:09 -0800 2011

don't get it. example?

  --Ethan McCutchen.....Thu Mar 03 15:00:53 -0800 2011

I don't follow the changes you just made to the solution, but given the gigantic cut in text, it can't help but be simpler. (=good :-)

  --John Abbe.....Thu Jun 16 02:32:55 -0700 2011