Benutzer:Kirmse/Surfkontrolle
Dieses Script ist eine Neuimplementation des Scripts proxy.cgi
von Joachim Deckers.
Es soll den Lehrern nur die Links von seinen Schülern der aktuellen Unterrichtsstunde anzeigen.
Dazu wird ein Cronjob eingerichtet, der zu Beginn der Stunde die Anzahl der Zeilen des Logfiles speichert. Ab dieser Zahl wertet das Script das Logfile aus.
Es kann konfiguriert werden, ob Lehreraccounts anonymisiert werden und wenn ja, mit welchen String. Um die Namen angezeigt zu bekommen, braucht nicht mehr konfiguriert werden, welcher Name zu der IP gehört. Diese Daten werden aus dem LDAP geholt. Voraussetzung für diese Funktion ist die Einrichtung fester IPs.
Cronjob
<source lang="perl">
- !/usr/bin/perl
use warnings; use strict;
my $datei = 'access.log';
my $datei2 = 'startwert';
my $zaehler = 0;
- wir lesen die Datei access.log ein
open DATEI, '<', $datei; while (my $zeile = <DATEI>) {
$zaehler++;
}
- wir speichern den Zaehlerstand in der Datei 'startwert'
open DATEI, '>', $datei2; print DATEI $zaehler; close DATEI;
__END__ </source>
Das eigentliche Script
<source lang="perl">
- !/usr/bin/perl
use warnings; use strict;
use CGI::Carp qw(fatalsToBrowser); use Net::LDAP;
- ========================== Konfigurationsbereich ========================
my %config = (
- hier wird angegeben, ob die Aufrufe fuer Lehrer angezeigt werden sollen
- no: ausblenden, yes: anzeigen des Lehrerlogins
TEACHER => 'yes',
- hier koennen weitere Logins angegeben werden, die nicht angezeigt werden sollen
- Format: UNVISIBLE => [ 'ffeuerstein', 'hkirmse' ];
UNVISIBLE => [ ],
- hier wird der String angegeben, mit dem Lehrerlogins ersetzt werden
UNVISIBLE_STRING => 'xxxxx' );
- hier können sie das Format fuer das Datum und die Uhrzeit angeben. Die
- Platzhalter %%tag%%, %%monat%%, %%jahr%%, %%stunde%%, %%minute%%, %%sekunden%%
- werden durch aktuellen Werte ersetzt, wobei alle Werte der Zeit zweistellig sind
- durch das vorgegebene Format entsteht z.B. "1.1.2008 13:41:05"
my $format = '%%tag%%.%%monat%%.%%jahr%% %%stunde%%:%%minute%%:%%sekunden%%';
- hier wird das Logfile angegeben
my $logfile = 'access.log';
- ========================== Fehlermeldungen in der Tabelle ===============
my $fehler1 = 'es liegen keine Einträge im Logfile vor'; my $fehler2 = 'es gibt keine Aufrufe aus diesem Raum';
- ========================== HTML-Bereich =================================
- Die generierte HTML-Seite wird zusammengesetzt aus dem Kopf, der (eigentlichen)
- Tabelle und dem Fuss. Hier wird die Variable $head als HERE-Dokument bereitgestellt.
- Ab der 2. Zeile, die mit: "<!doctype html ..." beginnt, ist alles reines HTML.
- Sie können damit ohne Perlkenntnisse das Aussehen der Seite beeinflussen.
- Beachten Sie, dass %%aufrufer%%, "%%rechner%%", "%%raum%% und %%datum%% durch
- das Script durch die aktuellen Werte ersetzt werden
my $head =<<'KOPF'; <!doctype html public "-//W3C//DTD HTML 4.0 //EN"> <html> <head> <title>Surfkontrolle</title> <meta name="author" content="Hans-Dietrich Kirmse"> <style type="text/css"> </style> </head> <body background="/online/whttxtr2.jpg" text="#000000" bgcolor="#FFFFFF" link="#0000FF" alink="#0000C0" vlink="#FF0000">
%%aufrufer%% am PC "%%rechner%%" im Raum "%%raum%%" | %%datum%% |
Es wurden folgende Web-Seiten aufgerufen:
KOPF- die Variable $foot sollte normalerweise nicht geändert werden, da nur Copyrightvermerk.
- Falls dieser an irgendeiner anderen Stelle erscheint, ist dagegen natürlich nichts einzuwenden.
Zeit | Arbeitsplatz | Login | URL |
---|
© <a class="link" href="mailto:hd.kirmse@gmx.de">HD. Kirmse</a>, ERG Saalfeld/Thüringen, surfcontrol Version 0.1 - Oktober 2008
</body> </html> FUSS
- wenn keine Anmeldungen am Fileserver vorliegen, dann wird folgende Tabelle
- zurueckgegeben, der Platzhalter %%fehler%% wird dann ersetzt
my $error =<<'FEHLER';
%%fehler%% FEHLER
- Ende des Konfigurationsbereiches #################################
- ========================== weitere globalen Variablen ===================
my $ldap_base; # wird z.B. zu 'dc=erg,c=de' my $ip; # IP des Lehrer-PCs my $teacher; # Login des aufrufenden Lehrers my @teacher; # die Logins der Lehrer my $room; # der Raum my %pcs; # die PCs in diesem Raum my @daten; # Daten vom Logfile my $startwert; # die Zeile des Logfiles my $date; # Datum und Zeit des Aufrufs my $table; # die Tabelle der HTML-Seite my $datei = 'startwert'; # Datei mit Startwert fuer akt. Stunde
- ========================== Hauptprogramm ================================
- wir holen uns das Login des Lehrers, von dem das Script aufgerufen wurde
$teacher = $ENV{REMOTE_USER}; defined($teacher) or die "es liegt keine Anmeldung am Apache vor, $!\n";
- wir holen uns die IP des Rechners, von dem das Script aufgerufen wurde
$ip = $ENV{REMOTE_ADDR}; defined($ip) or die "ein unerwarteter Fehler ist aufgetreten, $!\n";
- wir holen uns die Lehrer aus dem LDAP
@teacher = &get_teacher;
- wir holen uns den Raum und die Rechner fuer diesen Raum
($room, %pcs) = &get_pcs; # %pcs: Key ist die IP, Value der Name
- wir holen uns den Startwert (entspricht der Startzeit)
$startwert = &get_startwert($datei);
- wir lesen das Logfile ein und erzeugen dabei gleich unsere Datenstruktur
- Diese haben folgende Struktur: $daten[x] = \($nr, $ip, $pc, $login, $zeit, $url);
@daten = &get_data($logfile, $startwert);
- falls ueberhaupt keine Aufrufe am Proxy vorliegen, brechen wir hier ab
if (scalar @daten == 0) { $error =~ s/%%fehler%%/$fehler1/; $table = $error; goto AUSGABE; }
- wir reduzieren jetzt (anhand der IP) die Daten auf die Anmeldungen im Raum
@daten = &filtern_nach_raum(\@daten, \%pcs);
- falls keine Aufrufe am Proxy von diesem Raum vorliegen, brechen wir ab
if (scalar @daten == 0) { $error =~ s/%%fehler%%/$fehler2/; $table = $error; goto AUSGABE; }
- wir blenden die Lehrer aus
@daten = &lehrer_ausblenden(1, \@daten, \@teacher, \%config);
- wir bauen jetzt die eigentliche Tabelle zusammen
- es wird hier nur das HTML herumgestrickt (incl. URLs)
$table = &get_table( \@daten, \%config, \%pcs ); AUSGABE:
- wir holen uns Datum und Uhrzeit (wird im Kopf der HTML-Seite angezeigt)
$date = &datum($format);
- wir bearbeiten den Kopf
$head =~ s/%%aufrufer%%/$teacher/; $head =~ s/%%datum%%/$date/; $head =~ s/%%rechner%%/$pcs{$ip}/; $head =~ s/%%raum%%/$room/;
- wir schicken die Seite zum Apache
print "Content-type: text/html\n\n"; print $head; print $table; print $foot;
- Ende des Hauptprogramms #############################
- ========================== Funktionen ===================================
sub _get_ldapbase { my $ldap_base = ; # wir durchsuchen die ldap.conf mit einer Regex open DATEI, '<', '/etc/ldap.conf' or die "konnte ldap.conf nicht oeffnen, $!\n"; while (my $zeile = <DATEI>) { if ($zeile =~ m/^\s*base\s+(\w.*\w)\s*$/) { $ldap_base = $1; last; } } close DATEI; return $ldap_base; } sub get_teacher { my ($ldap_base, $ldap, $mesg, @teacher); # wir verbinden uns mit den LDAP $ldap = Net::LDAP->new('127.0.0.1',version => 3) or die "$@"; $ldap->bind or die "konnte mich nicht mit dem Server verbinden"; # wir holen uns die Suchbasis $ldap_base = &_get_ldapbase; # wir holen uns nun die Lehrer $mesg = $ldap->search( base => "ou=LEHRER,o=SCHULE,$ldap_base", filter => "gidNumber=101", attrs => [ 'uid' ], scope => "sub"); mesg->code and die $mesg->error; # wir trennen uns vom LDAP $ldap->unbind; # wir stecken die Daten aus den Entries in eine Liste @teacher = (); foreach my $entry ($mesg->entries) { push @teacher, $entry->get_value('uid'); } return @teacher; } sub get_pcs { my $ip = shift; my ($ldap_base, $ldap, $mesg, $entry, $dn, $pc, $suchbasis, %rechner, $tmp, $raum); # wir verbinden uns mit den LDAP $ldap = Net::LDAP->new('127.0.0.1',version => 3) or die "$@"; $ldap->bind or die "konnte mich nicht mit dem Server verbinden"; # wir holen uns die Suchbasis $ldap_base = &_get_ldapbase; # wir holen uns den Raum zu der IP ($ip) $mesg = $ldap->search( base => "cn=DHCP Service Config,o=DHCP,$ldap_base", filter => "dhcpStatements=fixed-address $ip", attrs => [ 'dhcpHost' ], scope => "base"); mesg->code and die $mesg->error; # wir holen uns den DN ($entry) = $mesg->entries or die "konnte keinen dhcpHost finden"; $dn = $entry->dn; # wir spalten vom DN den Rechnernamen ab, den Rest nehmen wir gleich als Suchbasis ($pc, $suchbasis) = $dn =~ /^cn=([^,]*),(.*)$/; # wir holen uns die Rechner (sind im DN) und IPs zu diesen Raum $mesg = $ldap->search( base => $suchbasis, filter => "objectClass=dhcpHost", attrs => [ 'dhcpStatements' ]); $mesg->code and die $mesg->error; # wir trennen uns vom LDAP $ldap->unbind; # wir stecken die Daten aus den Entries in einen "normalen" Hash %rechner = (); foreach my $entry ($mesg->entries) { $dn = $entry->dn; ($pc) = $dn =~ /^cn=([^,]*),/ ; # die Klammer ist der Rechnername $tmp = $entry->get_value('dhcpStatements'); ($ip) = $tmp =~ /fixed-address (.*)$/; $rechner{$ip} = $pc; } # wir holen uns noch den Raum ($raum) = $suchbasis =~ /^cn=([^,]*),/ ; # die Klammer ist der Raum return ($raum,%pc); } sub get_startwert { my $datei = shift; open DATEI, "<", $datei; my $startwert = <DATEI>; # es wird nur eine Zeile eingelesen chomp($startwert); # eventuellen Zeilenumbruch entfernen close DATEI; # wenn keine Zahl eingelsen wurde, dann setzen wir den Startwert auf 0 unless ($startwert =~ /^\d*$/) { $startwert = 0; } return $startwert; } sub get_data { my $logfile = shift; my $startwert = shift; my $zaehler = 1; # entspricht Zeilennummer my ($ip, $login, $dummy, $zeit, $dummy2, $addr, $url, $dummy3, $url_orig); open DATEI, "<", $logfile; while (my $zeile = <DATEI>) { # wir erhoehen den Zaehler $zaehler++; if ($zaehler > $startwert) { # wir splitten die Zeile beim Leerzeichen ($ip, $login, $dummy, $zeit, $dummy2, $addr, $url, $dummy3) = split (/ /,$zeile,8); # bevor wir die URL bearbeiten, erzeugen wir eine Kopie $url_orig = $url; # wir vernichten zuerst alles was nach einem Doppelkreuz steht ($url) = $url =~ /^([^#]*)/; # wir vernichten alles was nach einem Fragezeichen steht ($url) = $url =~ /^([^?]*)/; # URL wird genommen, wenn am Ende steht: # .shtm | .shtml | .htm | .html | .cgi | .jsp | .php | .php3 | .php4 | .py | .pl if (($url =~ /\.(s?html?|cgi|jsp|php.?|py|pl)$/) # es sollen auch reine Domainangaben angezeigt werden z.B. www.heise.de or ($url =~ /^http.?:\/\/[.\w]*$/) ) { # wir holen uns noch die Zeit, das Datum wird verworfen ($zeit) = $zeit =~ /:(..:..:..)$/ ; my @values = ($ip, $login, $zeit, $url, $zaehler, $url_orig); # wird anonyme Liste push @daten, \@values; } } } close DATEI; return @daten; }
- --------------------------------------------------------------------
- Funktion: es werden nur die Datensaetze aus diesem Raum zurueckgegeben
- Aufruf: @daten = &filtern_nach_raum(\@daten, \%pcs);
- Input: $daten[x] = \($ip, $login, $zeit, $url, $zaehler, $url_orig);
- $pcs{$ip} = $rechnername;
- Output: $daten[x] = \($ip, $login, $zeit, $url, $zaehler, $url_orig);
- Update: 20.01.2008
- --------------------------------------------------------------------
sub filtern_nach_raum { my $lref_daten = shift; my $href_pcs = shift; my @daten = @{$lref_daten}; my %pcs = %{$href_pcs}; my ($ip, @temp); foreach my $nr (@daten) { my @liste = @{$nr}; # wir holen uns die IP $ip = $liste[0]; # nur wenn diese IP zu den Rechnern des Raumes gehoert if (defined($pcs{$ip})) { # dann wollen wir diesen Datensatz push @temp, \@liste; } } return @temp; }
- @daten = &lehrer_ausblenden( LOGIN, \@daten, \@teacher, \%config);
sub lehrer_ausblenden { my $nummer = shift; # welches Listenelement das Login ist - hier "1" my $lref_daten = shift; my $lref_teacher = shift; my $href_config = shift; my @daten = @{$lref_daten}; my @teacher = @{$lref_teacher}; my %config = %{$href_config}; my @unvisible = @{$config{UNVISIBLE}}; my $unvisiblestring = $config{UNVISIBLE_STRING}; my %teacher = (); foreach my $element (@teacher) { $teacher{$element} = 1; } my ($login, $anz); if ($config{TEACHER} eq 'no') { foreach my $datum (@daten) { my @tmp = @{$datum}; # holen wir das Login $login = $tmp[$nummer]; # wir ueberpruefen, ob es ein Lehrer ist if (exists($teacher{$login})) { $tmp[1] = $unvisiblestring; # Login ersetzen $datum = \@tmp; } else { $anz = grep /$login/, @unvisible; # skalarer Kontext - grep gibt Anzahl zurueck if ($anz > 0) { $tmp[$nummer] = $unvisiblestring; # Login ersetzen $datum = \@tmp; } } } } return @daten; }
- --------------------------------------------------------------------
- Funktion: es wird die eigentliche Tabelle zusammengesetzt
- Aufruf: $table = &get_table(\@daten, \%config, \%pcs);
- Input: @daten = die bearbeiteten Daten
- %config = die Konfiguration
- Output: $tabelle = die Tabelle als ein großer String
- Update: 20.01.2008
- --------------------------------------------------------------------
sub get_table # $ip, $login, $zeit, $url, $zaehler, $url_orig { my $lref_daten = shift; my $href_config = shift; my $href_pcs = shift; my @daten = @{$lref_daten}; my %config = %{$href_config}; my %pcs = %{$href_pcs}; my $tabelle = ; my (@temp, $login, $farbe); foreach my $datum (@daten) { @temp = @{$datum}; $tabelle .= ''; # Beginn der HTML-Zeile # ---------- die 1. Spalte: "Zeit" ------------ $tabelle .= ''.$temp[2].'';
# -------- die 2. Spalte: "Arbeitsplatz" ------------
$tabelle .= ''.$pcs{$temp[0]}.''; # Rechnername
# -------- die 3. Spalte: "Login" ---------------
$tabelle .= ''.$temp[1].''; # Login
# -------- die 4. Spalte: "URL" ---------------
$tabelle .= '<a href="'.$temp[5].'">'.$temp[3].'</a>';
# -------------------------------------------------
$tabelle .= ''."\n"; # Ende der HTML-Zeile } return $tabelle; }
- --------------------------------------------------------------------
- Funktion: Hilfsfunktion zur zweistelligen Darstellung der Zeitdaten
- Aufruf: $sek = &zweistellig($sek);
- Input: $sek oder $min oder $std
- Output: diese Werte, aber zweistellig
- Update: 1.01.2008
- --------------------------------------------------------------------
sub zweistellig { my $datum = shift; if ($datum < 10) { $datum = '0'.$datum } return $datum; }
- --------------------------------------------------------------------
- Funktion: liefert formatierten Datumsstring
- Aufruf: $date = &datum($format);
- Input: String fuer das Ausgabeformat
- Output: String mit Datum und Uhrzeit
- Update: 1.01.2008
- --------------------------------------------------------------------
sub datum { my $format = shift; my @date = localtime(); my $sek = $date[0]; my $min = $date[1]; my $std = $date[2]; my $tag = $date[3]; my $monat = $date[4] + 1; my $jahr = $date[5] + 1900; $sek = &zweistellig($sek); $min = &zweistellig($min); $std = &zweistellig($std); my $rueckgabe = $format; $rueckgabe =~ s/%%tag%%/$tag/; $rueckgabe =~ s/%%monat%%/$monat/; $rueckgabe =~ s/%%jahr%%/$jahr/; $rueckgabe =~ s/%%stunde%%/$std/; $rueckgabe =~ s/%%minute%%/$min/; $rueckgabe =~ s/%%sekunden%%/$sek/; return $rueckgabe; } __END__ </source>