Frage

Mit Domain Driven Design im Sinne, wie würden Sie die Benutzerautorisierung in einem Repository implementieren? Genauer gesagt, wie würden Sie beschränken, welche Daten durch den Benutzer zur Verfügung gestellt Login sehen kann?

Lassen Sie uns sagen wir eine E-Commerce-Mall, die Produkte speichert, wo nur einige Produkte von einem Shop-Manager verwaltet werden. In diesem Fall sollten nicht alle Produkte von einem bestimmten Login gesehen werden.

Fragen:

  1. Möchten Sie alle Produkte in der Repo wählen, dann einen Filter verwenden, zu beschränken, welche Produkte zurückgegeben werden? Like GetProducts("keyword: boat").restrictBy("myusername")?
  2. Möchten Sie lesen die Login aus dem Controller im Repository und Filter Ergebnisse passiv?
  3. Wie würden Sie die Beziehung zwischen einer Benutzerrolle speichern und welchen Unternehmen könnten darauf zugreifen? Würden Sie einfach die Entitätsschlüssel speichern und die Rolle in einer many-to-many-Tabelle, mit einem Datensatz für jedes Produkt, das jeder Filialleiter zugreifen konnte?

Code-Beispiele oder Links zu Codebeispielen wären fantastisch. Danke.

War es hilfreich?

Lösung

Die Wende, die ich gemacht habe, ist Attribut auf der Controller-Aktion zu verwenden, die die Beziehung zwischen dem aktuellen Benutzer und der Einheit, dann entweder wird angefordert suchen erlaubt oder die Aktion nicht zulässt, auf den Ergebnisse des Look basiert. Ich habe ein paar verschiedene Attribute je nachdem, ob es geht durch einen Tisch beitreten oder eine direkte Beziehung hat. Es nutzt Reflexion vor, den Datenkontext in meinem Fall, aber in Ihnen das Repository (n) zu erhalten und zu prüfen, ob die Werte übereinstimmen. Ich werde den Code enthalten (die ich einige Anstrengungen unternommen haben, um genericize so kann es nicht kompilieren). Hinweis: Sie können diese erweitern als auch eine Vorstellung von Erlaubnis enthalten (in der Join-Tabelle).

-Code für die direkte Beziehung Attribut. Es überprüft, ob der aktuelle Benutzer ist der Eigentümer des Datensatz (das angegebene Attribut „id“ in dem Routing-Parameter entspricht die ID des aktuellen Benutzers in der Benutzertabelle).

[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();
        }

    }

}

Dies ist der Code für die Assoziation Attribut, das heißt, es gibt eine Verbindung in einer Join-Tabelle zwischen der ID in dem Routingparameter und die ID des Benutzers aus der Benutzertabelle. Beachten Sie, dass eine Abhängigkeit von dem System.Linq.Dynamic Code ist aus den VS2008 Proben .

[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;
    }
}

Andere Tipps

, fragte ich eine sehr ähnliche Frage auf dem S # arp Architektur Newsgroup , für die Tom Cabanski ein AOP Rahmen vorgeschlagen, wie

scroll top