2017-11-21 3 views
5

Ich versuche, eine Schnittstelle für den Benutzer zum Schreiben von benutzerdefinierten Abfragen über die Datenbank bereitzustellen. Ich muss sicherstellen, dass sie nur die Datensätze abfragen können, die ihnen erlaubt sind. Um dies zu tun, entschied ich mich für die zeilenbasierte Zugriffskontrolle mit django-guardian.Raw Abfrage und Zeilenzugriffssteuerung über mehrere Modelle in Django

Hier ist, wie meine Schemata aussehen

class BaseClass(models.Model): 
    somefield = models.TextField() 
    class Meta: 
     permissions = (
      ('view_record', 'View record'), 
     ) 

class ClassA(BaseClass): 
    # some other fields here 
    classb = models.ForeignKey(ClassB) 

class ClassB(BaseClass): 
    # some fields here 
    classc = models.ForeignKey(ClassC) 

class ClassC(BaseClass): 
    # some fields here 

würde ich in der Lage sein mag get_objects_for_group wie folgt zu verwenden:

>>> group = Group.objects.create('some group') 
>>> class_c = ClassC.objects.create('ClassC') 
>>> class_b = ClassB.objects.create('ClassB', classc=class_c) 
>>> class_a = ClassA.objects.create('ClassA', classb=class_b) 
>>> assign_perm('view_record', group, class_c) 
>>> assign_perm('view_record', group, class_b) 
>>> assign_perm('view_record', group, class_a) 
>>> get_objects_for_group(group, 'view_record') 

Das gibt mir eine QuerySet. Kann ich die BaseClass verwenden, die ich oben definiert habe, und eine rohe Abfrage über andere verwandte Klassen schreiben?

>>> qs.intersection(get_objects_for_group(group, 'view_record'), \ 
        BaseClass.objects.raw('select * from table_a a' 
              'join table_b b on a.id=b.table_a_id ' 
              'join table_c c on b.id=c.table_b_id ' 
              'where some conditions here')) 

Macht dieser Ansatz Sinn? Gibt es einen besseren Weg, dieses Problem anzugehen?

Danke!

Edit:

Eine andere Möglichkeit, das Problem zu bewältigen könnte eine separate Tabelle für jeden Benutzer werden zu schaffen. Ich verstehe die Komplexität, die dies zu meiner Anwendung hinzufügen könnte, aber:

  • Die Anzahl der Benutzer wird nicht mehr als 100s für eine lange Zeit sein. Keine Verbraucheranwendung.
  • Für unseren Anwendungsfall ist es ziemlich unwahrscheinlich, dass ich quer über diese Tabellen abfragen muss. Ich werde keine Abfrage schreiben, die irgendetwas aus table1, table2, table3 aggregieren muss, das zu demselben Modell gehört.
  • Die Beibehaltung einer separaten Tabelle pro Kunde kann von Vorteil sein.

Denken Sie, dass dies ein praktikabler Ansatz ist?

+0

Nur bestätigt, dass ich keine Schnittmenge auf RawQuerySet tun kann. – ertan

Antwort

2

Ich denke, Sie wissen bereits, was Sie tun müssen. Das Wort, nach dem Sie suchen, ist Multitenancy. Obwohl es keine Tabelle pro Kunde gibt. Die beste Lösung für Sie ist ein Schema pro Kunde. Leider ist der beste Artikel, den ich über Multitenancy hatte, nicht mehr verfügbar. Sehen Sie, wenn Sie eine zwischengespeicherte Version finden können: https://msdn.microsoft.com/en-us/library/aa479086.aspx, ansonsten gibt es zahlreiche Artikel im Internet. Ein weiterer praktikabler Ansatz ist ein Blick auf custom managers. Sie könnten einen benutzerdefinierten Manager für jeden Modell-Kunden schreiben und ihn entsprechend abfragen. Aber all dies wird zu Komplexität der Anwendung führen und wird bald aus Ihrer Hand kommen. Jeder Fehler in der Anwendungssicherheitsebene ist ein Albtraum für Sie.

Wiegen beide Ich würde geneigt sein, Multitenancy-Lösung zu sagen, wie Sie in Ihrer Bearbeitung sagten, ist bei weitem der beste Ansatz.

+0

Ich denke, ich werde mit Row Level Security auf PostgreSQL gehen: https://www.postgresql.org/docs/9.5/static/ddl-rowsecurity.html. Dieser Artikel hat mir sehr geholfen, die Anwendungsebene Benutzer mit Postgres-Richtlinien zu überbrücken: https://blog.2ndquadrant.com/application-users-vs-roow-level-security/ – ertan

+0

Interessante lesen. Danke für das Teilen. Aber ich sehe ein Problem. Der Aufwand für die Erstellung neuer Richtlinien und die Beibehaltung der Synchronität zwischen db-Schicht und Anwendungsschicht ist nicht angemessen. –

+1

Erstellen einer Richtlinie für jede Tabelle ist ein Overhead ja, aber Sie müssen es nur einmal tun. Wenn Sie ein Skript dazu schreiben und es in das Repo einchecken, fühlt es sich nicht so schlecht an. Re: Synchronisierungsdatenbank und Anwendungsschicht. Tatsächlich wird dies hauptsächlich dadurch gehandhabt, dass die Sitzungsvariable entsprechend mit einer Codezeile gesetzt wird und der Rest innerhalb der Richtlinien behandelt wird. Der einzige Haken ist, dass, wenn sich dieses spezielle Feld über eine Migration ändert, dies eine Sicherheitslücke schaffen könnte. Ich plane, dies mit einigen Integrationstests abzuschwächen. Ich habe das Gefühl, dass es im Vergleich zur Multitenancy einfacher ist. – ertan

2

Nach der Untersuchung vieler Optionen habe ich herausgefunden, dass ich dieses Problem auf Datenbankebene mit Row Level Security auf PostgreSQL lösen kann. Es endet am einfachsten und elegantesten.

This article hat mir sehr geholfen, die Anwendungsebene Benutzer mit PostgreSQL-Richtlinien zu überbrücken.

Was ich tun meine Forschung gelernt haben, ist:

  • Separate Tabellen immer noch eine Option in der Zukunft sein könnte, wenn die Kunden möglicherweise jeweils anderen Abfrage Leistungen beeinflussen können, da sie erlaubt beliebige Abfragen ausführen.

  • Der Versuch, es auf ORM-Ebene zu lösen, ist fast unmöglich, wenn Sie rohe oder Ad-hoc-Abfragen planen.

-1

Zuerst sollten Sie uns diese mit mehr Details, wie ist Ihre Architektur gesetzt und gebaut, mit django, damit wir Ihnen helfen können. Haben Sie eine API implementiert? Die Verwendung der Django-Vorlage ist nicht wirklich eine gute Idee, wenn Sie eine große Anwendung erstellen, die eine Menge Daten verbraucht. Dies kann sich massiv auf die Abfrage auswirken. Ich kann vorschlagen, Ihr Frontend aus dem Backend zu extrahieren.