2016-04-14 4 views
1

Wir verwenden das WebAPI/OData-Projekt, um eine Datenschicht für unsere Anwendung verfügbar zu machen.Zusätzliche Filterung in Odata Controllern für die Zugriffssteuerung

.NET Framework 4.6.1
System.Web.OData, Version = 5.9.0.0

Wir haben mehrere OData-Controller für verschiedene Datensätze (Chirurgen, Patienten etc.). Wenn ich versuche, nach Patienten zu suchen, gebe ich eine OData-Abfrage wie folgt aus, um eine Liste von Patienten zu erhalten, die meinen gewünschten Kriterien entsprechen.

http://localhost/MyService/Patients?$filter=contains($it/Name,”Joe”) 

Das Problem ist, dass ich nicht alle Patienten sehen darf. Da die RESTful-API extern verfügbar gemacht wird, kann ich nicht darauf vertrauen, dass der Client die Sicherheitsanforderungen durch Manipulation der URL durchsetzt.

Wenn ich Ansprüche verwende, um Benutzern den Zugriff auf bestimmte Patienten zu gewähren, könnte ich den Zugriff auf einen einzelnen Benutzer schützen, aber dann würde die Suche nach einer Liste übereinstimmender Benutzer überhaupt nicht funktionieren.

http://localhost/MyService/Patients(Id) 

Wenn ich versuche, dies zu umgehen und die Patienten besitzt Zugang durch die Einheit erhält die gleiche Art von Problem auf der nächst höheren Ebene stattfindet. Mit anderen Worten, wenn Chirurgen Patienten besitzen, wie bekomme ich dann eine Liste der Chirurgen, zu denen ich Zugang habe?

Gibt es eine Möglichkeit, der von OData erstellten Abfrage einige zusätzliche Kriterien hinzuzufügen, um die Zugriffssteuerungen durchzusetzen?

Antwort

1

Dies ist die GET-Methode für den Patienten OData Controller

public class PatientsController : BaseODataController<Patient> 
{ 
    public override IQueryable<Patient> Get() 
    { 
     return base.Get(); 
    } 
    ... 
} 

Es kehrt IQueryable <Patient> ...

Das einzige, was wir hinzufügen tun müssen Die Filterung der Zugriffssteuerung erfolgt über die zusätzlichen Prädikate ... Beispiel:

public override IQueryable<Patient> Get() 
{ 
    var qry = base.Get(); 
    return qry.Where(itm => itm.Name.FirstName.Contains("R")); 
} 

Wenn die Steuerung die Infrastruktur IQueryable <T> aufzählt er die gesamte Arbeitseinheit auswertet und erzeugt die endgültige Abfrage.

Wenn ich laufen Spur für die Datenbank und geben ihm eine komplexere OData Abfrage wie

http://localhost/MyService/Patients?$filter=Surgeries/any(d:d/PreOpDataComplete%20eq%20true). 

(Mit anderen Worten Praxen finden, wo die PreOpDataComplete Flag wahr ist) Ich werde sehen, die folgende Abfrage ausgeführt wird.

exec sp_executesql N'SELECT 
        [Project1].[C1] AS [C1], 
        [Project1].[Id] AS [Id], 
        [Project1].[Name_FirstName] AS [Name_FirstName], 
        [Project1].[Name_LastName] AS [Name_LastName], 
        [Project1].[Gender] AS [Gender], 
        [Project1].[BirthDate] AS [BirthDate], 
        [Project1].[MedicalRecordId] AS [MedicalRecordId], 
        [Project1].[Surgeon_Id] AS [Surgeon_Id] 
        FROM (SELECT 
            [Extent1].[Id] AS [Id], 
            [Extent1].[Name_FirstName] AS [Name_FirstName], 
            [Extent1].[Name_LastName] AS [Name_LastName], 
            [Extent1].[Gender] AS [Gender], 
            [Extent1].[BirthDate] AS [BirthDate], 
            [Extent1].[MedicalRecordId] AS [MedicalRecordId], 
            [Extent1].[Surgeon_Id] AS [Surgeon_Id], 
            1 AS [C1] 
            FROM [dbo].[Patient] AS [Extent1] 
            WHERE [Extent1].[Name_FirstName] LIKE N''%R%'' 
       )  AS [Project1] 
        WHERE  EXISTS (SELECT 
            1 AS [C1] 
            FROM [dbo].[Surgery] AS [Extent2] 
            WHERE ([Project1].[Id] = [Extent2].[Patient_Id]) AND ([Extent2].[PreOpDataComplete] = @p__linq__0) 
       )',N'@p__linq__0 bit',@p__linq__0=1 

Und ich bekomme das erwartete Ergebnis

{ 
    "@odata.context":"http://localhost/MyService/$metadata#Patients", 
    "value":[{ 
     "Name":{ 
      "FirstName":"Ronn", 
      "LastName":"Black" 
     }, 
     "Gender":"M", 
     "BirthDate":"1917-02-02T00:00:00-08:00", 
     "MedicalRecordId":"MRN 0001", 
     "Id":"8bf6dcc4-3f00-4a40-980c-ceb13f8f5360" 
    }] 
} 

Wenn ich ein einfaches Sicherheitsmodell übernehmen, wo Chirurgen ihre eigenen Patienten besitzen, und die ich habe eine Liste der Chirurgen ids ich darf zu Zugang in einem Anspruch. Ich würde einen Chirurgen Einheit hat, die etwa wie folgt aussieht:

public partial class Surgeon : IBaseEntity 
{ 
    [Key] 
    public Guid Id { get; set; } 
    public virtual ICollection<Patient> Patients { get; set; } 
    . . . 
} 

Nun, wenn ich die folgende Änderung vornehmen kann ich keine Suche einschränken nur die Patienten, ich darf sehen:

public override IQueryable<Patient> Get() 
{ 
    //IQueriable from the OData Selection 
    var qry = base.Get(); 
  
    //Enforce Access Security 
    var accessList = {get list of authorized surgeon id's from claims}; 
    var finalQry = uow.Surgeons 
        .Where(s => accessList.Contains(s.Id))  //Restrict to Surgeons I'm allowed to see 
        .SelectMany(surgeon => surgeon.Patients)  //All the patients I'm allowed to see (Left) 
        .Join(qry,                              //Join to Query (Right) 
            allowedPatients => allowedPatients.Id, //Key for Left 
            qryPatients => qryPatients.Id,         //Key for Right 
            (allowedPatients, qryPatients) =>      //Iterate through Matches 
            qryPatients);                          //Return the Matches from Right 
  
    return finalQry; 
}