Zur Navigation

Http GET Query-String und Passwortschutz

1 Stephan (Gast)

Hallo zusammen,

ich hoffe, ich bin hier richtig und könnt mir ein wenig Licht ins Dunkel bringen.
Eines vorab: ich bin kein admin sondern bewege mich fachlich im Bereich GDI/INSPIRE, habe somit trotzdem technisches Verständnis.

Nun zu meiner Frage:
Ich habe einen WebFeatureService (WFS) mit 15 Layern über den UMN MapServer aufgebaut und möchte diesen Dienst mit Passwort "schützen".

Der MapServer selbst ist eine cgi-Anwendung und besitzt keine Authentifizierungs- oder Rollenmodule. Die Herausforderung ist, dass jeder Layer einen unterschiedlichen Nutzer mit Passwort bekommen soll.
Des Weiteren gibt es einen Super-Nutzer, der auf alle Layer zugreifen darf.

Die URL sieht etwa so aus:
http://www.domäne.de/alkis/wfs?
wobei unterschiedliche http-Requests nach dem "?" möglich sind:
SERVICE=WFS&VERSION=1.1.0&REQUEST=GetCapabilities oder
SERVICE=WFS&VERSION=1.1.0&TYPENAME=Landkreise&REQUEST=DescribeFeatureType oder
SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=Landkreise&SRSNAME=EPSG:25833

TYPENAME = Layername

Bisher habe ich im Apache dies mal mit dem Modul auth_digest und auf das Mapfile (darin sind alle Layer beschrieben) direkt, umgesetzt.
Das heißt, dass der WFS-Dienst dann komplett gesperrt wird und nicht wie gewünscht Layerweise.

Hier mal das bisherige Beispiel:

Alias /alkis/wfs /usr/lib/cgi-bin/mapserv.fcgi
<Location /pfadMapfile/wfs>
AuthType Digest
AuthName "WFS Passwortabfrage"
AuthUserFile /etc/apache2/pwuser
Require user USER
SetEnv MS_MAPFILE /pfadMapfile/wfs/test.map
</Location>

Soweit ich verstanden habe, lässt sich dies irgendwie mit Filterregeln abfangen, da es HTTP-Requests sind.

Lässt sich dies im Apache mit rewrite oder if-directive irgendwie lösen?

Zusammenfassung:
- Zugriff auf den WFS-Dienst mit Apache und Digest/LDAP absichern
- 15 Layer -> jeder Layer hat unterschiedliche Nutzer, ein Super-User darf alle Layer sehen
- eine GetCapabilities-Anfrage (welche Layer sind verfügbar?) soll von allen Nutzern einsehbar sein
- erst wenn ein Layer im GIS-Client ausgewählt wird, soll die Nutzer/Passwortabfrage erfolgen

Besten Dank schon mal!
Stephan

28.11.2016 11:27

2 Jörg Kruse

Eine Frage vorweg:

Alias /alkis/wfs /usr/lib/cgi-bin/mapserv.fcgi
<Location /pfadMapfile/wfs>

Sollte hier statt "pfadMapfile" nicht auch "alkis" stehen?

Zweite Frage:

Lässt sich denn aus einer URL selbst schon ableiten, auf welchen Layer zugegriffen werden soll?

In MS_MAPFILE steht ja nur der Pfad des Mapfiles, dessen Inhalt dann wohl vom MapServer interpretiert wird, aber nicht vom Webserver?

28.11.2016 14:22 | geändert: 28.11.2016 14:24

3 Stephan (Gast)

Hallo Jörg,

ja, das ist korrekt. Es heißt natürlich:

Alias /alkis/wfs /usr/lib/cgi-bin/mapserv.fcgi
<Location /alkis/wfs>

Zur zweiten Frage:
Ja, lässt sich ableiten, da der Layername im Request vorkommt, z.B.
?Request...&TYPENAME=Landkreise...oder
?Request...&TYPENAME=Gemeinden...oder
?Request...&TYPENAME=Gemarkungen...oder

Habe einen Zwischenweg über <If </If> gefunden:

