我要做的是创建一个使用静态方法的类来管理某些类型资源(NHibernate实体对象)上不同用户类型的权限。具体来说,我想针对对象id检查当前主体(在asp.net MVC项目中),以查看他是否可以查看或编辑实体。我想到的签名如下:
PermissionManager.CanView<TEntity>(object id);
到目前为止,我已经完成了以下步骤:
1)这样的界面:
public interface ICanAccessQuery<TAccount, TEntity>
where TAccount : IAccountOwner
{
bool CanView(TAccount user, object entityKey);
bool CanEdit(TAccount user, object entityKey);
}
2)像这样的一些实现:
public class TeacherCanAccessCourseReportsQuery : ICanAccessQuery<Teacher, CourseReport>
{
public bool CanView(Teacher user, object entityKey)
{
var predicate = PredicateBuilder.Create<CourseReport>(x => x.Id == (long)entityKey);
var conditions = PredicateBuilder.Create<CourseReport>(x => x.Teacher.Id == user.Id);
conditions = conditions.Or(x => x.Teacher.Tutor.Id == user.Id);
conditions = conditions.Or(x => x.CoachingTeachers.Any(t => t.Id == user.Id));
predicate = predicate.And(conditions);
return RepositoryProvider.Get<CourseReport>().Count(predicate) > 0;
}
public bool CanEdit(Teacher user, object entityKey)
{
// similar implementation
}
}
3)我的PermissionManager类中的静态Configure()方法,在Global.asax中调用:
public static IDictionary<string, object> _permissions = new Dictionary<string, object>();
public static void Configure()
{
_permissions.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
}
4)在PermissionManager类中:
public static bool CanView<TEntity>(object primaryKey, params string[] enabledRoles)
{
var accounts = RepositoryProvider.Get<Account, AccountRepository>();
var principal = Thread.CurrentPrincipal as MyCustomPrincipal;
if (enabledRoles.Any(r => principal.IsInRole(r)))
return true;
IAccountOwner user = accounts.GetUser(principal.AccountId);
var can = false;
var @switch = new Dictionary<Type, Action> {
{ typeof(Teacher), () => can = CanView<Teacher, TEntity>(user as Teacher, primaryKey) },
{ typeof(TrainingCenter), () => can = CanView<TrainingCenter, TEntity>(user as TrainingCenter, primaryKey) }
};
@switch[user.GetType()]();
return can;
}
private static bool CanView<TAccount, TEntity>(TAccount user, object primaryKey)
where TAccount : IAccountOwner
{
var key = typeof(TAccount).Name + typeof(TEntity).Name;
if (_permissions.ContainsKey(key))
{
return (((ICanAccessQuery<TAccount, TEntity>)_permissions[key]).CanView(user, primaryKey);
}
return false;
}
将为CanEdit定义相同的方法...除了要调用的方法名称之外完全相同。
我要问的是:有更好的方式以更多的OOP方式定义我的想法吗?
答案 0 :(得分:1)
我已经实施了一个更好的解决方案,可能会对某些人感兴趣。
这是“我可以访问吗?”的界面查询:
public interface ICanAccessQuery<TAccount, TEntity>
where TAccount : IAccountOwner
where TEntity : IStoredEntity
{
bool CanView(TAccount user, TEntity entity);
bool CanEdit(TAccount user, TEntity entity);
}
我的实体现在实现了一个空接口IStoredEntity
来制定约束。
这是一个实施的例子:
public class TeacherCanAccessOrdersQuery : ICanAccessQuery<Teacher, Order>
{
public bool CanView(Teacher user, Order entity)
{
var predicate = PredicateBuilder.Create<Order>(x => x.Id == entity.Id && x => x.Account.Id == user.Account.Id);
return RepositoryProvider.Get<Order>().Count(predicate) > 0;
}
public bool CanEdit(Teacher user, Order entity)
{
// similar implementation
}
}
最后,我的新AuthorizationProvider
类(从PermissionManager更改名称,不喜欢它):
public class AuthorizationProvider
{
public enum Abilities
{
View,
Edit
};
private static IDictionary<string, object> _authorizations = new Dictionary<string, object>();
// this method should be called at application bootstrap, such as Global.asax in an asp.net app
public static void Configure()
{
_authorizations.Add(typeof(Teacher).Name + typeof(CourseReport).Name, new TeacherCanAccessCourseReportsQuery());
_authorizations.Add(typeof(Teacher).Name + typeof(Order).Name, new TeacherCanAccessOrdersQuery());
// other rules user type-entity type
}
// Can I view entity with primary key X?
public static bool CanI<TEntity>(Abilities ability, object entityKey)
where TEntity : IStoredEntity
{
TEntity entity = RepositoryProvider.Get<TEntity>().Load(entityKey);
return CanI<TEntity>(ability, entity, AccountRoles.Admin);
}
// Can I view entity (and if I have a specific role, I surely can)?
public static bool CanI<TEntity>(Abilities ability, TEntity entity, params string[] authorizedRoles)
where TEntity : IStoredEntity
{
var principal = Thread.CurrentPrincipal as MyCustomPrincipal;
if (authorizedRoles.Any(r => principal.IsInRole(r)))
return true;
var user = RepositoryProvider.Get<Account, AccountRepository>().GetUser(principal.AccountId);
// my system has only two types of users
if (user is Teacher)
{
return Can<Teacher, TEntity>(user as Teacher, ability, entity);
}
else if (user is TrainingCenter)
{
return Can<TrainingCenter, TEntity>(user as TrainingCenter, ability, entity);
}
return false;
}
/// Can user X (view|edit) entity Y?
/// With some reflection I call the needed method. In this way I can add "abilities" to my ICanAccessQuery
/// interface and its implementations without altering this class.
public static bool Can<TAccount, TEntity>(TAccount user, Abilities ability, TEntity entity)
where TAccount : IAccountOwner
where TEntity : IStoredEntity
{
var key = typeof(TAccount).Name + typeof(TEntity).Name;
if (_authorizations.ContainsKey(key))
{
var query = (ICanAccessQuery<TAccount, TEntity>)_authorizations[key];
string methodName = "Can" + ability.ToString();
var method = typeof(ICanAccessQuery<TAccount, TEntity>).GetMethod(methodName);
return (bool)method.Invoke(query, new object[] { user, entity });
}
return false;
}
}
在asp.net mvc控制器中使用的一个例子:
public ActionResult Details(long? id)
{
if (!id.HasValue)
return new EmptyResult();
if (!AuthorizationProvider.CanI<CourseReport>(AuthorizationProvider.Abilities.View, id.Value))
return RedirectToAccessDenied();
// etc.
}