I am implementing Authorization for WPF. I have bound permissions with screens. For example, there is a screen called floor. It has four permissions associated with it.
Along with every permission we have four levels of permissions.
So, if users have {View Floor} permission with permission level {ALL}. They can view all floors created by any user.
If users have {Self} Permission Level. They can only view floors created by themselves.
If a user have a Permission Level of {Role}. They can view all floors assigned against specified roles.
Roles for Permission Level are assigned like this:
Roles: Supervisor,Technician
If a BranchManager have the above Permission Level. Then he can view floors created by himself and created by any users which are having role of supervisor or technician.
Permission Level {User} is same as {Role}.
Here is my IQueryable to fetch floors:
IQueryable<FloorModel> FloorsQueryable = (from f in Floors
join b in Branches on f.BranchId equals b.Id
where (string.IsNullOrEmpty(filters.Name) || f.Name.ToLower().Contains(filters.Name.ToLower()))
&& (string.IsNullOrEmpty(filters.BranchName) || b.Name.ToLower().Contains(filters.BranchName.ToLower()))
&& (f.IsDeleted == null || f.IsDeleted == false)
&& f.BranchId == CurrentBranchId
&& f.ApplicationId == CurrentApplicationId
select new FloorModel
{
Id = f.Id,
Name = f.Name,
Code = f.Code,
Branch = new BranchBriefModel()
{
Id = f.BranchId,
Name = b.Name,
},
IsActive = f.IsActive ?? false,
Description = f.Description,
CreatedBy = f.CreatedBy ?? 0,
ApplicationId = CurrentApplicationId,
}).AsQueryable();
After this I need to apply some custom filter to this query based on Permission Level
FloorsQueryable = CustomFilter(FloorsQueryable,CurrentUserId,filters);
CustomFilter Code
public IQueryable<T> CustomFilter<T>(IQueryable<T> query, int CurrentUserId, BaseSearchModel search) where T : BaseModel, new()
{
var distinctClaims = search.ScreenPermission.Select(x => x.Claim.PermissionLevel).Distinct().ToList();
var viewPermission = search.ScreenPermission.Where(x => x.Name.Contains("View")).FirstOrDefault();
if (viewPermission != null && viewPermission.Claim.PermissionLevel != PermissionLevelCatalog.All)
{
if (distinctClaims.Any(x => x == PermissionLevelCatalog.Role))
{
query = (from q in query
join ur in UsersInRoles on q.CreatedBy equals ur.UserId
join r in Roles on ur.RoleId equals r.Id
select q);
}
string propertyName = "CreatedBy";
//if (string.IsNullOrEmpty(propertyName))
// return query;
// we have parameter "generic"
var selectorParameter = Expression.Parameter(typeof(T), "generic");
// constant "CurrentUserId"
var searchTermExpression = Expression.Constant(CurrentUserId);
// "generic.CreatedBy"
var selector = Expression.PropertyOrField(selectorParameter, propertyName);
// "generic.CreatedBy == "CurrentUserId"
var equal = Expression.Equal(selector, searchTermExpression);
// generic => generic.Name == "CurrentUserId"
var genericWhere = Expression.Lambda<Func<T, bool>>(equal, selectorParameter);
query = query.Where(genericWhere);
}
return query;
}
Now for Permission Level {Role} I need to add CreatorRoleId in select clause of IQueryable to apply the required check in the following code.
if (distinctClaims.Any(x => x == PermissionLevelCatalog.Role))
{
query = (from q in query
join ur in UsersInRoles on q.CreatedBy equals ur.UserId
join r in Roles on ur.RoleId equals r.Id
select q
);
}
Is it possible to some how set the value of CreatorRoleId? Some thing like the following
select {q=>q.CreatorRoleId=r.Id return q;}
This is supposed to be generic and run for any IQueryable. I do not want to join Role and UserInRole table for all the queries in project because these joins are only needed when some user has this {Role} Permission Level.
I think it's something like return query.Where(q => q.CreatorRoleId == r.Id);
I guess.