Alias /alkis/wfs /usr/share/cgi-bin/mapserv.fcgi
<Location alkis/wfs
<If "%{QUERY_STRING}=~ /(?i)&TYPENAME=Landkreise/">
LDAP-Server-Parameter
AuthName "Anmeldung"
AuthBasicProvider ldap
AuthType Basic
AuthLDAPGroupAttribute member
AuthLDAPGroupAttributeISDN On
AuthLDAPMaxSubGroupDepth 5
AuthLDAPSubGroupClass group
Require ldap-group (LIST,NLPL)
</If>
<If "%{QUERY_STRING}=~ /(?i)&TYPENAME=Gemeinden/">
LDAP-Server-Parameter
AuthName "Anmeldung"
AuthBasicProvider ldap
AuthType Basic
AuthLDAPGroupAttribute member
AuthLDAPGroupAttributeISDN On
AuthLDAPMaxSubGroupDepth 5
AuthLDAPSubGroupClass group
Require ldap-group (LIST,NLBZ)
</If>
<If "%{QUERY_STRING}=~ /(?i)&TYPENAME=Gemarkungen/">
LDAP-Server-Parameter
AuthName "Anmeldung"
AuthBasicProvider ldap
AuthType Basic
AuthLDAPGroupAttribute member
AuthLDAPGroupAttributeISDN On
AuthLDAPMaxSubGroupDepth 5
AuthLDAPSubGroupClass group
Require ldap-group (LIST,NLZ)
</If>
SetEnv MS_MAPFILE /home/user/mapfiles/alkis.mapfiles/fisbaum_sbv
Options +ExcecCGI
</Location>

PS: jetzt soll natürlich gleich noch ldap angebunden werden :)

Frage:
Nun sind natürlich 3 If-container in dieser Location enthalten.
Kann man das irgendwie kürzen?
Schließlich habe ich 15 Layer und da wollte ich ungern 15 If-Bedingungen aufführen. Geht sowas?

Danke und viele Grüße!
Stephan

28.11.2016 15:46

4 Jörg Kruse

Ah, OK, der Layername wird also mit dem Parameter TYPENAME übermittelt

Nun sind natürlich 3 If-container in dieser Location enthalten.
Kann man das irgendwie kürzen?

Naja, in den if-Containern musst du ja nur die Direktiven packen, die sich unterscheiden, also (zumindest) diese hier:

<If "%{QUERY_STRING} =~ /(?i)&TYPENAME=Landkreise/">
Require ldap-group (LIST,NLPL)
</If>

Der Rest kann ja außerhalb der if-Container stehen - falls ein Layer angegeben sein muss, in einem globalen if-Container:

<If "%{QUERY_STRING} =~ /(?i)&TYPENAME=">
AuthName "Anmeldung"
AuthBasicProvider ldap
AuthType Basic
AuthLDAPGroupAttribute member
AuthLDAPGroupAttributeISDN On
AuthLDAPMaxSubGroupDepth 5
AuthLDAPSubGroupClass group 
</If>

28.11.2016 16:04 | geändert: 28.11.2016 16:06

5 Stephan (Gast)

Ok, also sehe das aufgeräumt etwa so aus?

<If "%{QUERY_STRING} =~ /(?i)&TYPENAME=Landkreise/">
Require ldap-group (LIST,NLPL)
</If>
<If "%{QUERY_STRING}=~ /(?i)&TYPENAME=Gemarkungen/">
Require ldap-group (LIST,NLZ)
</If>
<If "%{QUERY_STRING}=~ /(?i)&TYPENAME=Gemeinden/">
Require ldap-group (LIST,NLBZ)
</If>
LDAP-Server-Parameter
AuthName "Anmeldung"
AuthBasicProvider ldap
AuthType Basic
AuthLDAPGroupAttribute member
AuthLDAPGroupAttributeISDN On
AuthLDAPMaxSubGroupDepth 5
AuthLDAPSubGroupClass group
SetEnv MS_MAPFILE /home/user/mapfiles/alkis.mapfiles/fisbaum_sbv
Options +ExcecCGI

Und kann man den Typename irgendwie in eine Variable packen, so dass bei entsprechendem Layer die richtige ldap-Gruppe greift?


...in einem globalen if-Container:
<If "%{QUERY_STRING} =~ /(?i)&TYPENAME=">
AuthName "Anmeldung"
AuthBasicProvider ldap
AuthType Basic
AuthLDAPGroupAttribute member
AuthLDAPGroupAttributeISDN On
AuthLDAPMaxSubGroupDepth 5
AuthLDAPSubGroupClass group
</If>

Das ist mir noch etwas unklar. Wie kommt denn dann der Typename an die richtige Stelle?

Viele Grüße!
Stephan

28.11.2016 16:19

6 Jörg Kruse

Ok, also sehe das aufgeräumt etwa so aus?

Ja, aber ich würde die allgemeinen Auth-Direktiven in den oben aufgeführten Container packen. Die Passwort-Abfrage soll ja nur dann kommen, wenn auf einen Layer zugegriffen wird. Das ist der Fall, wenn im Query String ein "&TYPENAME=" steht

Und kann man den Typename irgendwie in eine Variable packen, so dass bei entsprechendem Layer die richtige ldap-Gruppe greift?

Das wird doch durch schon die Direktive "Require ldap-group" in den jeweiligen if-Containern sichergestellt?

