setting-ize permissions+solution
Wagneering
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).
Implementation
@card.read_rule_id
For performance reasons, the card containing reader setting values will be optimized in the database as a "read_rule_id".
@user.read_rule_ids
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:
- the card itself is saved and name or type is updated. Simple on_save behavior
- the card's trunk's type is changed (oof). not so simple on_save behavior.
- a new setting value is created that overrides its current value.
- 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.
Migrations
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:
- find the most common setting for all simple cards. make that your *all value. comment is off.
- set *plusses to _left (all permissions except comment)
- set *stars so that create, update, and delete are limited to administrator. (read comes from *all)
- 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.
- 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
- Any remaining cards that are still misfits need *self settings.
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:
- assign user roles: this one's not really obvious either, but it feels very wrong to have one monolithic setting.
- set card permissions: that's accomplished by who can edit the above permissions cards
- administrate users: this is about editing emails and passwords and blocking users, all of which will need to be handled in cards
- create accounts: this is about creating the same kinds of information in the first place (minus the blocking, I guess)
- set global permissions: don't need it if there are no global permissions!