From 4326f9ced3e4c9ff498f2c710bda2b2f89fe284c Mon Sep 17 00:00:00 2001 From: Peter Gietz <peter@daasi.de> Date: Fri, 16 Dec 2011 13:17:31 +0000 Subject: [PATCH] Initial Cechin of the version with proxy rewrite path feature git-svn-id: https://textgridlab.org/svn/textgrid/trunk/middleware/tgauth@11703 7c539038-3410-0410-b1ec-0f2a7bf1c452 --- .../etc/pwReset.conf | 0 .../etc/pwReset.css | 0 .../etc/pwReset.sys | 428 +++++++ .../etc/pwReset.sys~ | 417 +++++++ .../pwReset.pl | 1062 +++++++++++++++++ .../svn-commit.tmp | 5 + 6 files changed, 1912 insertions(+) create mode 100644 info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.conf create mode 100644 info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.css create mode 100644 info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys create mode 100644 info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys~ create mode 100755 info.textgrid.middleware.tgauth.passwordReset/pwReset.pl create mode 100644 info.textgrid.middleware.tgauth.passwordReset/svn-commit.tmp diff --git a/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.conf b/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.conf new file mode 100644 index 0000000..e69de29 diff --git a/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.css b/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.css new file mode 100644 index 0000000..e69de29 diff --git a/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys b/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys new file mode 100644 index 0000000..9697c50 --- /dev/null +++ b/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys @@ -0,0 +1,428 @@ +#reading file /usr/local/src/devel/IdM/pwReset/etc/pwReset-unclean.sys + +progname = "pwReset" + +version = 0.1 + +date = "2011-01-11" +<author> + name = "Peter Gietz" + org= "DAASI International GmbH" + mail = "peter.gietz@daasi.de" +</author> + +<copyright> +text1 = Copyright (c) 2005 DAASI International GmbH +text2 = This library is free software; you can redistribute it and/or \ +modify it under the same terms as Perl itself. +</copyright> + +progshortdescr = "Webtool for password reset" + +<progdescription> +text1 ="pwReset is a simple webtool for password reset. It works as follows: \ +When first started (status = 'none') it tests whether cookies are enabled \ +(via redirect to status testcookie), and returns a respective message to the \ +user if not, with a link to start all over again." +text2 = "If the cookie is \ +retrievable an Apache-session is established (information is stored in files \ +on the server) and a mask is shown to the user, where she can input an ID \ +(loginid, email address or TextGrid ID). If mode 'oneinputfield' is set in the \ +configuration there is one input field for either of them, if not, there are \ +three separate input fields." +text3 = "After pressing the \"verify me\" button the programm is called with \ +'sendlink' status where the programm first checks whether the input values \ +conform to configurable regular expressions \ +(e.g. \".*\\@.*\" for email address), \ +returns respective errors if they do not conform. If they do, the programm \ +looks up the ID in the LDAP server and retrieves an email address from there \ +(even if the ID was the mail address). \ +If the ID was found a separate sessionid (not the ID in the cookie) is created \ +and concatenated to an url that calls status 'printform' and that url is sent \ +to the email address with some configurable text." +text4 = "If the user clicks on the url in that email, the sessionid in the \ +query is compared with the respective id stored in the session and if they \ +are identical a form to input the password (in two separate input fields) is \ +displayed. After pressing the button \"reset password\" the program is \ +restarted with the status 'modify'." +text5 = "In this last status again the matching of sessionid is checked, as \ +well as the matching of password and retyped password. Then it is checked how \ +often the session has been used (there is a counter for every access) and \ +whether that number is less than a configurable maximum. The password value is \ +then tested against the configurable regular expression that defines the \ +password policy. If any of these checks fail a respective error message is \ +displayed, otherwise the program looks up the entry and changes the password \ +in the LDAP server." +text6 = "At any error state, the user either gets the last input screen (e.g. \ +when wrong values have been inputted) or a link to restart the process (e.g. if \ +session is not valid any more). A lot of things are configurable (see the \ +single options below). HTTPS as well as TLS for the LDAP connection can be \ +enforced. All activities can be logged. Configuration concerning input fields \ +(labels, regExp, etc.) happens in a hash at the beginning of the source code. \ +Everything else can be configured in a Apache style configuration file. All \ +passwords needed (for LDAP access and for SMTP auth) are sored in a separate \ +file." +text7= "If you start the program with status 'adminhelp' \ +(/url/?status=adminhelp) this manpage is displayed in the browser. You can \ +additionally specify one single configuration option to only have the help to \ +that option displayed (e.g. /url/?status=adminhelp&helpfeature=emailconfig). \ +The adminhelp feature can be turned off in the configuration." + +</progdescription> + + +<bugs> +text1 = "Please report bugs to peter.gietz@daasi.de" +text2 = "some todos are: " +text3 = "more than one regExp per inputfield" +text4 = "multilanguage support is already designed but needs gettext translations" +text5 = "configurable css file does not work properly yet" +</bugs> + +<additions example> +text1 = "For getting this manpage: " +text2 = " pwReset.pl -h" +text3 = " " +</additions> + +<additions requirements> +text1 = "Following modules are required: " +text2 = "* Config::General" +text5 = "* File::Basename" +text6 = "* File::Copy" +text7 = "* File::Flock" +text8 = "* File::Temp" +text9 = "* Getopt::Std" +text10 = "* IO::Prompt" +text11 = "* Log::Log4perl" +text12 = "* LWP::Authen::Ntlm" +text13 = "* MIME::Base64" +text14 = "* Net::LDAP" +text15 = "* Net::SMTP" +text18 = "* Text::Wrap" +</additions> + +<options loglevel> + key = "l" + must = 0 + description = "Loglevel for controlling logmessages." + description_de = "Loglevel zur Kontrolle des Logging." + arg = 1 + argtype = "skalar" + values = "no, all, debug, info, warn, error, fatal" + default = "warn" +</options> + +<options logfile> + key = "L" + must = 0 + description = "Name of the logfile with absolute or relative path. " + description_de = "Name der Logdatei mit absolutem oder relativem Pfad." + arg = 1 + argtype = "filename_add_subdir_log" + default = "pwReset.log" +</options> + +<options debugmode> + key = "d" + must = 0 + description = "Sets debug mode to on." + description_de = "Stellt den Debug-Modus an." + arg = 0 +</options> + +<options verbose> + key = "v" + must = 0 + description = "Sets verbose mode which makes the program quite chatty." + description_de = "Stellt den Verbose-Modus an, womit das Programm \ + gespraechiger wird." + arg = 0 +</options> + +<options language> + key = "G" + must = 0 + description = "Sets the language for output like this one" + description_de = "Setzt die Sprache fuer Ausgaben wie diese. " + arg = 1 + default = en + values = en, de +</options> + +<options passwordfile> + key = "p" + description = "Name of the password file which contains the secrets \ + the programm must know to connect to data bases etc. \ + The format for all lines of this file must be: \ + <token><blank><password> where <token> has to be the \ + option label which defines the database such as e.g. \ + \"outputuri\". " + description_de = "Name einer Datei, welche die Passworte enthaelt, \ + die das Programm wissen muss um Datenbanken zu \ + kontaktieren etc. Das Format dieser Datei ist: \ + <Token><Leerzeichen><Passwort> wobei <Token> ein \ + Optionsname sein muss, welcher die Datenbank \ + definiert wie z.B. \"outputuri\". " + arg = 1 + default = "pwReset.secret" + argtype = "filename_exist" +</options> + +<options configfile> + key = "c" + must = 0 + description = Name of the user config file with absolute or \ + relative path." + description_de = Name der benutzerdefinierten Konfigurationsdatei \ + mit absolutem oder relativem Pfad." + arg = 1 + argtype = "filename_exist_subdir_etc" + default = "./etc/pwReset.conf" +</options> + + +<options printhelp> + key = "h" + must = 0 + description = "prints out the manpage" + description_de = "Druckt die Manpage aus" + arg = 0 +</options> + + +<options helpfeature> + key = "H" + must = 0 + description = "prints out description of the feature referenced by \ + commandline flag or config file token. " + description_de = "Druckt die Beschreibung der Option aus, die \ + entweder ueber den Kommandozeilen- oder ueber den \ + Konfigurationsdatei-Parameter spezifiziert wird." + arg = 1 +</options> + + +<options emailconfig> + key = "e" + must = 0 + description = "specifies the SMTP-communication of the program, in a \ + string with token-value pairs, format: \ + \#token1=value1;\#token2=value; etc. \ + Following tokens are understood: \ + \#smtprelay sets the mailserver from which the mail should \ + be sent \ + \#from sets the from address of the mail to be sent. \ + \#to sets the mailaddress of the administrator to whom mails \ + should be sent. More than one address can be separated by \ + comma. \ + \#subjectpart sets a prefix that will be included in the \ + subject line.\n\ + \#hello sets the smtpclient name.\n\ + \#smtpuser sets the user name SMTP Auth authentication. \ + In this case the corresponding password has to \ + be stored in the passwordfile (-p, default is \ + dbconnector.secret) behind the token smtpauth." + description_de = "Spezifiziert das Mail-Interface des Programms \ + wodurch in bestimmten Faellen automatische E-Mails an den \ + Administrator geschickt werden koennen. Das Format besteht \ + aus mit Semikolon getrennten Schluessel-Wert-Paaren, wobei \ + der Schluessel mit einem vorgestellten \# gekennzeichnet \ + wird: \n\ + \#token1=value1;\#token2=value; etc. \n\ + Folgende Tokens werden unterstuetzt: \n\ + \#smtprelay spezifiziert den Mailserver von welchem aus die \ + Mail geschickt werden soll.\n\ + \#from spezifiziert die Sender-E-Mail-Adresse.\n\ + \#to spezifiziert die Ziel-Adresse des Administratoren, an \ + den die automatischen Mails geschickt werden sollen. \ + Hierbei koennen mehrere mit Komma separierte Adressen \ + angegeben werden.\n\ + \#subjectpart setzt ein Praefix fuer die verschiedenen \ + Mail-Subjects.\n\ + \#hello setzt den smtpclient-Namen.\n\ + \#smtpuser setzt den User-Namen für SMTP \ + Auth-Authentifizierung. Wenn dieser gesetzt ist, muss \ + das korrespondierende Passwort in der mit \ + passwordfile spezifizierten Datei (-p, Voreinstellung \ + ist dbconnector.secret) hinter dem Stichwort smtpauth \ + eingetragen sein." + arg = 1 + argtype = token_emailinfo + default = 0 +</options> + +<options cssfile> + key = "C" + must = 0 + description = "Name of the CSS file with absolute or \ + relative path." + description_de = "Name der CSS-Datei \ + mit absolutem oder relativem Pfad." + arg = 1 + argtype = "filename_exist_subdir_etc" + default = "./etc/pwReset.css" +</options> + +<options sessionpath> + key = "s" + must = 0 + description = "Path (relative or absolute) of the directory where to store session information." + description_de = "Pfad (relativ oder absolut) zum Verzeichnis, in dem die Session-Informationen gespeichert werden." + arg = 1 + argtype = "filename_dir" + default = "./sessions" +</options> + +<options sessionlockpath> + key = "S" + must = 0 + description = "Path (relative or absolute) of the directory where to store session lock information." + description_de = "Pfad (relativ oder absolut) zum Verzeichnis, in dem die Session-Lock-Informationen gespeichert werden." + arg = 1 + argtype = "filename_dir" + default = "./locks" +</options> + +<options sessiontime> + key = "T" + must = 0 + description = "Sets the duration of a cookie session. Format: +<number><unit>, where you can specify the following units: s for seconds, m for minutes, h for hours, d for days, M for months, and y for years" + description_de = "definiert die Lebensdauer einer cookie session. Format: +<Nummer><Einheit>, wobei Einheit s für Sekunden, m für Minuten, h für Stunden, d für Tage, M für Monate, und y für Jahre steht. " + arg = 1 + default = "2d" +</options> + + +<options title> + key = "t" + must = 1 + description = "Sets the page title." + description_de = "definiert den Seitentitel." + arg = 1 + default = "Password Reset Tool" +</options> + +<options charset> + key = "X" + must = 1 + description = "Sets the character set." + description_de = "definiert die Zeichensatzkodierung." + arg = 1 + default = "utf-8" + values = "utf-8, iso5889" +</options> + +<options bgcolor> + key = "b" + must = 0 + description = "Sets the background color" + description_de = "definiert die Hintergrundsfarbe." + arg = 1 + default = "ffffff" +</options> + +<options linkmail> + key = "K" + must = 0 + description = "Sets the text for the link mail" + description_de = "Definiert die link mail" + arg = 1 + default = "You receive this message, because someone (probably you) requested $ to reset your Password. $ You can do this with following link: %URL%" +</options> + + +<options meta> + key = "M" + must = 0 + description = "Sets the metatags. Format: name1:content1|name2:content2" + description_de = "definiert die Meta-Tags. \ + Format: name1:content1|name2:content2" + arg = 1 + default = "Robots:noindex,nofollow" +</options> + +<options ldapuri> + key = "u" + must = 1 + description = "URI for input of LDAP data. \n\ + The format for the LDAP URI is (see RFC 4516): \ + ldap://<host>[:<port>]/<basedn>?<attributes>?<scope>?\n\ + <filter>?<extension> where <extension> can by now only \ + be either bindname=<bindname> for authenticating to the \ + server. In this case the corresponding password has to \ + be stored in the passwordfile (-p, default is \ + dbconnector.secret) behind the token inputuri. \ + The second extension supported is \ + config=<Slapd-configuration file>, which is only \ + needed with inputformat _REPL." + description_de = "URI fuer Eingabedaten aus einem LDAP-Server.\n\ + Das Format fuer die LDAP-URI (siehe RFC 4516) ist: \ + ldap://<host>[:<port>]/<basedn>?<attributes>?<scope>?\n\ + <filter>?<extension> wobei als <extension> gegenwaertig \ + folgende Erweiterungen unterstuetzt werden:\n\ + bindname=<bindname> zur Authentifizierung am Server, \ + wobei das korrespondierende Passwort in der mit \ + passwordfile spezifizierten Datei (-p, Voreinstellung \ + ist dbconnector.secret) hinter dem Stichwort inputuri \ + eingetragen sein muss.\n\ + config=<Slapd-configuration file>, womit die zu \ + verwendende Open-LDAP-Konfigurationsdatei spezifiziert \ + wird, was nur im Zusammenhang mit dem inputformat (-I) \ + \"_REPL\" benoetigt wird." + arg = 1 + argtype = "uri" +</options> + +<options pwhash> + key = "P" + must = 1 + description = "Specifies the password hash algorithm" + description_de = "definiert den Passwort-Hash-Algorhitmus" + arg = 1 + default = "SSHA" + values = "SSHA, MD5, CRYPT" +</options> + +<options forcehttps> + key = "f" + must = 0 + description = "Forces https." + description_de = "Erzwingt https." + arg = 0 +</options> + +<options oneinputfield> + key = "o" + must = 0 + description = "Combines all ID-inputfields to one and creates a complex filter" + description_de = "vereinigt alle ID-Eingabefelder in eins und baut entsprechende Filter auf" + arg = 0 +</options> + +<options forcetls> + key = "z" + must = 0 + description = "Forces START_TLS in LDAP-connection" + description_de = "Erzwingt START_TLS in LDAP-Verbindung" + arg = 0 +</options> + + +<options rewritepath> + key = "r" + must = 0 + description = "Rewrites path, which might be needed for proxying. \ + Format: <regexp>=><string>;<regexp>=><string>;..." + description_de = "Schreibt den Pfad um, was bei Proxying notwendig \ + sein kann. Format: <regexp>=><string>;<regexp>=><string>;..." + arg = 0 +</options> + +<options enableadminhelp> + key = "a" + must = 0 + description = "Allows display of adminhelp" + description_de = "Erlaubt die Anzeige der Admin-Hilfe" + arg = 0 +</options> diff --git a/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys~ b/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys~ new file mode 100644 index 0000000..73546dc --- /dev/null +++ b/info.textgrid.middleware.tgauth.passwordReset/etc/pwReset.sys~ @@ -0,0 +1,417 @@ +#reading file /usr/local/src/devel/IdM/pwReset/etc/pwReset-unclean.sys + +progname = "pwReset" + +version = 0.1 + +date = "2011-01-11" +<author> + name = "Peter Gietz" + org= "DAASI International GmbH" + mail = "peter.gietz@daasi.de" +</author> + +<copyright> +text1 = Copyright (c) 2005 DAASI International GmbH +text2 = This library is free software; you can redistribute it and/or \ +modify it under the same terms as Perl itself. +</copyright> + +progshortdescr = "Webtool for password reset" + +<progdescription> +text1 ="pwReset is a simple webtool for password reset. It works as follows: \ +When first started (status = 'none') it tests whether cookies are enabled \ +(via redirect to status testcookie), and returns a respective message to the \ +user if not, with a link to start all over again." +text2 = "If the cookie is \ +retrievable an Apache-session is established (information is stored in files \ +on the server) and a mask is shown to the user, where she can input an ID \ +(loginid, email address or TextGrid ID). If mode 'oneinputfield' is set in the \ +configuration there is one input field for either of them, if not, there are \ +three separate input fields." +text3 = "After pressing the \"verify me\" button the programm is called with \ +'sendlink' status where the programm first checks whether the input values \ +conform to configurable regular expressions \ +(e.g. \".*\\@.*\" for email address), \ +returns respective errors if they do not conform. If they do, the programm \ +looks up the ID in the LDAP server and retrieves an email address from there \ +(even if the ID was the mail address). \ +If the ID was found a separate sessionid (not the ID in the cookie) is created \ +and concatenated to an url that calls status 'printform' and that url is sent \ +to the email address with some configurable text." +text4 = "If the user clicks on the url in that email, the sessionid in the \ +query is compared with the respective id stored in the session and if they \ +are identical a form to input the password (in two separate input fields) is \ +displayed. After pressing the button \"reset password\" the program is \ +restarted with the status 'modify'." +text5 = "In this last status again the matching of sessionid is checked, as \ +well as the matching of password and retyped password. Then it is checked how \ +often the session has been used (there is a counter for every access) and \ +whether that number is less than a configurable maximum. The password value is \ +then tested against the configurable regular expression that defines the \ +password policy. If any of these checks fail a respective error message is \ +displayed, otherwise the program looks up the entry and changes the password \ +in the LDAP server." +text6 = "At any error state, the user either gets the last input screen (e.g. \ +when wrong values have been inputted) or a link to restart the process (e.g. if \ +session is not valid any more). A lot of things are configurable (see the \ +single options below). HTTPS as well as TLS for the LDAP connection can be \ +enforced. All activities can be logged. Configuration concerning input fields \ +(labels, regExp, etc.) happens in a hash at the beginning of the source code. \ +Everything else can be configured in a Apache style configuration file. All \ +passwords needed (for LDAP access and for SMTP auth) are sored in a separate \ +file." +text7= "If you start the program with status 'adminhelp' \ +(/url/?status=adminhelp) this manpage is displayed in the browser. You can \ +additionally specify one single configuration option to only have the help to \ +that option displayed (e.g. /url/?status=adminhelp&helpfeature=emailconfig). \ +The adminhelp feature can be turned off in the configuration." + +</progdescription> + + +<bugs> +text1 = "Please report bugs to peter.gietz@daasi.de" +text2 = "some todos are: " +text3 = "more than one regExp per inputfield" +text4 = "multilanguage support is already designed but needs gettext translations" +text5 = "configurable css file does not work properly yet" +</bugs> + +<additions example> +text1 = "For getting this manpage: " +text2 = " pwReset.pl -h" +text3 = " " +</additions> + +<additions requirements> +text1 = "Following modules are required: " +text2 = "* Config::General" +text5 = "* File::Basename" +text6 = "* File::Copy" +text7 = "* File::Flock" +text8 = "* File::Temp" +text9 = "* Getopt::Std" +text10 = "* IO::Prompt" +text11 = "* Log::Log4perl" +text12 = "* LWP::Authen::Ntlm" +text13 = "* MIME::Base64" +text14 = "* Net::LDAP" +text15 = "* Net::SMTP" +text18 = "* Text::Wrap" +</additions> + +<options loglevel> + key = "l" + must = 0 + description = "Loglevel for controlling logmessages." + description_de = "Loglevel zur Kontrolle des Logging." + arg = 1 + argtype = "skalar" + values = "no, all, debug, info, warn, error, fatal" + default = "warn" +</options> + +<options logfile> + key = "L" + must = 0 + description = "Name of the logfile with absolute or relative path. " + description_de = "Name der Logdatei mit absolutem oder relativem Pfad." + arg = 1 + argtype = "filename_add_subdir_log" + default = "pwReset.log" +</options> + +<options debugmode> + key = "d" + must = 0 + description = "Sets debug mode to on." + description_de = "Stellt den Debug-Modus an." + arg = 0 +</options> + +<options verbose> + key = "v" + must = 0 + description = "Sets verbose mode which makes the program quite chatty." + description_de = "Stellt den Verbose-Modus an, womit das Programm \ + gespraechiger wird." + arg = 0 +</options> + +<options language> + key = "G" + must = 0 + description = "Sets the language for output like this one" + description_de = "Setzt die Sprache fuer Ausgaben wie diese. " + arg = 1 + default = en + values = en, de +</options> + +<options passwordfile> + key = "p" + description = "Name of the password file which contains the secrets \ + the programm must know to connect to data bases etc. \ + The format for all lines of this file must be: \ + <token><blank><password> where <token> has to be the \ + option label which defines the database such as e.g. \ + \"outputuri\". " + description_de = "Name einer Datei, welche die Passworte enthaelt, \ + die das Programm wissen muss um Datenbanken zu \ + kontaktieren etc. Das Format dieser Datei ist: \ + <Token><Leerzeichen><Passwort> wobei <Token> ein \ + Optionsname sein muss, welcher die Datenbank \ + definiert wie z.B. \"outputuri\". " + arg = 1 + default = "pwReset.secret" + argtype = "filename_exist" +</options> + +<options configfile> + key = "c" + must = 0 + description = Name of the user config file with absolute or \ + relative path." + description_de = Name der benutzerdefinierten Konfigurationsdatei \ + mit absolutem oder relativem Pfad." + arg = 1 + argtype = "filename_exist_subdir_etc" + default = "./etc/pwReset.conf" +</options> + + +<options printhelp> + key = "h" + must = 0 + description = "prints out the manpage" + description_de = "Druckt die Manpage aus" + arg = 0 +</options> + + +<options helpfeature> + key = "H" + must = 0 + description = "prints out description of the feature referenced by \ + commandline flag or config file token. " + description_de = "Druckt die Beschreibung der Option aus, die \ + entweder ueber den Kommandozeilen- oder ueber den \ + Konfigurationsdatei-Parameter spezifiziert wird." + arg = 1 +</options> + + +<options emailconfig> + key = "e" + must = 0 + description = "specifies the SMTP-communication of the program, in a \ + string with token-value pairs, format: \ + \#token1=value1;\#token2=value; etc. \ + Following tokens are understood: \ + \#smtprelay sets the mailserver from which the mail should \ + be sent \ + \#from sets the from address of the mail to be sent. \ + \#to sets the mailaddress of the administrator to whom mails \ + should be sent. More than one address can be separated by \ + comma. \ + \#subjectpart sets a prefix that will be included in the \ + subject line.\n\ + \#hello sets the smtpclient name.\n\ + \#smtpuser sets the user name SMTP Auth authentication. \ + In this case the corresponding password has to \ + be stored in the passwordfile (-p, default is \ + dbconnector.secret) behind the token smtpauth." + description_de = "Spezifiziert das Mail-Interface des Programms \ + wodurch in bestimmten Faellen automatische E-Mails an den \ + Administrator geschickt werden koennen. Das Format besteht \ + aus mit Semikolon getrennten Schluessel-Wert-Paaren, wobei \ + der Schluessel mit einem vorgestellten \# gekennzeichnet \ + wird: \n\ + \#token1=value1;\#token2=value; etc. \n\ + Folgende Tokens werden unterstuetzt: \n\ + \#smtprelay spezifiziert den Mailserver von welchem aus die \ + Mail geschickt werden soll.\n\ + \#from spezifiziert die Sender-E-Mail-Adresse.\n\ + \#to spezifiziert die Ziel-Adresse des Administratoren, an \ + den die automatischen Mails geschickt werden sollen. \ + Hierbei koennen mehrere mit Komma separierte Adressen \ + angegeben werden.\n\ + \#subjectpart setzt ein Praefix fuer die verschiedenen \ + Mail-Subjects.\n\ + \#hello setzt den smtpclient-Namen.\n\ + \#smtpuser setzt den User-Namen für SMTP \ + Auth-Authentifizierung. Wenn dieser gesetzt ist, muss \ + das korrespondierende Passwort in der mit \ + passwordfile spezifizierten Datei (-p, Voreinstellung \ + ist dbconnector.secret) hinter dem Stichwort smtpauth \ + eingetragen sein." + arg = 1 + argtype = token_emailinfo + default = 0 +</options> + +<options cssfile> + key = "C" + must = 0 + description = "Name of the CSS file with absolute or \ + relative path." + description_de = "Name der CSS-Datei \ + mit absolutem oder relativem Pfad." + arg = 1 + argtype = "filename_exist_subdir_etc" + default = "./etc/pwReset.css" +</options> + +<options sessionpath> + key = "s" + must = 0 + description = "Path (relative or absolute) of the directory where to store session information." + description_de = "Pfad (relativ oder absolut) zum Verzeichnis, in dem die Session-Informationen gespeichert werden." + arg = 1 + argtype = "filename_dir" + default = "./sessions" +</options> + +<options sessionlockpath> + key = "S" + must = 0 + description = "Path (relative or absolute) of the directory where to store session lock information." + description_de = "Pfad (relativ oder absolut) zum Verzeichnis, in dem die Session-Lock-Informationen gespeichert werden." + arg = 1 + argtype = "filename_dir" + default = "./locks" +</options> + +<options sessiontime> + key = "T" + must = 0 + description = "Sets the duration of a cookie session. Format: +<number><unit>, where you can specify the following units: s for seconds, m for minutes, h for hours, d for days, M for months, and y for years" + description_de = "definiert die Lebensdauer einer cookie session. Format: +<Nummer><Einheit>, wobei Einheit s für Sekunden, m für Minuten, h für Stunden, d für Tage, M für Monate, und y für Jahre steht. " + arg = 1 + default = "2d" +</options> + + +<options title> + key = "t" + must = 1 + description = "Sets the page title." + description_de = "definiert den Seitentitel." + arg = 1 + default = "Password Reset Tool" +</options> + +<options charset> + key = "X" + must = 1 + description = "Sets the character set." + description_de = "definiert die Zeichensatzkodierung." + arg = 1 + default = "utf-8" + values = "utf-8, iso5889" +</options> + +<options bgcolor> + key = "b" + must = 0 + description = "Sets the background color" + description_de = "definiert die Hintergrundsfarbe." + arg = 1 + default = "ffffff" +</options> + +<options linkmail> + key = "K" + must = 0 + description = "Sets the text for the link mail" + description_de = "Definiert die link mail" + arg = 1 + default = "You receive this message, because someone (probably you) requested $ to reset your Password. $ You can do this with following link: %URL%" +</options> + + +<options meta> + key = "M" + must = 0 + description = "Sets the metatags. Format: name1:content1|name2:content2" + description_de = "definiert die Meta-Tags. \ + Format: name1:content1|name2:content2" + arg = 1 + default = "Robots:noindex,nofollow" +</options> + +<options ldapuri> + key = "u" + must = 1 + description = "URI for input of LDAP data. \n\ + The format for the LDAP URI is (see RFC 4516): \ + ldap://<host>[:<port>]/<basedn>?<attributes>?<scope>?\n\ + <filter>?<extension> where <extension> can by now only \ + be either bindname=<bindname> for authenticating to the \ + server. In this case the corresponding password has to \ + be stored in the passwordfile (-p, default is \ + dbconnector.secret) behind the token inputuri. \ + The second extension supported is \ + config=<Slapd-configuration file>, which is only \ + needed with inputformat _REPL." + description_de = "URI fuer Eingabedaten aus einem LDAP-Server.\n\ + Das Format fuer die LDAP-URI (siehe RFC 4516) ist: \ + ldap://<host>[:<port>]/<basedn>?<attributes>?<scope>?\n\ + <filter>?<extension> wobei als <extension> gegenwaertig \ + folgende Erweiterungen unterstuetzt werden:\n\ + bindname=<bindname> zur Authentifizierung am Server, \ + wobei das korrespondierende Passwort in der mit \ + passwordfile spezifizierten Datei (-p, Voreinstellung \ + ist dbconnector.secret) hinter dem Stichwort inputuri \ + eingetragen sein muss.\n\ + config=<Slapd-configuration file>, womit die zu \ + verwendende Open-LDAP-Konfigurationsdatei spezifiziert \ + wird, was nur im Zusammenhang mit dem inputformat (-I) \ + \"_REPL\" benoetigt wird." + arg = 1 + argtype = "uri" +</options> + +<options pwhash> + key = "P" + must = 1 + description = "Specifies the password hash algorithm" + description_de = "definiert den Passwort-Hash-Algorhitmus" + arg = 1 + default = "SSHA" + values = "SSHA, MD5, CRYPT" +</options> + +<options forcehttps> + key = "f" + must = 0 + description = "Forces https." + description_de = "Erzwingt https." + arg = 0 +</options> + +<options oneinputfield> + key = "o" + must = 0 + description = "Combines all ID-inputfields to one and creates a complex filter" + description_de = "vereinigt alle ID-Eingabefelder in eins und baut entsprechende Filter auf" + arg = 0 +</options> + +<options forcetls> + key = "z" + must = 0 + description = "Forces START_TLS in LDAP-connection" + description_de = "Erzwingt START_TLS in LDAP-Verbindung" + arg = 0 +</options> + +<options enableadminhelp> + key = "a" + must = 0 + description = "Allows display of adminhelp" + description_de = "Erlaubt die Anzeige der Admin-Hilfe" + arg = 0 +</options> diff --git a/info.textgrid.middleware.tgauth.passwordReset/pwReset.pl b/info.textgrid.middleware.tgauth.passwordReset/pwReset.pl new file mode 100755 index 0000000..0a6c4f3 --- /dev/null +++ b/info.textgrid.middleware.tgauth.passwordReset/pwReset.pl @@ -0,0 +1,1062 @@ +#!/usr/bin/perl -w + +# pwreset.pl + +# author: Peter Gietz + +# Version 0.01 +# 10.1.2011 +# copyright DAASI international GmbH 2011 + +use warnings; + +unshift (@INC,"../lib"); +$ENV{PATH}= "/bin:/usr/bin/"; +delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' }; +umask(077); + + +use vars qw($VERSION $NAME $PATH $PROT); + +$VERSION = "0.1"; +$NAME = 'pwReset'; +$PATH = '/cgi-bin'; + +$PROT = 'http'; + + +use HTTP::Daemon; +use HTTP::Status; + + +use DAASIlib::CONF qw (is_debug); +use DAASIlib::LDAP; +use DAASIlib::LOG; +use DAASIlib::Convert; +use DAASIlib::Data; +use DAASIlib::SMTP; +#use DAASIlib::XML; +#use DAASIlib::Objects; +use DAASIlib::Gettext; + +use Crypt::SmbHash qw(lmhash nthash); +use Digest::MD5 qw(md5 md5_hex md5_base64); +use Digest::SHA1 qw(sha1 sha1_hex sha1_base64); +use MIME::Base64; +use Encode qw(encode); +use Unicode::Map8; +use Unicode::String qw(utf16); +use Apache::Session::File; +require HTML::Template; +use URI::Escape; + +use utf8; + +use strict; +use vars qw( $count $config ); +use CGI qw/:standard :html3/; +use CGI::Fast; + +local $count =0; + +my $conf = new DAASIlib::CONF; + +#my $object = new DAASIlib::Objects; + +our $data = new DAASIlib::Data; +our ($progname, $progpath, $etcdir, $sysconfig) = $data->getProgramFiles($0); +my $gt = $conf->loadConfig($sysconfig, $progpath, $etcdir, 0, $progname); +$data->init($progname, $conf->{data}{passwordfile}); + +my $is_verbose = $conf->{data}{verbose}; + +my $log = new DAASIlib::LOG; +$log->init($progname, $is_verbose); + +#$conf->{data}{logfile} =~/^(.+)$/; +#my $logfile = $1; +my $logfile = $conf->{data}{logfile}; +my $logger = $log->initLog4Perl($logfile, + $conf->{data}{loglevel}, + $conf->is_debug() ); + +$logger->info("$progname started"); + +if ( $conf->{data}{rewritepath} ) { + $PATH = &rewritepath($PATH, $conf->{data}{rewritepath}); +} + + + +### may be that should become %inputfields ?? +our %inputfields = ( 'mail' => { + 'name' => 'mail', + 'type' => 'text', + 'len' => 40, + 'label' => 'Email address:', + 'default' => '', + 'regexp' => '.+\@.+\..+', + 'ldapattr' => 'mail', + 'status' => 'start', + 'priority' => 2, + }, + 'login' => { + 'name' => 'login', + 'type' => 'text', + 'len' => 40, + 'label' => 'Login name:', + 'default' => '', + 'regexp' => '', + 'ldapattr' => 'uid', + 'status' => 'start', + 'priority' => 1, + }, + 'principalname' => { + 'name' => 'principalname', + 'type' => 'text', + 'len' => 40, + 'label' => 'TextGrid ID:', + 'default' => '', + 'regexp' => '.+\@.+', + 'rule' => 'must contain an @ character', + 'ldapattr' => 'edupersonprincipalname', + 'status' => 'end;start', + 'priority' => 3, + 'usage' => 'ID', + }, + 'password' => { + 'name' => 'password', + 'type' => 'password', + 'len' => 40, + 'label' => 'New password:', + 'default' => '', + 'regexp' => '.{8,}', + 'rule' => 'must be at least 8 chars long', + 'ldapattr' => 'userpassword', + 'status' => 'printform', + 'priority' => 10, + }, + 'retype' => { + 'name' => 'retype', + 'type' => 'password', + 'len' => 40, + 'label' => 'Please retype password:', + 'default' => '', +# 'regexp' => '.{8,}', + 'ldapattr' => '', + 'status' => 'printform', + 'priority' => 11, + }, + ); + +our $isdebug = 0; +our $istest = 0; + +while ( my $cgi = new CGI::Fast ) { + + $count++; + my $html = &buildResponse ( $cgi, $count ); + print $html; + +} + +sub buildResponse { + my ( $q, $hitcount ) = @_; + + if ( $q->param('debug') ) { + $isdebug=1; + } + + if ( $conf->{'data'}{'emailconfig'} ) { + foreach my $mailconf ( split /;/, $conf->{'data'}{'emailconfig'} ) { + my ($key, $value ) = split (/=/, $mailconf, 2); + $key =~ s/^\\//; + $key =~ s/^#//; + $conf->{data}{emailinfo}{$key}=$value; + $logger->debug("found mail configuration: $key=$value"); + } + } +# else { +# $response .= $q->("Sorry internal Error cannot perform, please send +# } + + my $statusmessage = $q->param('status') ? $q->param('status') : 'none'; + + $logger->info("pwreset ($VERSION) started, status: $statusmessage"); + $logger->debug("used sysfile $sysconfig"); + $logger->debug("used configfile $conf->{data}{configfile}"); +# $logger->debug("status is: ".$q->param('status')) if $q->param('status') ; + + my %session; + my $sessionid = &handlesession($q, \%session, $statusmessage ); + +# use Data::Dumper; +# my $dumpstr = Dumper(%session); + + foreach my $key ( sort keys %session ) { + if ( $key ) { + $logger->debug("session $key: ". $session{$key}); + } + } + + my ($header, $response) = ''; + + $header = &printheader($q); + $response .= &debugcgi($q, $hitcount) if $isdebug; +# $response .= $q->p("Session: $dumpstr") if $isdebug; + + $response .= $q->h1($conf->{'data'}{'title'}); + + my $status = $q->param('status'); + + if ( ! $status ) { + $logger->debug("buildResponse(): No status thus stating start_process ..."); + $response .= &startprocess($q); +# $response .= $q->h3("No Status, this shouldnd't happen here, please report"); + } + elsif ( $status eq 'testcookie' ) { + $response = &testcookie($q); + } + elsif ( $status eq 'sendlink' ) { + $response .= &sendlink($q, \%session); +# my ( $resp, $head ) = &sendlink($q); +# $response .= $resp; +# $header = $head if ($head ); + } + elsif ( $status eq 'printform' ) { + $response .= &printform($q, \%session); + } + elsif ( $status eq 'modify' ) { + $response .= &modify($q, \%session); + } + elsif ( $conf->{data}{enableadminhelp} && $status eq 'adminhelp' ) { +# my $lang = $q->param('lang') ? $q->param('lang') : 'en'; +# $response .= $q->pre(`./$NAME.pl -h -G $lang`); + if ( my $feature = $q->param('helpfeature') ) { + $response .= $q->pre(`./$NAME.pl -H $feature`); + } + else { + $response .= $q->pre(`./$NAME.pl -h`); + } + } + else { + $response .= &printerror('status', $q); + } + + + if ( $conf->{data}{forcehttps} && ! $q->https ) { + $response = $q->h3("ERROR Programm does not work without HTTPS"); + } + + $response .= &printtail( $q); + + $logger->info("$progname ended"); + + + $response = "$header$response"; + + return $response; + +} + +sub handlesession { + my ( $q, $rh_session, $status ) = @_; + +# my %session; +# my $exist = ''; + + my $sid = $q->cookie ( -name => $NAME ); + + if ( $sid ) { + + my $sessionfile = $conf->{'data'}{ 'sessionpath' }."/$sid"; + if ( -e $sessionfile ) { +# $exist = $sid; + $logger->debug( "Session-Management: Reading session $sid"); + tie %{$rh_session}, "Apache::Session::File", $sid, + { Directory => $conf->{'data'}{ 'sessionpath' }, + LockDirectory => $conf->{'data'}{ 'sessionlockpath'} + }; + } + else { + $logger->debug( "Session-Management: creating new session $sid"); + + tie %{$rh_session}, "Apache::Session::File", undef, + { Directory => $conf->{'data'}{ 'sessionpath' }, + LockDirectory => $conf->{'data'}{ 'sessionlockpath'} + }; + $sid = $rh_session->{_session_id}; + $rh_session->{'counter'}=0; + &setcookie( $q, $sid) ; + } + } + else { + tie %{$rh_session}, "Apache::Session::File", undef, + { Directory => $conf->{'data'}{ 'sessionpath' }, + LockDirectory => $conf->{'data'}{ 'sessionlockpath'} + }; + $sid = $rh_session->{_session_id}; + $rh_session->{'counter'}=0; + &setcookie( $q, $sid) ; + } + + if ( $rh_session->{'status'} ) { + $rh_session->{'laststatus'} = $rh_session->{'status'}; + } + $rh_session->{'status'} = $status; + $rh_session->{'counter'} += 1; + + + return $sid; +} + + + +sub startprocess { + my ( $q ) = @_; + + my $response = ''; + + my $status = 'sendlink'; + + my %params; + $params{status}=$status; + my $url = &createurl($q, \%params); + + $q->param('status', $status); + + $response .= $q->h2("Identify yourself"); + $response .= $q->p("my url: $url") if $isdebug; + + $response .= $q->start_form( -method => 'post', + -action => $url, + ); + + + $response .= &printfields ($q, 'start', $conf->{'data'}{'oneinputfield'}); + $response .= &printhiddens($q, $status); + + $response .= $q->submit(-name =>'button_name', + -value =>'verify me', + ); + + $response .= $q->end_form; + + return $response; +} + +sub createid { + + my $id = "xaOUd".rand(100000)."ff1A.846"; + + + return $id; +} + +sub setcookie { + my ( $q, $sid ) = @_; + my $status = $q->param('status') ? $q->param('status'): 'none' ; + $logger->debug("setcookie(): Status is: $status, SID is: $sid"); + + my %params; + if ( $status ne 'testcookie' && $status ne 'adminhelp' ) { +# if ( $status ne 'testcookie' ) { +# my $id = $sid; + $params{'status'}='testcookie'; + my $nexturl = &createurl($q, \%params); +# my $server = $q->server_name; + # my $id = &createid(); + my $cookietime = ($status eq 'modify') ? 'now' : &getcookietime(); + + my $cookie = $q->cookie(-name => $NAME, + -value => $sid, + -expires => $cookietime, + -path => $PATH, +# -domain => 'XXX', +# -secure => 1, + ); + + $logger->debug(" setcookie(): sending |$cookie| via redirect to testcookie"); + print $q->redirect ( -url => $nexturl, + -cookie => $cookie ); + exit(); + } +} + +sub createurl { + my ( $q, $rh_params ) = @_; + + my $server = $q->server_name; + my $url = $q->url(); + + if ( $conf->{data}{rewritepath} ) { + $url = &rewritepath($url, $conf->{data}{rewritepath}); + } + + my $token = '?'; + if ( $rh_params ) { + foreach my $key ( keys %{$rh_params} ) { + $url .= $token; + $url .= $key.'='.$rh_params->{$key}; + $token = '&'; + } + } + $url .= "${token}debug=".$q->param('debug') if $q->param('debug') ; + +# $status ? +# "$PROT://$server$PATH/$NAME.pl?status=$status" : +# "$PROT://$server$PATH/$NAME.pl"; + + $logger->debug("createurl(): created url $url"); + return $url; +} + +sub testcookie { + my ( $q ) = @_; + my $response=''; + + my $cookie = $q->cookie ( -name => $NAME ); + my $nexturl = &createurl( $q, undef); + $logger->debug("testcookie(): Cookie is: $cookie"); + + if ( defined $cookie ) { +# $response .= $q->h2("TESTCOOKIE: SUCCESS"); + $logger->debug("testcookie(): Found cookie, will proceed"); + $q->param('status', 'sendlink'); + $response .= $q->h1($conf->{'data'}{'title'}); + $response .= &startprocess($q); + } + else { + $logger->warn("testcookie(): Cookies seem to be disabled"); + + $response .= $q->h2( "Cookies Disabled!" ); + $response .= $q->h3( "Your browser is not accepting cookies"); + $response .= $q->p( "Please enable cookies in your browser preferences and ". + $q->a( { -href => $nexturl }, "return to start"). + "." + ); + } + + return $response; +} + +sub sendlink { + my ( $q, $rh_session ) = @_; + my ($header, $response) = ''; + + $response .= $q->h2("Verification and sending email"); + + if ( my $paramerrors = &getparamerrors ( $q ) ) { +# if ( $paramerrors ) { + $response .= $paramerrors; + $response .= $q->p("Please try again"); + $response .= &startprocess($q); + return $response; + } + + my $searchquery = &getsearchquery($q, 'start'); + $logger->debug("sendlinkl(): searchquery is $searchquery"); + + my $filter = &createfilter( $q, $searchquery, 'start'); + my ( $searchresponse, $mail, $entry ) = &searchentry($q, $rh_session, $filter); + $response .= $q->b($searchresponse) if $searchresponse ; + my $user = " (".$q->param($searchquery).")"; + + if ( ! $mail ) { + $response .= &startprocess($q); + } + else { + $response .=$q->p("Your TextGrid account $user exists."); + $response .= $q->p("\nsmtpserver: ".$conf->{data}{emailinfo}{smtprelay}) if $isdebug; + $response .= &sendlinkmail($q, $mail, $rh_session); + } + + return $response; +} + +sub getsearchquery { + my ( $q, $status ) = @_; + my $searchquery = ''; + + if ( $q->param('search') ) { + $searchquery = 'search'; + } + else { + foreach my $f ( sort prioritysort keys %inputfields ) { + if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { + if ( $q->param($inputfields{$f}{name}) ) { + $searchquery = $inputfields{$f}{name}; + last; + } + } + } + } + + return $searchquery; +} + + +sub sendlinkmail { + my ( $q, $mail, $rh_session ) = @_; + my $response = ''; + + my $status = 'printform'; + my $smtp = new DAASIlib::SMTP; + + + $smtp->set_server($conf->{data}{emailinfo}{smtprelay}); + my $subject = $conf->{data}{emailinfo}{subjectpart}; + $subject .= " How to reset your TextGrid password"; + my $body = $conf->{data}{linkmail}; + my ($cc, $bcc, $pw) = ''; + $body =~ s/\$/\n/g; + + my $secret = &createid(); + +# my $url = $q->url(); +# $url .= "?status=printform&sessionid=$secret"; +# $url .= "&debug=".$q->param('debug') if $q->param('debug') ; + + my %params; + $params{status}=$status; + $params{sessionid}=$secret; + my $url = &createurl($q, \%params); + + $rh_session->{'secret'} = $secret; + + $body =~ s/%URL%/$url/; + + my $result = $smtp->send_mail($conf->{data}{emailinfo}{from}, + $subject,$body,$mail,$cc,$bcc, + $conf->{data}{emailinfo}{hello}, + $conf->{data}{emailinfo}{smtpuser}, + ) if ! $istest; + + $response .= $q->p("An email has been sent to $mail."); + + + + return $response; + +} + + +sub createfilter { + my ( $q, $searchquery, $status ) = @_; + + my ( $searchattr, $filter ) = ''; + + if ( $searchquery eq 'search' ) { + $filter = '( | '; + foreach my $f ( sort prioritysort keys %inputfields ) { + if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { + $searchattr = $inputfields{$f}->{'ldapattr'}; + if ( $searchattr ) { + $filter .= "($searchattr=".$q->param($searchquery).")"; + } + } + } + $filter .= ')'; + } + else { + $searchattr = $inputfields{$searchquery}{'ldapattr'}; + $filter = "($searchattr=".$q->param($searchquery).")"; + } + + $logger->debug("createfilter(): created filter $filter"); + return $filter; +} + +sub searchentry { + my ( $q, $rh_session, $filter, $ldap ) = @_; + my $response = ''; + + my $searchattr; +# my $filter; + my $mail; + my $entry; + my $shouldclose = 0; + + my @attrs = &getattrs('start'); + + + $response .= $q->p("searching with Filter $filter") if $isdebug; + + + my $libldap = new DAASIlib::LDAP; + my $rh_ldap = $libldap->defineServerFromURI($conf->{data}{ldapuri}, + 'ldapuri', 0); + $rh_ldap->{is_tls} = 1 if $conf->{data}{forcetls}; + +# $rh_ldap->{scope} = 'sub'; + + $rh_ldap->{attribs} = \@attrs; + $rh_ldap->{filter} = $filter; + + if ( ! $ldap ) { +# my $ldap = $libldap->connectServer($rh_ldap, 1); + $ldap = $libldap->connectServer($rh_ldap); + if ( ! $ldap ) { + $response .= $q->p("could not connect to LDAP-Server"); + return $response; + } + $shouldclose = 1; + } + + my $mesg = $libldap->doSearch($ldap, $rh_ldap); + if ($mesg->count()) { + if ( $mesg->count() > 1 ) { + $response .= $q->p("Error: found more than one entry!!!"); + $logger->error("Error: found more than one entry!!!"); + } + + $entry = $mesg->pop_entry(); + my $entrydn= $entry->dn(); + $logger->debug("found entry $entrydn"); + $mail = $entry->get_value('mail'); + foreach my $attr ( @attrs ) { + $rh_session->{$attr} = $entry->get_value($attr); + } + if ( ! $mail ) { + $response .= $q->p("Error: could not find an email to send message to"); + $logger->error("Error: could not find an email to send message to"); + } + } + else { + $logger->debug("found no entry with filter $filter"); + $response .= $q->p("Error: there is no user with $filter"); + $logger->error("Error: there is no user with $filter"); + } + + if ( $shouldclose ) { + $ldap->unbind; + } + + return $response, $mail, $entry; +} + +sub printform { + my ( $q, $rh_session ) = @_; + + $logger->debug("printform(): started"); + my $status = ''; + my $response = ''; + $response .= $q->h2("Input of new password"); + my $cookie = $q->cookie(-name => 'pwReset'); + +# use Data::Dumper; +# $response .= $q->p(Dumper($cookie)); + my $secret = $rh_session->{'secret'}; + + if ( $q->param('sessionid') eq $secret ) { + + $response .= $q->h3("Everything went fine, you can now reset your Password"); + $status = 'modify'; + my %params; + $params{'status'}=$status; + my $url = &createurl($q, \%params); + + $response .= $q->start_form( -method => 'post', + -action => $url, + ); + $response .= &printfields ($q, 'printform'); + $logger->debug("printform(): status: $status"); + $response .= &printhiddens($q, $status); + + $logger->debug("printform(): fine so I set status to $status"); + $response .= $q->submit(-name =>'button_name', + -value =>'reset password', + ); + $response .= $q->end_form; + + } + else { + my $cookietimestring = &getcookietimestring(); + $response .= $q->h3("There has been an Error"). + $q->p("This can be out of one of the following reasons:"). + $q->ul( $q->li({-type=>'disc'}, + [ + "Your browser didn't allow to set a cookie", + "You used a different browser, when requesting the reset", + "The request is too old (older than $cookietimestring)", + "Any other unforseen error", + ])); + } + + return $response; +} + + +sub modify { + my ( $q, $rh_session ) = @_; + + my $response = ''; + my $cookie = $q->cookie(-name => 'pwReset'); + my $secret = $rh_session->{'secret'}; + my $user = $rh_session->{'edupersonprincipalname'}; + my $maxcount = 40; + + my $libldap = new DAASIlib::LDAP; + my $rh_ldap = $libldap->defineServerFromURI($conf->{data}{ldapuri}, + 'ldapuri', 0); + $rh_ldap->{is_tls} = 1 if $conf->{data}{forcetls}; + my $ldap = $libldap->connectServer($rh_ldap); +# my $maxcount = 4; + + $response .= $q->h2("Modifying Password"); + + if ( $q->param('sessionid') eq $secret ) { + if ( $q->param('password') eq $q->param('retype') ) { + if ( $rh_session->{'counter'} > $maxcount ) { + $response .= $q->p("ERROR: Session is not valid any more, because it was called too often. You have to begin the process again"); + tied(%{$rh_session})->delete; + my $url = &createurl($q, undef); + $response .= $q->a( { -href => $url }, "return to start"); +# $response .= &startprocess($q); + } + elsif ( my $paramerrors = &getparamerrors ( $q ) ) { + $response .= $paramerrors; + $response .= $q->p("Please try again"); + $response .= &printform($q, $rh_session); + } + else { + my $idattr = $inputfields{&getidfield()}{'ldapattr'}; + my $user = $rh_session->{$idattr}; + my $filter = "($idattr=$user)"; + my ( $searchresponse, $mail, $entry ) = &searchentry($q, $rh_session, $filter); + my $entrydn = $entry->dn(); + $response .= $q->p("Password for user $user will be reset"); +# $response .= "entry: $entrydn"; + my $plain = $q->param('password'); + my $digest = $conf->{'data'}{'pwhash'}; + my $encodedPassword = `/usr/sbin/slappasswd -h {$digest} -s \'$plain\'`; + + $logger->debug("changing Password of entry $entrydn at ". + $conf->{data}{ldapuri}); + $entry->replace ( + $inputfields{'password'}{'ldapattr'} => $encodedPassword + ); + my $res = $entry->update($ldap); + +## The following produces crypt passwords, that didn't work +# use Net::LDAP::Extension::SetPassword; +# my $res = $ldap->set_password( +# newpasswd => $q->param('password'), +# user => $entrydn +# ); + $logger->debug("Result of password change: ".$res->error()); + $response .= $q->p("result of update: ".$res->error()); + $ldap->unbind; + } + } + else { + $response .= $q->p("ERROR: the password and the retyped password did not match, please try again"); + $response .= &printform($q, $rh_session); + } + } + else { + $response .= $q->p("ERROR: sessionid: $secret not returned in query: ".$q->param('sessionid') ); + } + + return $response; +} + +sub getidfield { + my $idfield=''; + foreach my $key ( keys %inputfields ) { + if ( $inputfields{$key}{ 'usage'} eq 'ID') { + $idfield = $key; + } + } + $idfield = $idfield ? $idfield : 'edupersonprincipalname'; + return $idfield; +} + +sub getcookietime { + my $string=''; + + if ( $conf->{'data'}{'sessiontime'} =~/(\d+)([smhdMy])/ ) { + $string = '+'.$conf->{'data'}{'sessiontime'}; + } + else { + $logger->error("ERROR: wrong Format in configuration of sessiontime"); + } + + return $string; +} + + +sub getcookietimestring { + my $string=''; + + my %units = ( + 's' => 'seconds', + 'm' => 'minutes', + 'h' => 'hours', + 'd' => 'days', + 'M' => 'months', + 'y' => 'years', + ); + + my $num; + my $unitstring; + + if ( $conf->{'data'}{'sessiontime'} =~/(\d+)([smhdMy])/ ) { + $num = $1; + $unitstring = $units{$2}; + $string = "$num $unitstring"; + } + + return $string; +} + + +sub rewritepath { + my ($path,$rules) = @_; + + foreach my $rule ( split / ?; ?/, $rules ) { + my ($from, $to ) = split / ?=> ?/, $rule; + $path =~s/$from/$to/; + } + return $path; +} + +sub printhiddens { + my ( $q, $state ) = @_; + my $fields=''; + + + $logger->debug("printhiddens(): got status: $state"); + $state = $state ? $state : $q->param('status'); + $logger->debug("printhiddens(): changed status to: $state"); +### $fields .= $q->hidden( 'status', "$state"); +#### There seems to be a bug in the CGI-library, since the above didn't work ( + $fields .= "<input type=\"hidden\" name=\"status\" value=\"$state\"/>"; + + $fields .= $q->hidden( 'sessionid', $q->param('sessionid') ) + if $q->param('sessionid') ; + +# $fields .= $q->hidden( -name => 'status', -default => "XXX${status}YYY"); +# $fields .= $q->hidden( -name => 'sxxxx', -default => "XXX${status}YYY"); +# $fields .= $q->hidden( -name => 'sessionid', -default => $q->param('sessionid') ) +# if $q->param('sessionid') ; + + if ( $q->param('debug') ) { + $fields .= $q->hidden( -name => 'debug', -default => $q->param('debug')); + $fields .= $q->p("debug is on"); + } + $logger->debug("printhiddens(): returning fields: $fields"); + return $fields; + +} + +sub prioritysort { + $inputfields{$a}->{'priority'}<=>$inputfields{$b}->{'priority'} +} + +sub getattrs { + my ($status) = @_; + my @attrs=(); + $logger->debug("getattrs(): got status $status"); + + foreach my $f ( sort prioritysort keys %inputfields ) { + if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { + push @attrs, $inputfields{$f}{ldapattr}; + $logger->debug("getattrs(): found attribute: ".$inputfields{$f}{ldapattr}); + } + } + return @attrs; +} + +sub printfields { + my ($q, $status, $isonefield) = @_; + my $fields=''; + my $labels = 'Please input either '; +#Please input either your Login Name, your E-Mail address or your TextGrid ID. Your password can only be changed for @textgrid.de TextGrid IDs + foreach my $f ( + sort { $inputfields{$a}->{'priority'}<=>$inputfields{$b}->{'priority'} } + keys %inputfields ) { + + if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { + if ( $inputfields{$f}{type} eq 'text' ) { + my $label = $inputfields{$f}->{'label'}; + $label =~ s/(.*):\s?/$1/; + $labels .= "your $label or "; + $fields .= &printtextfield($q, $inputfields{$f}); + } + elsif ( $inputfields{$f}->{type} eq 'password' ) { + $fields .= &printpasswordfield($q, $inputfields{$f}); + } + else { + $fields .= &printerror('input type', $q); + } + $fields .= $q->br; + } + } + + if ( $isonefield ) { + $labels =~ s/ or $//; + $labels .= "<br>\n (Your password can only be changed for \@textgrid.de TextGrid IDs)"; + $fields = $labels.": ".$q->br.$q->br; + $fields .= $q->textfield( + -name => 'search', + -value => '', + -size =>40, + -maxlen => 40, + ); + } + + return $fields; +} + +sub printtextfield { + my ($q,$f, $label) = @_; + + my $field = ''; + $label = $label ? $label : $f->{'label'}; + $field .= $label.$q->br; + $field .= $q->textfield( + -name => $f->{'name'}, + -value => $f->{'default'}, + -size => $f->{'len'}, + -maxlen => $f->{'len'}, + ); + + return $field; +} + +sub printpasswordfield { + my ($q,$f) = @_; + + my $field = ''; + $field .= $f->{'label'}.$q->br; + $field .= $q->password_field( + -name => $f->{'name'}, + -value => $f->{'default'}, + -size => $f->{'len'}, + -maxlen => $f->{'len'}, + ); + + return $field; +} + + + +sub printtail { + my ( $q) = @_; + + my $tail = $q->end_html(); + + return $tail; +} + + +sub printheader { + my ( $q ) = @_; + + my $header = $q->header( + -type => 'text/html', + -charset=> $conf->{'data'}{'charset'}, + # -pragma => 'no-cache', # for debugging only + # -expires=> 'now', # for debugging only + -expires=> '+3m', # should be used in production +# -cookie => $cookie, + ); + +# if ( $cookie ) { +# $logger->debug("Setting Cookie"); +# } + + my $color = $conf->{'data'}{'bgcolor'}; + +# if ( $color !~ /#/ ) { +# $color = '#'.$color; +# } + + my %metas; + + foreach my $metadef ( split /\|/, $conf->{'data'}{'meta'} ) { + my ($name,$content) = split /:/, $metadef, 2; + $metas{$name}=$content; + } + $header .= $q->start_html( + -title => $conf->{'data'}{'title'}, + -bgcolor => $color, + -meta => \%metas, +# -style => { -src => [ $conf->{'data'}{'cssfile'} ], +# -media => 'all'}, + ); + + + return $header; +} + + +sub printerror { + my ( $err, $q) = @_; + + my $error = $q->p("Error: wrong $err"); + + return $error; +} + +sub getparamerrors { + my ( $q ) = @_; + + my $errors = ''; + my @names = $q->param(); + + foreach (@names) { + my $value = $q->param($_); + my $regexp = $inputfields{$_}{'regexp'}; + my $rule = $inputfields{$_}{'rule'}; + $rule = $rule ? + "rule \"$rule\"" : + "Regular Expression /$regexp/"; + my $display; + + if ( $inputfields{$_}{'type'} eq 'password' ) { + $display = "[not displayed]"; + } + else { + $display = $value; + } + if ( $value && $regexp && $value !~ /$regexp/ ) { + $errors .= $q->p("Error in inputfield $_: value \"$display\" does not conform to $rule"); + } + } + + return $errors; +} + +sub debugcgi { + my ( $q, $hitcount ) = @_; + + my $response = ''; + $response .= $q->h3("Debuginfo"); + $response .= $q->p("This is hit number <b>$hitcount</b>"); + + my @keywords = $q->keywords(); + my @names = $q->param(); + + my @values; + + foreach (@names) { + push @values,"$_: ".$q->param($_); + } + $response .= $q->ul( $q->li({-type=>'disc'},["Q keywords: @keywords", "Q param: @names"])); + if ( @values ) { + $response .= $q->p("Query has following param / value pairs:"); + $response .= $q->ul( $q->li({-type=>'disc'}, @values)); + } + + $response .= $q->p("remote host: ".$q->remote_host()); + $response .= $q->p("remote addr: ".$q->remote_addr()); + $response .= $q->p("user agent: ".$q->user_agent()); + + $response .= $conf->getConf("current configuration:"); + + + return $response; + +} diff --git a/info.textgrid.middleware.tgauth.passwordReset/svn-commit.tmp b/info.textgrid.middleware.tgauth.passwordReset/svn-commit.tmp new file mode 100644 index 0000000..790bb93 --- /dev/null +++ b/info.textgrid.middleware.tgauth.passwordReset/svn-commit.tmp @@ -0,0 +1,5 @@ +Checcked in Version with proxy path rewrite feature + +--This line, and those below, will be ignored-- + +A etc -- GitLab