Add FileDiffACLsHook to allow people to hook up repository ACLs.
Review Request #11857 — Created Oct. 18, 2021 and submitted
One common request we've had over the years is to mirror repository
access control into Review Board. This is complicated by a few factors.
Review Board has always used a single account to access the remote
repository, and it caches aggressively to provide reasonable interactive
performance, so we can't just rely on the repository access directly. In
addition, it's not uncommon for repository users/usernames to not match
up with Review Board users/usernames (especially when repositories are
hosted on SaaS services).In order to make this possible, we're adding a new extension hook,
FileDiffACLsHook
. This hook is invoked when a user attempts to access
a review request, and is run for eachDiffSet
/FileDiff
. The result
of this is cached perDiffSet
to keep reasonable performance overall.
People can then create an extension implementing this hook to do
whatever access control they want, whether that's querying the
repository or referencing external config files.
- Implemented a sample extension that queried
p4 protect
information
to do access control for Perforce repositories. This implementation is
visible in the documentation for the hook. - Ran unit tests.
Summary | ID |
---|---|
e6f74bb9a28c096decad5617047ee89accd39625 |
Description | From | Last Updated |
---|---|---|
"hosting" services. |
chipx86 | |
Each of these will need to be full reference paths, or Sphinx will fail to map and will probably emit … |
chipx86 | |
False or True? |
chipx86 | |
Can we require **kwargs (or document it at least) to make future expansion possible without breaking things? |
chipx86 | |
In 4.0 (maybe late 3.0.x?), we added scmtool_id, which is more stable and will be needed for registries. This should … |
chipx86 | |
Two blank lines. |
chipx86 | |
Can you place this alphabetically in the hook list? |
chipx86 | |
As above, let's add **kwargs to any hook methods. |
chipx86 | |
The pattern we use for similar hooks is to return a boolean if we have an explicit result, or None … |
chipx86 | |
Shouldn't this be checking results? |
chipx86 | |
Blank line between these. |
chipx86 | |
Leftover debugging? |
chipx86 | |
I saw the comment about letting the hook just take a diffset (I think that's maybe a worthwhile change — … |
chipx86 | |
This is a little difficult to read. How about returning the result as a variable, and then checking the variable? |
chipx86 | |
E712 comparison to False should be 'if cond is False:' or 'if not cond:' |
reviewbot | |
Blank line between these. |
chipx86 | |
Should that be extension: "%s" or extension "%s"? I see that the ReviewRequestApprovalHook version has the former, but I wonder … |
chipx86 | |
Just to check (and we should explicitly document this everywhere): Do all hooks need to say either True or None … |
chipx86 | |
Can we have unit tests that test through the hook mechanism? I feel we'd want to ensure that, for N … |
chipx86 | |
E501 line too long (80 > 79 characters) |
reviewbot | |
F841 local variable 'diffset' is assigned to but never used |
reviewbot | |
Private functions should go after public functions. |
chipx86 | |
Private functions should go after public functions. |
chipx86 | |
This function is missing an explicit return at the end. That'd result in a None, but docs say to expect … |
chipx86 | |
I think the only thing left I'd want to see is a change to this paragraph to hammer in the … |
chipx86 |
-
-
As written, the
is_accessible
method will be called once for each filediff in each diffset, per the code in review_request.py:for filediff in diffset.files.all(): ic(filediff) for hook in FileDiffACLHook.hooks: try: if not hook.is_accessible(diffset, filediff, user):
I think it would be more flexible, though, if the hook were called once per diffset instead.
For a Perforce example, suppose a company organizes its ACLs based on the first 3 levels in the depotFile path:
//<depot>/<project>/<branch>/...
That is, within a given depot, project, and branch, if a user has access to one file, they have access to all files.
And suppose that a review has a fileset containing 100 filediffs:
//depot/foo/main/f00.c //depot/foo/main/f01.c ... //depot/foo/main/f98.c //depot/foo/main/f99.c
In this case, the hook function would only need to test a single depot path, //depot/foo/main/..., with "p4 protects" instead of testing all 100 paths to get the result.
And in cases where there is no existing ACL pattern that can be used to optimize the tests, the hook method can still iterate over the filediffs in the diffset.
-
-
-
Each of these will need to be full reference paths, or Sphinx will fail to map and will probably emit warnings.
-
-
Can we require
**kwargs
(or document it at least) to make future expansion possible without breaking things? -
In 4.0 (maybe late 3.0.x?), we added
scmtool_id
, which is more stable and will be needed for registries. This should check that and compare against'perforce'
. -
-
-
-
The pattern we use for similar hooks is to return a boolean if we have an explicit result, or
None
if we don't. Can we stick to that here? Make the "I don't know, someone else check" result just beNone
? -
-
-
-
I saw the comment about letting the hook just take a diffset (I think that's maybe a worthwhile change — will discuss in that comment), so this comment depends on the outcome of that a bit, but...
We should avoid inline functions here. The function gets redefined on every call to the parent function. I think we should instead just move this out into the class.
-
This is a little difficult to read. How about returning the result as a variable, and then checking the variable?
- Commits:
-
Summary ID 548cbf935825de3c7605e49c2a629d0852288bf1 6762b040281c2d3ebec676acf9d9f726b14ab642 - Diff:
-
Revision 2 (+452 -6)
- Commits:
-
Summary ID 6762b040281c2d3ebec676acf9d9f726b14ab642 b7ec9fed7f520bbff7b878d719bd5dbfeb8f7615 - Diff:
-
Revision 3 (+452 -6)
Checks run (2 succeeded)
-
The design looks sound to me. Just a few comments.
-
-
Should that be
extension: "%s"
orextension "%s"
?I see that the
ReviewRequestApprovalHook
version has the former, but I wonder if that was a typo back then. -
Just to check (and we should explicitly document this everywhere): Do all hooks need to say either
True
orNone
for the result to beTrue
, or do we want to allow anyTrue
to win?ReviewRequestApprovalHook
chains results, so that a hook can do effectivelyif prev_approved and ...
. We don't have that design for this hook (should we?), so there isn't a precedent here.I don't honestly know what the right design is, so this is really just a question and a request to make it crystal-clear in docs/comments, specifically describing what happens with multiple hooks (important to note that hooks can be called in any order).
-
Can we have unit tests that test through the hook mechanism? I feel we'd want to ensure that, for N hooks (maybe 2, 3) that we get the right results for a combination of
True
,False
, andNone
results.
- Commits:
-
Summary ID b7ec9fed7f520bbff7b878d719bd5dbfeb8f7615 a3ddb46130e2572e42441b4738aef65ff88f5a8c - Diff:
-
Revision 4 (+606 -8)
- Commits:
-
Summary ID a3ddb46130e2572e42441b4738aef65ff88f5a8c a64a009aa6f200b73d3f0ee986c0d84e5ce7420e - Diff:
-
Revision 5 (+606 -8)
Checks run (2 succeeded)
- Change Summary:
-
Use SpyOpReturn instead of call_fake
- Commits:
-
Summary ID a64a009aa6f200b73d3f0ee986c0d84e5ce7420e e3fc11341bfc6b329c483198adafbee27b0f1616 - Diff:
-
Revision 6 (+596 -8)
Checks run (2 succeeded)
- Commits:
-
Summary ID e3fc11341bfc6b329c483198adafbee27b0f1616 e6f74bb9a28c096decad5617047ee89accd39625 - Diff:
-
Revision 7 (+600 -8)
Checks run (2 succeeded)
-
-
I think the only thing left I'd want to see is a change to this paragraph to hammer in the expectations of the implementation. Right now, the requirements are all in one big paragraph, and I just know people are going to mentally skip something.
Can we make the "If the user does not have ..." text onward each its own bullet point, like:
The hook is expected to return: * ``True`` if the user does have access to all the files. * ``False`` if the user does not have access to one or more files. * ``None`` if the hook did not check the diff (for example, it might only check a single SCM or hosting service type, and ignore any others) If any hook returns ``False``, the user will not have access to the diff.