Das ist mir noch etwas unklar. Wie kommt denn dann der Typename an die richtige Stelle?

Die URL wird ja nicht verändert, so dass der MapServer weiterhin den Query String mit dem Parameter TYPENAME auslesen kann. Mit der Abfrage in den einzelnen if Containern wird lediglich auf Vorhandensein eines bestimmten Typenames überprüft, und dann mittels der betreffenden "Require ldap-group"-Direktive die betreffende LDAP-Gruppe definiert. Und im allgemeinen if Container (<If "%{QUERY_STRING} =~ /(?i)&TYPENAME=">) wird nur auf Vorhandensein irgendeines Typenames geprüft und dann die allgemeinen Auth-Direktiven eingebaut

28.11.2016 16:40 | geändert: 28.11.2016 16:44

7 Stephan (Gast)

Ok, langsam verstehe ich es aber mir ist nur noch nicht so recht klar, wo ich den allgemeinen if Container definiere: ich habe eine vhost.conf Datei in der diese oben beschriebenen Anweisungen stehen. Erstelle ich beispielsweise im conf-enabled Verzeichnis so eine globale if-container Datei?

Gruß, Stephan

29.11.2016 11:01

8 Jörg Kruse

In welchem Verzeichnis befindet sich die Datei vhost.conf?

Die URL sieht etwa so aus:
http://www.domäne.de/alkis/wfs?

Wenn der Passwortschutz nur für die Domain www.domäne.de eingerichtet werden soll, dann würde ich den location Container im VirtualHost-Container dieser Domain unterbringen. Die betreffende Datei sollte sich im Verzeichnis sites-available befinden - Auskunft über die Zuständigkeit geben die ServerName und ServerAlias Direktiven.

Alle genannten if Container wiederum sollten innerhalb des location Containers <Location alkis/wfs> stehen, da der Passwortschutz ja nur unterhalb dieses Pfades greifen soll.

Edit:

Diese Alias-Direktive darf dann natürlich auch nur in dem betreffenden VirtualHost-Container stehen:

Alias /alkis/wfs /usr/lib/cgi-bin/mapserv.fcgi

.. sonst kann man den MapServer auf einer anderen Domain ohne Passwortschutz aufrufen

29.11.2016 11:55 | geändert: 29.11.2016 12:05

9 Stephan (Gast)

die vhost.conf liegt im Verzeichnis conf-enabled. Darin sind mehrere Alias-Diektive (mit unterschiedlichen Pfaden zu den Mapfiles) für den MapServer aufgeführt. Passwortgeschützt wird nur eine Alias-Direktive.

Der MapServer kann zwar über verschiedene Alias-Direktive angesprochen werden, liefert aber nur Daten, wenn auch ein Mapfile verlinkt ist, sonst kommt nur eine Meldung, dass er läuft, sofern diese einsehbar ist.

29.11.2016 14:56

10 Jörg Kruse

die vhost.conf liegt im Verzeichnis conf-enabled.

Normalerweise befinden sich dort nur Symlinks, die (aktivierte) Konfigdateien im Verzeichnis conf-availabe verlinken. Aber wie auch immer - in dem Fall ist der MapServer unter allen Domains erreichbar. Wenn das für dich OK ist, kannst du das so machen.

Nochmal zu deiner Frage:

aber mir ist nur noch nicht so recht klar, wo ich den allgemeinen if Container definiere: ich habe eine vhost.conf Datei in der diese oben beschriebenen Anweisungen stehen. Erstelle ich beispielsweise im conf-enabled Verzeichnis so eine globale if-container Datei?

Ich würde den <Location /alkis/wfs> Container mit den Auth-Direktiven dort platzieren, wo sich auch die dazugehörige Alias-Direktive befindet. Das schließt den "globalen" if-Container natürlich mit ein. Mit "global" bezog ich mich darauf, dass die Bedingung sich nicht auf einen bestimmten TYPENAME bezieht, also dort die Auth-Direktiven aufgeführt sind, die gleichermaßen für alle TYPENAMEs gelten. So musst du diese nicht achtmal hintereinander auführen, sondern nur einmal.

Mir war da aber noch ein Fehler unterlaufen, da fehlt noch der zweite Delimiter:

<If "%{QUERY_STRING} =~ /(?i)&TYPENAME=/">

Die Bedingung sorgt dafür, dass die Authentifizierung nur dann erfolgt, wenn der String "&TYPENAME=" im Query String enhalten ist. So kommt die Passwortabfrage dann z.B. nicht beim Query String "SERVICE=WFS&VERSION=1.1.0&REQUEST=GetCapabilities", also wo nur die Capabilities abgefragt werden.

29.11.2016 17:00 | geändert: 29.11.2016 17:00