Add support for mail grantees - #785
Conversation
rhafer
left a comment
There was a problem hiding this comment.
Here's some findings that a quick review with the help of opencode revealed. I haven't looked into them a depth. But I think they're mostly valid:
- High: pkg/storage/pkg/decomposedfs/spaces.go:897 indexes guest spaces by mail, but ListStorageSpaces only reads userSpaceIndex at line 348. Guests cannot discover granted project spaces.
- Medium: Guest emails are lowercased during persistence in pkg/storage/utils/ace/ace.go:220, but lookup and deletion use original casing at grants.go:330 and node/node.go:1315. Mixed-case guest grants cannot be updated or removed.
- Medium: Space purge removes user grants only from userSpaceIndex at spaces.go:792, leaving stale guest entries in mailSpaceIndex.
e45f5a2 to
46baf62
Compare
|
rebased once more. And made the lowercase normalization for a bit more explicit just for mail grants (Usertype Guest). To me this looks good now. Unfortunately I can't approve since I created the PR, even though most of the code is originally from @aduffeck . |
9c07130 to
d50640b
Compare
|
The more I look into this the more I think that this is not ready for prime time yet. I guess I wanted to rush this through a little too fast, after it has been sitting idle for so long. After some discussion and more code review, my main concerns are around using the mail address as the value of the OpaqueId in the cs3 UserId and the consequences that this has for the different places, where we persist the id in the filesystem:
Mail addresses, especially the local-part, can contain all kinds of weird characters. E.g. Each of the above places has it's own restriction about what a valid value for a directory-, file- or extended attribute name can be. So we need to ensure that we won't break stuff. There are some options:
To not completely stall efforts on the mail-invites, and as I think the above should have no impact on the user-facing (graph) API for now, I'd suggest to do the following with this PR:
@pbleser-oc @micbar @maki5 Opinions? |
How about introducing the concept of anonymous user, which have restricted access but still has the same structure as normal user, means has an real uuid and email. This might be a bigger change but in this case I assume we will not need to change the whole storage and handling logic. Maybe a new role for this kind of users with access to just shared with it resource will potentially solve the issue. |
Yes, absolutely, because of how/where it is written on disk. In theory we could even use different strategies for different use-cases:
Those are just examples to highlight that the decision to encode, and how to encode, is really up to the functions/layers that actually store those email addresses.
If it's opaque, then it's up to the storage level to encode it, as it's only the concern of the storage that
I wouldn't put that on the caller, it's not their concern/problem/business. One point though: are those opaque values always email addresses? or are the some sort of IDs, and in the case of guest shares we use an email address? Point being:
Those might be practical reasons for putting the burden of the encoding on the callers, since only they would know that it's an email address in the first place. (Making a lot of assumptions here, I'm not familiar with that codebase.)
That sounds like it should be the least good option, because it will require having an additional form of storage that will persist those mappings, and we don't know when they will be eligible for removal, etc... If we want to do that without a storage, we need a derivation function that works in both directions, like... an encoding :) and we're back to base64 :) In any case it should be implemented as a single strategy, in a single set of functions to encode/decode that are then used everywhere, and then we can still change it in the future (albeit requiring migrations).
We should definitely pick the appropriate encoding solution before putting it into main as we don't want to have to implement a migration as well. |
The latter, in case of guest shares they're mail addresses, for "normal" user shares they're usually UUIDs in our type of setups.
There are some parts e.g. in the lower levels of the share manager where we don't know anymore that is is an mail address. It might be possible to change that without much effort, haven't checked in detail yet.
Yes, that is something I'd like to avoid.
Still very helpful for me though 🙏.
Agreed. I was just trying to get some ideas of my head.
👍 |
That is a bit unfortunate, because that leaks requirements on those IDs that are passed down to the lowest level that are probably not documented, and can't really be enforced by the compiler either since they're just of type Those requirements being implementation-dependent, like being safe to be used as a filename. Possibly a hellish refactoring to do at this point (not sure how much code would be affected), but one approach would be to
It would still make the "must be usable as a filename" concern bleed out upwards from the low-level storage layer, but at least it would make that requirement a lot clearer, and the compiler will help us enforcing it to some degree. |
Looking at the current code at least in the ShareManager, it doesn't seem to be too hard to adjust the places where the userid is passed as a generic |
723c7ce to
77fa410
Compare
|
Suggested approach, similar to what @pbleser-oc I think: Introduce a small interface with a single method that returns a filename safe string: Create a local type, wrapping Change the methods in the jsoncs3 sharemanager that now get the user id value passed as a The new type and interface should also be used by the storage provider to encode the names of the extended attributes Implementation is WIP, was distracted by other stuff. |
77fa410 to
302ad87
Compare
Move the ACE prefix constants to the ace package and get rid of hard coded prefixes spread throughout the code.
Introduce a helper for getting a canonical representation for a UserId. Currently this is mainly relevant for the USER_TYPE_GUEST users which user a mail address as the UserId. Also adapts the existing UserIDEqual() helper to make use of the new function.
As the cache keys end up as parts of file names the access methods for the caches now accept the new "FilenameEncoder" interface. The interface implements a single method "SafeFilename()". The new wrapper types FSSafeUserId and FSSafeGroupId implement that interface for the cs3 UserId and GroupId Types. Currently only UserIds of the type GUEST are actually encoded (simple base64 encoding) all other IDs are assumed to be safe filenames. Similar to the share manager caches we now encode the IDs for mail user invites when using them as names for attributes in the ACE string or as file/directory names for the space indexes.
302ad87 to
b3bdf9c
Compare
|
I've reworked the share manager and the decomposedfs ACE persistence to base64-wrap guest uids. Also rebased to latest main. @maki5 @pbleser-oc I would appreciate another round of review. (The interesting bits are in the last 2 commits, I'd say) |
pbleser-oc
left a comment
There was a problem hiding this comment.
Looks good, thanks for the improvements.
| } else if e.granteeType() == provider.GranteeType_GRANTEE_TYPE_USER { | ||
| g.Grantee.Id = &provider.Grantee_UserId{UserId: &userpb.UserId{OpaqueId: id}} | ||
| if strings.HasPrefix(e.principal, MailAcePrefix) { | ||
| userID, _ := (utils.FSSafeUserID{ID: &userpb.UserId{Type: userpb.UserType_USER_TYPE_GUEST}}).Decode(id) |
There was a problem hiding this comment.
we are ignoring err here, might be good to handle it
There was a problem hiding this comment.
Hm, you're right about ignoring the error.
Though the method we're in is only called for ACE values that have been created by Unmarshal which does actually error out on invalid base64.
I am going to rework this to avoid the double decoding, which will also get us rid of the ignored error check here.
There was a problem hiding this comment.
I am going to rework this to avoid the double decoding, which will also get us rid of the ignored error check here.
Hm, looking at this again. I think this would make the code worse.
ACE are only generate by FromGrant or Unmarshal. FromGrant already ensure valid base64, and Unmarshal rejects invalid base64 (see: line 281).
I'll add a clarifying comment why we ignore the error.
maki5
left a comment
There was a problem hiding this comment.
overall looks good, just a couple of small mentions
|
|
||
| // CanonicalUserID returns the stable representation of a UserId as e.g. used for user storage keys. | ||
| // Currently this is only relevant for UserId of the USER_TYPE_GUEST, which are matched case-insensitively. | ||
| func CanonicalUserID(id *userpb.UserId) string { |
There was a problem hiding this comment.
this method is pretty similar to func (id FSSafeUserID) SafeFilename() string, maybe it's worth to combine them and to use just one method instead
There was a problem hiding this comment.
Hm, I think it's cleaner to keep them separate. CanonicalUserID is mainly for comparison/equality check, while SafeFilename() is about encoding the ID so it can be safely used for filesystem names.
|
|
||
| func UserAce(id *userpb.UserId) string { | ||
| return "u:" + id.OpaqueId | ||
| filename := (utils.FSSafeUserID{ID: id}).SafeFilename() |
There was a problem hiding this comment.
maybe it's better to have a constructor in utils to not initialise the struct here directly, there are some other places where this kind of approach is used
There was a problem hiding this comment.
Yep. Makes sense. I'll change that.
Co-authored-by: Pascal Bleser <p.bleser@opencloud.eu>
106d13b to
0026eef
Compare
Also makes the "id" struct field private.
|
Merging after a final chat with @maki5 about the latest adjustments. |
revived and rebased #616 on top of current master
This PR implements sharing with email addresses as per opencloud-eu/opencloud#2516