Я выбрал использование атрибута в действии контроллера, который проверяет связь между текущим пользователем и запрашиваемым объектом, а затем разрешает или запрещает действие на основе результатов поиска. У меня есть несколько различных атрибутов в зависимости от того, проходит ли он через таблицу соединений или имеет прямые отношения. Он использует отражение против, в моем случае, контекста данных, но в вашем хранилище (ях), чтобы получить и проверить, что значения совпадают. Я включу приведенный ниже код (который я приложил некоторые усилия для обобщения, чтобы он не компилировался). Обратите внимание, что вы можете расширить это, включив также некоторое понятие разрешения (в таблицу соединений).
Код для атрибута прямой связи. Он проверяет, что текущий пользователь является владельцем записи (указанный атрибут «id» в параметрах маршрутизации совпадает с идентификатором текущего пользователя в пользовательской таблице).
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )]
public class RoleOrOwnerAuthorizationAttribute : AuthorizationAttribute
{
private IDataContextFactory ContextFactory { get; set; }
private string routeParameter = "id";
/// <summary>
/// The name of the routing parameter to use to identify the owner of the data (participant id) in question. Default is "id".
/// </summary>
public string RouteParameter
{
get { return this.routeParameter; }
set { this.routeParameter = value; }
}
public RoleOrOwnerAuthorizationAttribute()
: this( null )
{
}
// this is for unit testing support
public RoleOrOwnerAuthorizationAttribute( IDataContextFactory factory )
{
this.ContextFactory = factory ?? DefaultFactory();
}
public override void OnAuthorization( AuthorizationContext filterContext )
{
if (filterContext == null)
{
throw new ArgumentNullException( "filterContext" );
}
if (AuthorizeCore( filterContext.HttpContext ))
{
SetCachePolicy( filterContext );
}
else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
}
else if (filterContext.HttpContext.User.IsInRole( "SuperUser" ) || IsOwner( filterContext ))
{
SetCachePolicy( filterContext );
}
else
{
ViewDataDictionary viewData = new ViewDataDictionary();
viewData.Add( "Message", "You do not have sufficient privileges for this operation." );
filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
}
}
private bool IsOwner( AuthorizationContext filterContext )
{
using (var dc = this.ContextFactory.GetDataContextWrapper())
{
int id = -1;
if (filterContext.RouteData.Values.ContainsKey( this.RouteParameter ))
{
id = Convert.ToInt32( filterContext.RouteData.Values[this.RouteParameter] );
}
string userName = filterContext.HttpContext.User.Identity.Name;
return dc.Table<UserTable>().Where( p => p.UserName == userName && p.ParticipantID == id ).Any();
}
}
}
Это код атрибута ассоциации, т. Е. В таблице соединений существует связь между идентификатором в параметре маршрутизации и идентификатором пользователя из пользовательской таблицы. Обратите внимание, что существует зависимость от кода System.Linq.Dynamic из VS2008 Samples .
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )]
public class RoleOrOwnerAssociatedAuthorizationAttribute : MasterEventAuthorizationAttribute
{
private IDataContextFactory ContextFactory { get; set; }
public RoleOrOwnerAssociatedAuthorizationAttribute()
: this( null )
{
}
// this supports unit testing
public RoleOrOwnerAssociatedAuthorizationAttribute( IDataContextFactory factory )
{
this.ContextFactory = factory ?? new DefaultDataContextFactory();
}
/// <summary>
/// The table in which to find the current user by name.
/// </summary>
public string UserTable { get; set; }
/// <summary>
/// The name of the property in the UserTable that holds the user's name to match against
/// the current context's user name.
/// </summary>
public string UserNameProperty { get; set; }
/// <summary>
/// The property to select from the UserTable to match against the UserEntityProperty on the JoinTable
/// to determine membership.
/// </summary>
public string UserSelectionProperty { get; set; }
/// <summary>
/// The join table that links users and the entity table. An entry in this table indicates
/// an association between the user and the entity.
/// </summary>
public string JoinTable { get; set; }
/// <summary>
/// The property on the JoinTable used to hold the entity's key.
/// </summary>
public string EntityProperty { get; set; }
/// <summary>
/// The property on the JoinTable used to hold the user's key.
/// </summary>
public string UserEntityProperty { get; set; }
/// <summary>
/// The name of the route data parameter which holds the group's key being requested.
/// </summary>
public string RouteParameter { get; set; }
public override void OnAuthorization( AuthorizationContext filterContext )
{
using (var dc = this.ContextFactory.GetDataContextWrapper())
{
if (filterContext == null)
{
throw new ArgumentNullException( "filterContext" );
}
if (AuthorizeCore( filterContext.HttpContext ))
{
SetCachePolicy( filterContext );
}
else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
}
else if (filterContext.HttpContext.User.IsInRole( "SuperUser" )
|| IsRelated( filterContext, this.GetTable( dc, this.JoinTable ), this.GetTable( dc, this.UserTable ) ))
{
SetCachePolicy( filterContext );
}
else
{
ViewDataDictionary viewData = new ViewDataDictionary();
viewData.Add( "Message", "You do not have sufficient privileges for this operation." );
filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
}
}
}
protected bool IsRelated( AuthorizationContext filterContext, IQueryable joinTable, IQueryable userTable )
{
bool result = false;
try
{
int entityIdentifier = Convert.ToInt32( filterContext.RouteData.Values[this.RouteParameter] );
int userIdentifier = this.GetUserIdentifer( filterContext, userTable );
result = joinTable.Where( this.EntityProperty + "=@0 and " + this.UserEntityProperty + "=@1",
entityIdentifier,
userIdentifier )
.Count() > 0;
}
catch (NullReferenceException) { }
catch (ArgumentNullException) { }
return result;
}
private int GetUserIdentifer( AuthorizationContext filterContext, IQueryable userTable )
{
string userName = filterContext.HttpContext.User.Identity.Name;
var query = userTable.Where( this.UserNameProperty + "=@0", userName )
.Select( this.UserSelectionProperty );
int userIdentifer = -1;
foreach (var value in query)
{
userIdentifer = Convert.ToInt32( value );
break;
}
return userIdentifer;
}
private IQueryable GetTable( IDataContextWrapper dc, string name )
{
IQueryable result = null;
if (!string.IsNullOrEmpty( name ))
{
DataContext context = dc.GetContext<DefaultDataContext>();
PropertyInfo info = context.GetType().GetProperty( name );
if (info != null)
{
result = info.GetValue( context, null ) as IQueryable;
}
}
return result;
}
}