Skip to content

Commit

Permalink
Showing 3 changed files with 43 additions and 11 deletions.
37 changes: 35 additions & 2 deletions src/NuGetGallery.Core/Entities/Organization.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

namespace NuGetGallery
{
@@ -15,14 +18,18 @@ namespace NuGetGallery
/// <see href="https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt" />
public class Organization : User
{
public Organization() : base()
public Organization() : this(null)
{
Members = new List<Membership>();
}

public Organization(string name) : base(name)
{
Members = new List<Membership>();

_administrators = new Lazy<IEnumerable<User>>(
() => Members.Where(m => m.IsAdmin).Select(m => m.Member).ToList());
_collaborators = new Lazy<IEnumerable<User>>(
() => Members.Where(m => !m.IsAdmin).Select(m => m.Member).ToList());
}

/// <summary>
@@ -34,5 +41,31 @@ public Organization(string name) : base(name)
/// Requests to become a member of this <see cref="Organization"/>.
/// </summary>
public virtual ICollection<MembershipRequest> MemberRequests { get; set; }

#region per-request query cache

private Lazy<IEnumerable<User>> _administrators;
private Lazy<IEnumerable<User>> _collaborators;

[NotMapped]
public IEnumerable<User> Administrators
{
get
{
return _administrators.Value;
}
}

[NotMapped]
public IEnumerable<User> Collaborators
{
get
{
return _collaborators.Value;
}
}

#endregion

}
}
13 changes: 6 additions & 7 deletions src/NuGetGallery/Helpers/PermissionsHelpers.cs
Original file line number Diff line number Diff line change
@@ -114,20 +114,19 @@ private static bool IsRequirementSatisfied(PermissionsRequirement permissionsReq
return true;
}

var matchingMembers = entityOwners
.OfType<Organization>()
.SelectMany(o => o.Members)
.Where(m => isUserMatch(m.Member))
.ToList();
var entityOrganizationOwners = entityOwners
.OfType<Organization>();

// use cached Administrators collection to avoid querying members directly
if (WouldSatisfy(PermissionsRequirement.OrganizationAdmin, permissionsRequirement) &&
matchingMembers.Any(m => m.IsAdmin))
entityOrganizationOwners.Any(o => o.Administrators.Any(m => isUserMatch(m))))
{
return true;
}

// use cached Collaborators collection to avoid querying members directly
if (WouldSatisfy(PermissionsRequirement.OrganizationCollaborator, permissionsRequirement) &&
matchingMembers.Any(m => !m.IsAdmin))
entityOrganizationOwners.Any(o => o.Collaborators.Any(m => isUserMatch(m))))
{
return true;
}
4 changes: 2 additions & 2 deletions src/NuGetGallery/Services/UserService.cs
Original file line number Diff line number Diff line change
@@ -109,7 +109,7 @@ public async Task<Membership> UpdateMemberAsync(Organization organization, strin
if (membership.IsAdmin != isAdmin)
{
// block removal of last admin
if (membership.IsAdmin && organization.Members.Count(m => m.IsAdmin) == 1)
if (membership.IsAdmin && organization.Administrators.Count() == 1)
{
throw new EntityException(string.Format(CultureInfo.CurrentCulture,
Strings.UpdateOrDeleteMember_CannotRemoveLastAdmin, memberName));
@@ -134,7 +134,7 @@ public async Task DeleteMemberAsync(Organization organization, string memberName
}

// block removal of last admin
if (membership.IsAdmin && organization.Members.Count(m => m.IsAdmin) == 1)
if (membership.IsAdmin && organization.Administrators.Count() == 1)
{
throw new EntityException(string.Format(CultureInfo.CurrentCulture,
Strings.UpdateOrDeleteMember_CannotRemoveLastAdmin, memberName));

0 comments on commit a194a7f

Please sign in to comment.