-
Notifications
You must be signed in to change notification settings - Fork 645
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -121,13 +121,13 @@ public async Task RemovePackageOwnerAsync(PackageRegistration packageRegistratio | |
using (var strategy = new SuspendDbExecutionStrategy()) | ||
using (var transaction = _entitiesContext.GetDatabase().BeginTransaction()) | ||
{ | ||
await RemovePackageOwnerImplAsync(packageRegistration, requestingOwner, ownerToBeRemoved); | ||
await RemovePackageOwnerImplAsync(packageRegistration, ownerToBeRemoved); | ||
transaction.Commit(); | ||
} | ||
} | ||
else | ||
{ | ||
await RemovePackageOwnerImplAsync(packageRegistration, requestingOwner, ownerToBeRemoved); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We verify that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we call |
||
await RemovePackageOwnerImplAsync(packageRegistration, ownerToBeRemoved); | ||
} | ||
|
||
await _auditingService.SaveAuditRecordAsync( | ||
|
@@ -139,38 +139,29 @@ await _auditingService.SaveAuditRecordAsync( | |
} | ||
} | ||
|
||
private async Task RemovePackageOwnerImplAsync(PackageRegistration packageRegistration, User requestingOwner, User ownerToBeRemoved) | ||
private async Task RemovePackageOwnerImplAsync(PackageRegistration packageRegistration, User ownerToBeRemoved) | ||
{ | ||
// 1. Remove this package registration from the namespaces owned by this user if he is the only package owner in the set of matching namespaces | ||
// 2. Remove the IsVerified flag from package registration if all the matching namespaces are owned by this user alone (no other package owner owns a matching namespace for this PR) | ||
var allMatchingNamespacesForRegistration = packageRegistration.ReservedNamespaces; | ||
if (allMatchingNamespacesForRegistration.Any()) | ||
// Remove the user from owners list of package registration | ||
await _packageService.RemovePackageOwnerAsync(packageRegistration, ownerToBeRemoved, commitChanges: false); | ||
|
||
// Remove this package registration from the namespaces owned by this user that are owned by no other package owners | ||
foreach (var reservedNamespace in packageRegistration.ReservedNamespaces.ToArray()) | ||
{ | ||
var allPackageOwners = packageRegistration.Owners; | ||
var matchingNamespacesOwnedByUser = allMatchingNamespacesForRegistration | ||
.Where(rn => rn.Owners.Any(o => o == ownerToBeRemoved)); | ||
var namespacesToModify = matchingNamespacesOwnedByUser | ||
.Where(rn => rn.Owners.Intersect(allPackageOwners).Count() == 1) | ||
.ToList(); | ||
|
||
if (namespacesToModify.Any()) | ||
if (!packageRegistration.Owners | ||
.Any(o => ActionsRequiringPermissions.AddPackageToReservedNamespace | ||
.CheckPermissionsOnBehalfOfAnyAccount(o, reservedNamespace) == PermissionsCheckResult.Allowed)) | ||
{ | ||
// The package will lose its 'IsVerified' flag if the user is the only package owner who owns all the namespaces that match this registration | ||
var shouldModifyIsVerified = allMatchingNamespacesForRegistration.Count() == namespacesToModify.Count(); | ||
if (shouldModifyIsVerified && packageRegistration.IsVerified) | ||
{ | ||
await _packageService.UpdatePackageVerifiedStatusAsync(new List<PackageRegistration> { packageRegistration }, isVerified: false); | ||
} | ||
|
||
namespacesToModify | ||
.ForEach(rn => _reservedNamespaceService.RemovePackageRegistrationFromNamespace(rn.Value, packageRegistration)); | ||
|
||
await _entitiesContext.SaveChangesAsync(); | ||
_reservedNamespaceService.RemovePackageRegistrationFromNamespace(reservedNamespace, packageRegistration); | ||
} | ||
} | ||
|
||
// Remove the user from owners list of package registration | ||
await _packageService.RemovePackageOwnerAsync(packageRegistration, ownerToBeRemoved); | ||
// Remove the IsVerified flag from package registration if all the matching namespaces are owned by this user alone (no other package owner owns a matching namespace for this PR) | ||
if (packageRegistration.IsVerified && !packageRegistration.ReservedNamespaces.Any()) | ||
{ | ||
await _packageService.UpdatePackageVerifiedStatusAsync(new List<PackageRegistration> { packageRegistration }, isVerified: false, commitChanges: false); | ||
} | ||
|
||
await _entitiesContext.SaveChangesAsync(); | ||
} | ||
|
||
public async Task DeletePackageOwnershipRequestAsync(PackageRegistration packageRegistration, User newOwner) | ||
|
@@ -191,31 +182,21 @@ public async Task DeletePackageOwnershipRequestAsync(PackageRegistration package | |
await _packageOwnerRequestService.DeletePackageOwnershipRequest(request); | ||
} | ||
} | ||
|
||
// The requesting owner can remove other owner only if | ||
// 1. Is an admin. | ||
// 2. Owns a namespace. | ||
// 3. Or the other user also does not own a namespace. | ||
|
||
private static bool OwnerHasPermissionsToRemove(User requestingOwner, User ownerToBeRemoved, PackageRegistration packageRegistration) | ||
{ | ||
if (requestingOwner.IsAdministrator) | ||
var reservedNamespaces = packageRegistration.ReservedNamespaces.ToList(); | ||
if (ActionsRequiringPermissions.AddPackageToReservedNamespace | ||
.CheckPermissionsOnBehalfOfAnyAccount(ownerToBeRemoved, reservedNamespaces) == PermissionsCheckResult.Allowed) | ||
{ | ||
return true; | ||
// If the owner to be removed owns a reserved namespace that applies to this package, | ||
// the requesting user must own a reserved namespace that applies to this package or be a site admin. | ||
return ActionsRequiringPermissions.RemovePackageFromReservedNamespace | ||
.CheckPermissionsOnBehalfOfAnyAccount(requestingOwner, reservedNamespaces) == PermissionsCheckResult.Allowed; | ||
} | ||
|
||
var requestingOwnerOwnsNamespace = IsUserAnOwnerOfPackageNamespace(packageRegistration, requestingOwner); | ||
if (requestingOwnerOwnsNamespace) | ||
{ | ||
return true; | ||
} | ||
|
||
var ownerToBeRemovedOwnsNamespace = IsUserAnOwnerOfPackageNamespace(packageRegistration, ownerToBeRemoved); | ||
return !ownerToBeRemovedOwnsNamespace; | ||
} | ||
|
||
private static bool IsUserAnOwnerOfPackageNamespace(PackageRegistration packageRegistration, User user) | ||
{ | ||
return packageRegistration.ReservedNamespaces.Any(rn => rn.Owners.Any(owner => owner == user)); | ||
// If the owner to be removed does not own any reserved namespaces that apply to this package, they can be removed by anyone. | ||
return true; | ||
} | ||
} | ||
} |
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 there another way around this? Would prefer to avoid unnecessary allocations.
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.
no, this system expects an array of reserved namespaces so we have to convert a single namespace into an array