Fix/cache by role exception (#8574)

This commit is contained in:
Matteo Piovanelli 2022-07-08 16:52:30 +02:00 committed by GitHub
parent f2ec2a70cd
commit 63cfe7babe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -24,6 +24,7 @@ namespace Orchard.OutputCache.Filters {
IRepository<RoleRecord> roleRepo, IRepository<RoleRecord> roleRepo,
IRepository<RolesPermissionsRecord> rolesPermissionsRepo, IRepository<RolesPermissionsRecord> rolesPermissionsRepo,
IRepository<PermissionRecord> permissionRepo) { IRepository<PermissionRecord> permissionRepo) {
_authenticationService = authenticationService; _authenticationService = authenticationService;
_authorizer = authorizer; _authorizer = authorizer;
_userRolesRepo = userRolesRepo; _userRolesRepo = userRolesRepo;
@ -33,6 +34,8 @@ namespace Orchard.OutputCache.Filters {
} }
public void KeyGenerated(StringBuilder key) { public void KeyGenerated(StringBuilder key) {
// Can the queries in this method be optimized away so that their results can be memorized
// at least within the scope of a request?
List<UserPermission> userRolesPermissions = new List<UserPermission>(); List<UserPermission> userRolesPermissions = new List<UserPermission>();
IQueryable<UserPermission> userRolesPermissionsQuery = Enumerable.Empty<UserPermission>().AsQueryable(); IQueryable<UserPermission> userRolesPermissionsQuery = Enumerable.Empty<UserPermission>().AsQueryable();
IQueryable<UserPermission> permissionsQuery = Enumerable.Empty<UserPermission>().AsQueryable(); IQueryable<UserPermission> permissionsQuery = Enumerable.Empty<UserPermission>().AsQueryable();
@ -41,21 +44,30 @@ namespace Orchard.OutputCache.Filters {
if (currentUser != null) { if (currentUser != null) {
// add the Authenticated role and its permissions // add the Authenticated role and its permissions
// the Authenticated role is not assigned to the current user // the Authenticated role is not assigned to the current user
permissionsQuery = GetPermissioFromRole("Authenticated"); permissionsQuery = GetPermissionsFromRole("Authenticated");
if (_authorizer.Authorize(StandardPermissions.SiteOwner)) { if (_authorizer.Authorize(StandardPermissions.SiteOwner)) {
// the site owner has no permissions // The SuperUser is a SiteOwner that has no assigned role. To properly manage
// get the roles of the site owner // that case we make up a "fake" UserPermission here to add to SiteOwners. We
// just need to make sure that the role we use there doesn't actually exist.
userRolesPermissions.Add(new UserPermission {
RoleName = SiteOwnerRoleName(),
PermissionName = "AllPermissions" // A SiteOWner has all Permissions
});
// A user with the SiteOwner permission may also have other roles
userRolesPermissionsQuery = _userRolesRepo userRolesPermissionsQuery = _userRolesRepo
.Table.Where(usr => usr.UserId == currentUser.Id) .Table.Where(usr => usr.UserId == currentUser.Id)
.Join( .Join(
_roleRepo.Table, _roleRepo.Table,
ur => ur.Role.Id, ur => ur.Role.Id,
r => r.Id, r => r.Id,
(ur, r) => r (ur, r) => new UserPermission { RoleName = r.Name }
) );
.Select(urp => new UserPermission { RoleName = urp.Name }); // Since SiteOwners have all permissions, we don't need to query for them here.
} else { // We still query for their roles, because we may be displaying different stuff
// to users with different roles, even when they happen to have all permissions.
}
else {
userRolesPermissionsQuery = _userRolesRepo userRolesPermissionsQuery = _userRolesRepo
// get user roles and permissions // get user roles and permissions
.Table.Where(usr => usr.UserId == currentUser.Id) .Table.Where(usr => usr.UserId == currentUser.Id)
@ -85,25 +97,23 @@ namespace Orchard.OutputCache.Filters {
(rp, p) => new UserPermission { RoleName = rp.Role.Name, PermissionName = p.FeatureName + "." + p.Name } (rp, p) => new UserPermission { RoleName = rp.Role.Name, PermissionName = p.FeatureName + "." + p.Name }
); );
} }
} else { }
else {
// the anonymous user has no roles, get its permissions // the anonymous user has no roles, get its permissions
permissionsQuery = GetPermissioFromRole("Anonymous"); permissionsQuery = GetPermissionsFromRole("Anonymous");
} }
if (userRolesPermissionsQuery.Any()) { if (userRolesPermissionsQuery.Any()) {
userRolesPermissions.AddRange(userRolesPermissionsQuery userRolesPermissions.AddRange(userRolesPermissionsQuery
.OrderBy(urp => urp.RoleName)
.ThenBy(urp => urp.PermissionName)
.ToList()); .ToList());
} }
if (permissionsQuery.Any()) { if (permissionsQuery.Any()) {
userRolesPermissions.AddRange(permissionsQuery userRolesPermissions.AddRange(permissionsQuery
.OrderBy(urp => urp.RoleName)
.ThenBy(urp => urp.PermissionName)
.ToList()); .ToList());
} }
if (userRolesPermissions.Any()) { if (userRolesPermissions.Any()) {
var userRoles = String.Join(";", userRolesPermissions var userRoles = String.Join(";", userRolesPermissions
.Select(r => r.RoleName) .Select(r => r.RoleName)
.Distinct() .Distinct()
@ -118,12 +128,38 @@ namespace Orchard.OutputCache.Filters {
key.Append(string.Format("UserRoles={0};UserPermissions={1};", key.Append(string.Format("UserRoles={0};UserPermissions={1};",
userRoles.GetHashCode(), userRoles.GetHashCode(),
userPermissions.GetHashCode())); userPermissions.GetHashCode()));
} else { }
else {
key.Append("UserRoles=;UserPermissions=;"); key.Append("UserRoles=;UserPermissions=;");
} }
} }
private IQueryable<UserPermission> GetPermissioFromRole(string role) { private const string _siteOwnerRoleName = "SiteOwnerRole";
private IEnumerable<string> _siteOwnerRoleNames;
private string SiteOwnerRoleName() {
if (_siteOwnerRoleNames == null) {
// memorize this so it's only executed once per request
_siteOwnerRoleNames = _roleRepo.Table
.Where(rr => rr.Name.StartsWith(_siteOwnerRoleName))
.Select(rr => rr.Name)
.ToList()
.Distinct() // sanity check
;
}
var roleName = _siteOwnerRoleName;
if (_siteOwnerRoleNames.Any() && _siteOwnerRoleNames.Contains(roleName)) {
// compute unique and repeatable roleName
var i = 0;
do {
roleName = $"{_siteOwnerRoleName}-{i}";
i++;
} while (_siteOwnerRoleNames.Contains(roleName));
}
return roleName;
}
private IQueryable<UserPermission> GetPermissionsFromRole(string role) {
return _roleRepo return _roleRepo
.Table.Where(r => r.Name == role) .Table.Where(r => r.Name == role)
.Join( .Join(