-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add project.leave() #410
Conversation
bb0743d
to
92967a7
Compare
@gmaclennan do you mind taking a look at what's currently implemented here and let me know what glaring omissions there are? mostly have uncertainties around the steps that need to happen within the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach is looking correct to me. However we don't currently have a way to assign a role, so it's not exactly working as expected - see #413. Initial fix in #414 but that just causes project.$leave() to throw right now.
This also raises questions for me about the implementation of Capabilities. The idea is that DEFAULT_CAPABILITIES will eventually move into the database, and can be edited by users. I think there are two separate types of roles:
- "invitable roles" - e.g. roles that someone with the right capability can assign to a new device in a project. These should also be the only roles that can be assigned with
$member.assignRole()
. - "special roles" used to implement internal details of the capability system - "creator", "blocked device" and "left of own choice". I think those assignments should only be used internally, e.g. we should implement a
$member.block()
method, and we have thisproject.$leave()
method.
I think we should implement a capabilities.getRoles()
function or similar which currently returns the corodinator and member roles, but in the future will read from the database. We can then use this as an assertion in assignRole().
I think maybe we give the "leave" role special treatment - any role can assign the leave role to themselves, apart from a device with the blocked role - hence blocked should be treated as a "special role" with slightly different rules.
I think this PR is blocked for now by #413 which we should resolve first.
92967a7
to
3adf2a7
Compare
3adf2a7
to
6b6ae3f
Compare
6b6ae3f
to
184320e
Compare
@gmaclennan do you mind taking another look at the changes here and providing input on what else is needed? Not sure what from #410 (review) should be done separately, and not sure if the TODOs I have in the changes diff reflect what's needed |
src/capabilities.js
Outdated
// TODO: Does this make sense? | ||
roleAssignment: [LEFT_ROLE_ID], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I'm looking at this, the more it seems that this change isn't necessary (i.e. NO_ROLE being able to assign LEFT), but would like confirmation before undoing
[LEFT_ROLE_ID]: { | ||
name: 'Left', | ||
docs: mapObject(currentSchemaVersions, (key) => { | ||
return [ | ||
key, | ||
{ | ||
readOwn: false, | ||
writeOwn: false, | ||
readOthers: false, | ||
writeOthers: false, | ||
}, | ||
] | ||
}), | ||
roleAssignment: [], | ||
sync: { | ||
auth: 'allowed', | ||
config: 'blocked', | ||
data: 'blocked', | ||
blobIndex: 'blocked', | ||
blob: 'blocked', | ||
}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this an accurate representation for this role?
src/mapeo-project.js
Outdated
this.#sharedDb | ||
.delete(projectSettingsTable) | ||
.where(eq(projectSettingsTable.docId, this.#projectId)) | ||
.run() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense but not entirely sure. My assumption is that once you leave a project, you shouldn't be able to access this kind of information, but maybe that's too strict in this case?
src/mapeo-project.js
Outdated
) | ||
) | ||
|
||
// 3. Clear data from project database |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't implemented yet because I have questions about how this should be done. Is it just a naive db.delete(...)
? (where db = new Database(this.#sqlite)
)
Wondering if the deletion of data in sqlite should be handled by something like CoreManager.deleteData()
? A little bit tangled with the different boundaries and what should be responsible for this 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, I think the behaviour is all correct, although there are a couple of changes that could be made to make it a bit less fragile and also a possible structure change:
- Comparing roles based on the
capability.name
property is fragile if we start storing these records in the database (instead of hard-coding them) then we need to remember to never allow the name to change. Listing role records I think is a more robust way of doing what you want (I think that would work, but haven't tried it myself). - Possible change to whether we keep
LEFT_ROLE_ID
in theroleAssignment
array, or treat it as a special case. I think it's fine as you have it, but we should document somewhere to make sure it is always included inroleAssignment
if we use this approach. - Looking at all the parameters that now need to be passed to a project instance, I wonder whether it makes sense to split
project.$leave
into two methods? A privateproject[kleave]()
method andmanager.leaveProject(projectId)
. I think it's fine as it is, I think just that adding all these other parameters that a project instance must know about feels not quite right to me, but not sure if that's just aesthetics or there is a concrete reason for that.
I think the only thing missing is deleting the indexed data in the sqliteDb, but maybe that can be in a follow-up PR so that we can get this completed and merged?
src/mapeo-project.js
Outdated
} | ||
|
||
if ( | ||
deviceIdToCaps[deviceId].name === |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing this comparison on .name
seems fragile - we would need to ensure that we never change .name
in the codebase, or things would break.
I think what we need here is to do dataTypes.role.getAll()
, which will return an array of records where docId
=== deviceId
and you can read the roleId
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes, makes sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is getting trickier than expected. feels like I'm doing some logic that's done by Capabilities.getAll()
in order to account for edge cases 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
never mind, I figured it out 😄 addressed via 5fab0af
src/capabilities.js
Outdated
@@ -254,6 +288,10 @@ export class Capabilities { | |||
* @param {keyof typeof DEFAULT_CAPABILITIES} roleId | |||
*/ | |||
async assignRole(deviceId, roleId) { | |||
if (deviceId !== this.#ownDeviceId && roleId === LEFT_ROLE_ID) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's ok to do it this way, although it does mean that we need to make it a hard requirement that all capability records can assign this (although there is the open question of whether a blocked user can also leave - they wouldn't be able to leave for other reasons, because they would not be able to write to the project any more).
An alternative approach would be to not include LEFT_ROLE_ID
in the roleAssignments
property and instead add this check to line 318 (if (!ownCapabilities.roleAssignment.includes(roleId)) {
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed via ca03ef3
Good to merge and address anything else in follow up
Closes #241
TODO: