Skip to content
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

Allow organization admins to remove their organization's ownership of a package in a reserved namespace #5984

Merged
merged 5 commits into from
May 23, 2018

Conversation

scottbommarito
Copy link
Contributor

#5944

1 - Organization A owns package B that is in reserved namespace C
2 - User D that is admin of A removes A from ownership of B

Expected:
Organization A is no longer an owner of B and B loses its verification

Actual:
D gets an HTTP 500.
Organization A remains an owner of B.

This PR fixes that behavior.

Also made some general fixes to prevent issues like these in the future.
E.g. we now use ActionsRequiringPermissions to determine reserved namespace package verification.

@@ -15,7 +15,7 @@ namespace NuGetGallery
/// E.g. "JQuery.Extensions.MyCoolExtension" matches both "JQuery.*" and "JQuery.Extensions.*".
/// </remarks>
public class ActionRequiringReservedNamespacePermissions
: ActionRequiringEntityPermissions<IReadOnlyCollection<ReservedNamespace>>, IActionRequiringEntityPermissions<ActionOnNewPackageContext>
: ActionRequiringEntityPermissions<IReadOnlyCollection<ReservedNamespace>>, IActionRequiringEntityPermissions<ActionOnNewPackageContext>, IActionRequiringEntityPermissions<ReservedNamespace>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: wrap long lines; makes it easier to review


private IReadOnlyCollection<ReservedNamespace> GetReservedNamespaces(ReservedNamespace reservedNamespace)
{
return new[] { reservedNamespace };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there another way around this? Would prefer to avoid unnecessary allocations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, this system expects an array of reserved namespaces so we have to convert a single namespace into an array

/// </summary>
public static ActionRequiringReservedNamespacePermissions ShowPackageAsVerifiedByReservedNamespace =
new ActionRequiringReservedNamespacePermissions(
accountOnBehalfOfPermissionsRequirement: PermissionsRequirement.Owner,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance, this readonly permission seemed more restrictive than the Remove permission below... but seems like that's because we only invoke this on package owners. Maybe it's worth calling this out in the comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, this action is

If I own a package, I can show that package as verified by a reserved namespace that applies to it.

The reserved namespace must be owned by you and exactly you (you cannot show your package as verified on behalf of another user that owns a reserved namespace that applies to it), so the permissions requirements here are both Owner. It's a little convoluted but by using ActionsRequiringPermissions for this purpose we shouldn't have issues similar to what we saw here in the future.

transaction.Commit();
}
}
else
{
await RemovePackageOwnerImplAsync(packageRegistration, requestingOwner, ownerToBeRemoved);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We verify that requestingOwner has permissions for ownerToBeRemoved (organization) higher in the stack?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we call OwnerHasPermissionsToRemove earlier which does the verification.

{
return packageRegistration.ReservedNamespaces.Any(rn => rn.Owners.Any(owner => owner == user));
// If multiple package owners own a reserved namespace that the package is a member of, then removing an owner will not remove the package from all of its reserved namespaces.
// If no package owners own a reserved namespace that the package is a member of, then the package is not in a reserved namespace and the owner can be removed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is confusing, essentially if there are no reserved namespaces associated with a package, then the owner can be removed would be the simplest way to state it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is trying to explain how the number of package owners owning a reserving namespace is related to the package being in a reserved namespace...I think your comment removes this relationship. I'm not sure how to make the connection and also not have a confusing comment here.

return packageRegistration.ReservedNamespaces.Any(rn => rn.Owners.Any(owner => owner == user));
// If multiple package owners own a reserved namespace that the package is a member of, then removing an owner will not remove the package from all of its reserved namespaces.
// If no package owners own a reserved namespace that the package is a member of, then the package is not in a reserved namespace and the owner can be removed.
return packageOwnersThatOwnAReservedNamespaceThatThePackageIsAMemberOf != 1;
Copy link
Contributor

@shishirx34 shishirx34 May 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method is missing a case, you aren't checking the namespace ownership of ownerToBeRemoved, if the requestingOwner doesn't own the namespace, and neither does the ownerToBeRemoved, and there two other owners who do own the namespace, this method will return false; which it shouldn't.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep it simple like the previous way, and check for the ownerships(of course with OnBehalfOf) of both owners to derive logical conclusion on permission for removal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally correct. The reason why this wasn't broken in my unit tests was because of the != (instead of a ==) in line 203. I have fixed and simplified the logic.

Copy link
Contributor

@shishirx34 shishirx34 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are some missing checks for owner removal.

/// <summary>
/// The action of showing a package as verified by a reserved namespace.
/// </summary>
public static ActionRequiringReservedNamespacePermissions ShowPackageAsVerifiedByReservedNamespace =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a misnomer for the action. Rather rename it as IsOwnerOfReservedNamespace or a better action that represents ownership of namespace than the ShowPackageAsVerified, its not really the application of this action in the code below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants