
Here is a patch for some password enforcement features I've added to our fork of 1.3.2 at NASA. Not all of it will apply cleanly against a 1.3.2 tree; for example, we have a custom module AdminCustomer.pm that allows us to quickly modify customer user info. The changes to Admin.pm, CustomerPreferences.pm, and AgentPreferences.pm might also require some manual merging, but the code should be easy enough to grok. I think some or all of this might be found in the upcoming OTRS 2.x, but might be useful for folks using 1.3.x that need it NOW. :) New options to Default.pm: + $Self->{PasswordClassEnforce} = 1; + $Self->{PasswordClassMinimum} = 3; + $Self->{PasswordLengthMin} = 6; + $Self->{PasswordLengthMax} = 12; + $Self->{PasswordHistoryEnforce} = 1; + $Self->{PasswordHistoryMinimum} = 5; PasswordClassEnforce is a boolean to enable character class enforcement. PasswordClassMinimum defines the number of unique char classes that must be met (max 4: upper, lower, numerical, symbols). PasswordLengthMin and PasswordLengthMax are self documenting. PasswordHistoryEnforce is a boolean to enable password history tracking. PasswordHistoryMinimum defines the number of passwords in history to compare against. There are two new modules, PasswordValidate.pm and PasswordHistory.pm, that add the new functionalities. There are also two new tables: -- -- Table structure for table `system_user_password_history` -- CREATE TABLE `system_user_password_history` ( `id` bigint(20) NOT NULL auto_increment, `login` varchar(50) NOT NULL default '', `pw` varchar(50) NOT NULL default '', `timestamp` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`id`), KEY `system_user_password_history_login` (`login`) ) TYPE=MyISAM; -- -- Table structure for table `customer_user_password_history` -- CREATE TABLE `customer_user_password_history` ( `id` bigint(20) NOT NULL auto_increment, `login` varchar(50) NOT NULL default '', `pw` varchar(50) NOT NULL default '', `timestamp` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`id`), KEY `customer_user_password_history_login` (`login`) ) TYPE=MyISAM; Enjoy! -- Jason Dixon 301.286.8635 voice NASA / Goddard Space Flight Center jason@nccs.nasa.gov AMTI, Inc. Building 28, Room S212 Code 931

Hi Alessandro: Yes, I'm still working on getting the whole thing released. Because the work was done as a contractor for NASA, there are a number of steps (i.e., red tape) that must be followed in order to open up the code. Rest assured that I am pursuing this aggressively. Thanks, Jason On Tue, 12 Apr 2005, Alessandro Ranellucci wrote:
On 12-04-2005 at 19:13, Jason Dixon wrote:
[...]
Hi Jason, what about your other interesting improvements? I remember ticket merge, asset management and other nice features. Are you going to release them as well?
- Alessandro Ranellucci
_______________________________________________ OTRS mailing list: dev - Webpage: http://otrs.org/ Archive: http://lists.otrs.org/pipermail/dev To unsubscribe: http://lists.otrs.org/cgi-bin/listinfo/dev
-- Jason Dixon 301.286.8635 voice NASA / Goddard Space Flight Center jason@nccs.nasa.gov AMTI, Inc. Building 28, Room S212 Code 931

Hi Jason, cool work. :) Thanks! Yes, there is also a kind of this feature in the upcoming OTRS 2.0. -=> The password management is moved to a module, so it can be configured via the preferences options: $Self->{PreferencesGroups}->{Password} = { Module => 'Kernel::Output::HTML::PreferencesPassword', Colum => 'Other Options', Label => 'Change Password', Prio => 1000, PasswordHistory => 0, # PasswordRegExp => '[a-z]|[A-z]|[0-9]|\.|;|,|:|-|\+|#|!|\$|&|\?', PasswordRegExp => '', PasswordMinSize => 0, PasswordMin2Lower2UpperCharacters => 0, PasswordMin2Characters => 0, PasswordNeedDigit => 0, Activ => 1, }; Thanks Jason for you inspirations! :-) -Martin Jason Dixon wrote:
Here is a patch for some password enforcement features I've added to our fork of 1.3.2 at NASA. Not all of it will apply cleanly against a 1.3.2 tree; for example, we have a custom module AdminCustomer.pm that allows us to quickly modify customer user info. The changes to Admin.pm, CustomerPreferences.pm, and AgentPreferences.pm might also require some manual merging, but the code should be easy enough to grok. I think some or all of this might be found in the upcoming OTRS 2.x, but might be useful for folks using 1.3.x that need it NOW. :)
New options to Default.pm:
+ $Self->{PasswordClassEnforce} = 1; + $Self->{PasswordClassMinimum} = 3; + $Self->{PasswordLengthMin} = 6; + $Self->{PasswordLengthMax} = 12; + $Self->{PasswordHistoryEnforce} = 1; + $Self->{PasswordHistoryMinimum} = 5;
PasswordClassEnforce is a boolean to enable character class enforcement. PasswordClassMinimum defines the number of unique char classes that must be met (max 4: upper, lower, numerical, symbols). PasswordLengthMin and PasswordLengthMax are self documenting. PasswordHistoryEnforce is a boolean to enable password history tracking. PasswordHistoryMinimum defines the number of passwords in history to compare against.
There are two new modules, PasswordValidate.pm and PasswordHistory.pm, that add the new functionalities. There are also two new tables:
-- -- Table structure for table `system_user_password_history` --
CREATE TABLE `system_user_password_history` ( `id` bigint(20) NOT NULL auto_increment, `login` varchar(50) NOT NULL default '', `pw` varchar(50) NOT NULL default '', `timestamp` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`id`), KEY `system_user_password_history_login` (`login`) ) TYPE=MyISAM;
-- -- Table structure for table `customer_user_password_history` --
CREATE TABLE `customer_user_password_history` ( `id` bigint(20) NOT NULL auto_increment, `login` varchar(50) NOT NULL default '', `pw` varchar(50) NOT NULL default '', `timestamp` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`id`), KEY `customer_user_password_history_login` (`login`) ) TYPE=MyISAM;
Enjoy!
------------------------------------------------------------------------
Index: otrs/Kernel/Config/Defaults.pm diff -u otrs/Kernel/Config/Defaults.pm:1.31 otrs/Kernel/Config/Defaults.pm:1.33 --- otrs/Kernel/Config/Defaults.pm:1.31 Mon Dec 13 14:25:36 2004 +++ otrs/Kernel/Config/Defaults.pm Mon Apr 4 01:08:33 2005 @@ -2,7 +2,7 @@ # Kernel/Config/Defaults.pm - Default Config file for OTRS kernel # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: Defaults.pm,v 1.31 2004/12/13 19:25:36 malberts Exp $ +# $Id: Defaults.pm,v 1.33 2005/04/04 05:08:33 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -20,7 +20,7 @@ use strict; use vars qw(@ISA $VERSION); -$VERSION = '$Revision: 1.31 $'; +$VERSION = '$Revision: 1.33 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- @@ -316,6 +316,16 @@ Lastname => 'sn', Email => 'mail', }; + + # --------------------------------------------------- # + # Password Change Options # + # --------------------------------------------------- # + $Self->{PasswordClassEnforce} = 1; + $Self->{PasswordClassMinimum} = 3; # 4 classes max + $Self->{PasswordLengthMin} = 6; + $Self->{PasswordLengthMax} = 12; + $Self->{PasswordHistoryEnforce} = 1; + $Self->{PasswordHistoryMinimum} = 5; # Number of password changes to review
# --------------------------------------------------- # # default agent settings # Index: otrs/Kernel/Modules/AdminCustomerUser.pm diff -u otrs/Kernel/Modules/AdminCustomerUser.pm:1.4 otrs/Kernel/Modules/AdminCustomerUser.pm:1.6 --- otrs/Kernel/Modules/AdminCustomerUser.pm:1.4 Tue Dec 21 22:46:08 2004 +++ otrs/Kernel/Modules/AdminCustomerUser.pm Tue Mar 22 13:15:12 2005 @@ -2,7 +2,7 @@ # Kernel/Modules/AdminCustomerUser.pm - to add/update/delete customer user and preferences # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: AdminCustomerUser.pm,v 1.4 2004/12/22 03:46:08 jason Exp $ +# $Id: AdminCustomerUser.pm,v 1.6 2005/03/22 18:15:12 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -13,9 +13,10 @@ use strict; use Kernel::System::CustomerUser; +use Kernel::System::PasswordValidate;
use vars qw($VERSION); -$VERSION = '$Revision: 1.4 $ '; +$VERSION = '$Revision: 1.6 $ '; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- @@ -145,66 +146,82 @@ $GetParam{UserCustomerID} = $Self->{ParamObject}->GetParam(Param => 'UserEmail') || ''; } $GetParam{ID} = $Self->{ParamObject}->GetParam(Param => 'ID') || ''; - # update user - if ($Self->{CustomerUserObject}->CustomerUserUpdate(%GetParam, UserID => $Self->{UserID})) { - # update preferences - foreach my $Pref (sort keys %{$Self->{ConfigObject}->Get('CustomerPreferencesView')}) { - foreach my $Group (@{$Self->{ConfigObject}->Get('CustomerPreferencesView')->{$Pref}}) { - my $PrefKey = $Self->{ConfigObject}->{CustomerPreferencesGroups}->{$Group}->{PrefKey} || ''; - my $Type = $Self->{ConfigObject}->{CustomerPreferencesGroups}->{$Group}->{Type} || ''; - if ($Type eq 'Generic' && $PrefKey) { - if (!$Self->{CustomerUserObject}->SetPreferences( - UserID => $GetParam{ID}, - Key => $PrefKey, - Value => $Self->{ParamObject}->GetParam(Param => "GenericTopic::$PrefKey"), - )) { - my $Output = $NavBar.$Self->{LayoutObject}->Error(); - $Output .= $Self->{LayoutObject}->Footer(); - return $Output; + # enforce password guidelines + my $password_obj = Kernel::System::PasswordValidate->new( + password => $GetParam{UserPassword}, + min_char_classes => $Self->{ConfigObject}->Get('PasswordClassMinimum'), + min_length => $Self->{ConfigObject}->Get('PasswordLengthMin'), + max_length => $Self->{ConfigObject}->Get('PasswordLengthMax'), + ); + my $error = $password_obj->check(); + if (($Self->{ConfigObject}->Get('PasswordClassEnforce')) && ($error != 1) && (length($GetParam{UserPassword}) > 0)) { + $Self->{LogObject}->Log(Priority => 'error', Message => $error); + my $NavBar = $Self->{LayoutObject}->Header(Area => 'Admin', Title => 'Customer User'); + my $Output = $NavBar.$Self->{LayoutObject}->Error(); + $Output .= $Self->{LayoutObject}->Footer(); + return $Output; + } else { + # update user + if ($Self->{CustomerUserObject}->CustomerUserUpdate(%GetParam, UserID => $Self->{UserID})) { + # update preferences + foreach my $Pref (sort keys %{$Self->{ConfigObject}->Get('CustomerPreferencesView')}) { + foreach my $Group (@{$Self->{ConfigObject}->Get('CustomerPreferencesView')->{$Pref}}) { + my $PrefKey = $Self->{ConfigObject}->{CustomerPreferencesGroups}->{$Group}->{PrefKey} || ''; + my $Type = $Self->{ConfigObject}->{CustomerPreferencesGroups}->{$Group}->{Type} || ''; + if ($Type eq 'Generic' && $PrefKey) { + if (!$Self->{CustomerUserObject}->SetPreferences( + UserID => $GetParam{ID}, + Key => $PrefKey, + Value => $Self->{ParamObject}->GetParam(Param => "GenericTopic::$PrefKey"), + )) { + my $Output = $NavBar.$Self->{LayoutObject}->Error(); + $Output .= $Self->{LayoutObject}->Footer(); + return $Output; + } } - } - if ($Type eq 'Upload' && $PrefKey) { - my %UploadStuff = $Self->{ParamObject}->GetUploadAll( - Param => "GenericTopic::$PrefKey", - Source => 'String', - ); - if ($UploadStuff{Content}) { - $Self->{CustomerUserObject}->SetPreferences( - UserID => $GetParam{ID}, - Key => $PrefKey, - Value => $UploadStuff{Content}, - ); - $Self->{CustomerUserObject}->SetPreferences( - UserID => $GetParam{ID}, - Key => $PrefKey."::Filename", - Value => $UploadStuff{Filename}, - ); - $Self->{CustomerUserObject}->SetPreferences( - UserID => $GetParam{ID}, - Key => $PrefKey."::ContentType", - Value => $UploadStuff{ContentType}, - ); + if ($Type eq 'Upload' && $PrefKey) { + my %UploadStuff = $Self->{ParamObject}->GetUploadAll( + Param => "GenericTopic::$PrefKey", + Source => 'String', + ); + if ($UploadStuff{Content}) { + $Self->{CustomerUserObject}->SetPreferences( + UserID => $GetParam{ID}, + Key => $PrefKey, + Value => $UploadStuff{Content}, + ); + $Self->{CustomerUserObject}->SetPreferences( + UserID => $GetParam{ID}, + Key => $PrefKey."::Filename", + Value => $UploadStuff{Filename}, + ); + $Self->{CustomerUserObject}->SetPreferences( + UserID => $GetParam{ID}, + Key => $PrefKey."::ContentType", + Value => $UploadStuff{ContentType}, + ); + } } + } } - } + # redirect + if (my $ZoomTicketReturnID = $Self->{ParamObject}->GetParam(Param => 'ZoomTicketReturnID')) { + return $Self->{LayoutObject}->Redirect( + OP => "Action=AgentZoom&TicketID=$ZoomTicketReturnID", + ); + } + else { + return $Self->{LayoutObject}->Redirect( + OP => "Action=AdminCustomerUser&Nav=$Nav&Search=$Search", + ); + } } - # redirect - if (my $ZoomTicketReturnID = $Self->{ParamObject}->GetParam(Param => 'ZoomTicketReturnID')) { - return $Self->{LayoutObject}->Redirect( - OP => "Action=AgentZoom&TicketID=$ZoomTicketReturnID", - ); - } - else { - return $Self->{LayoutObject}->Redirect( - OP => "Action=AdminCustomerUser&Nav=$Nav&Search=$Search", - ); - } - } - else { - my $Output = $NavBar.$Self->{LayoutObject}->Error(); - $Output .= $Self->{LayoutObject}->Footer(); - return $Output; - } + else { + my $Output = $NavBar.$Self->{LayoutObject}->Error(); + $Output .= $Self->{LayoutObject}->Footer(); + return $Output; + } + } # new closing TEST } # search elsif ($Self->{Subaction} eq 'Search') { Index: otrs/Kernel/Modules/AgentPreferences.pm diff -u otrs/Kernel/Modules/AgentPreferences.pm:1.1.1.1 otrs/Kernel/Modules/AgentPreferences.pm:1.3 --- otrs/Kernel/Modules/AgentPreferences.pm:1.1.1.1 Sat Nov 13 22:14:15 2004 +++ otrs/Kernel/Modules/AgentPreferences.pm Mon Apr 4 01:09:29 2005 @@ -2,7 +2,7 @@ # Kernel/Modules/AgentPreferences.pm - provides agent preferences # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: AgentPreferences.pm,v 1.1.1.1 2004/11/14 03:14:15 jason Exp $ +# $Id: AgentPreferences.pm,v 1.3 2005/04/04 05:09:29 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -12,9 +12,12 @@ package Kernel::Modules::AgentPreferences; use strict; +use Kernel::System::PasswordValidate; +use Kernel::System::PasswordHistory; +use Data::Dumper;
use vars qw($VERSION); -$VERSION = '$Revision: 1.1.1.1 $'; +$VERSION = '$Revision: 1.3 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- @@ -102,11 +105,52 @@ my $Pw1 = $Self->{ParamObject}->GetParam(Param => 'NewPw1') || '';
if ($Pw eq $Pw1 && $Pw) { + my $password_class_obj = Kernel::System::PasswordValidate->new( + password => $Pw, + min_char_classes => $Self->{ConfigObject}->Get('PasswordClassMinimum'), + min_length => $Self->{ConfigObject}->Get('PasswordLengthMin'), + max_length => $Self->{ConfigObject}->Get('PasswordLengthMax'), + ); + my $password_class_error = $password_class_obj->check(); + my $password_history_obj = Kernel::System::PasswordHistory->new( + dbh => $Self->{DBObject}, + table => 'system_user_password_history', + password => crypt($Pw, $Self->{UserLogin}), + user => $Self->{UserLogin}, + history_num => $Self->{ConfigObject}->Get('PasswordHistoryMinimum'), + ); + my $password_history_error = $password_history_obj->check(); + print STDERR "Kernel::System::CustomerPreferences::UpdatePw\n"; + print STDERR "Password class error = $password_class_error\n"; + print STDERR "Password history error = $password_history_error\n"; + if ($Self->{ConfigObject}->Get('DemoSystem')) { $Output .= $Self->{LayoutObject}->Redirect( OP => "Action=AgentPreferences&What=1", ); } + elsif (($Self->{ConfigObject}->Get('PasswordClassEnforce')) && ($password_class_error ne 1)) { + $Output .= $Self->{LayoutObject}->Header(Area => 'Agent', Title => 'Preferences'); + my %LockedData = $Self->{TicketObject}->GetLockedCount(UserID => $Self->{UserID}); + $Output .= $Self->{LayoutObject}->NavigationBar(LockData => \%LockedData); + $Output .= $Self->{LayoutObject}->Error( + Message => "$password_class_error Please try again!", + Comment => "$password_class_error Please try again!", + ); + $Output .= $Self->Form(); + $Output .= $Self->{LayoutObject}->Footer(); + } + elsif (($Self->{ConfigObject}->Get('PasswordHistoryEnforce')) && ($password_history_error ne 1)) { + $Output .= $Self->{LayoutObject}->Header(Area => 'Agent', Title => 'Preferences'); + my %LockedData = $Self->{TicketObject}->GetLockedCount(UserID => $Self->{UserID}); + $Output .= $Self->{LayoutObject}->NavigationBar(LockData => \%LockedData); + $Output .= $Self->{LayoutObject}->Error( + Message => "$password_history_error Please try again!", + Comment => "$password_history_error Please try again!", + ); + $Output .= $Self->Form(); + $Output .= $Self->{LayoutObject}->Footer(); + } elsif ($Self->{UserObject}->SetPassword(UserLogin => $Self->{UserLogin}, PW => $Pw)) { $Output .= $Self->{LayoutObject}->Redirect( OP => "Action=AgentPreferences&What=1", @@ -127,7 +171,7 @@ $Output .= $Self->{LayoutObject}->Header(Area => 'Agent', Title => 'Preferences'); my %LockedData = $Self->{TicketObject}->GetLockedCount(UserID => $Self->{UserID}); $Output .= $Self->{LayoutObject}->NavigationBar(LockData => \%LockedData); - $Output .= $Self->{LayoutObject}->Notify(Info => 'Passwords dosn\'t match! Please try it again!'); + $Output .= $Self->{LayoutObject}->Notify(Info => 'Passwords don\'t match! Please try again!'); $Output .= $Self->Form(); $Output .= $Self->{LayoutObject}->Footer(); } Index: otrs/Kernel/Modules/CustomerPreferences.pm diff -u otrs/Kernel/Modules/CustomerPreferences.pm:1.1.1.1 otrs/Kernel/Modules/CustomerPreferences.pm:1.4 --- otrs/Kernel/Modules/CustomerPreferences.pm:1.1.1.1 Sat Nov 13 22:14:15 2004 +++ otrs/Kernel/Modules/CustomerPreferences.pm Mon Apr 4 01:09:29 2005 @@ -2,7 +2,7 @@ # Kernel/Modules/CustomerPreferences.pm - provides agent preferences # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: CustomerPreferences.pm,v 1.1.1.1 2004/11/14 03:14:15 jason Exp $ +# $Id: CustomerPreferences.pm,v 1.4 2005/04/04 05:09:29 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -12,9 +12,11 @@ package Kernel::Modules::CustomerPreferences; use strict; +use Kernel::System::PasswordValidate; +use Kernel::System::PasswordHistory;
use vars qw($VERSION); -$VERSION = '$Revision: 1.1.1.1 $'; +$VERSION = '$Revision: 1.4 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- @@ -98,16 +100,52 @@ my $UserID = $Self->{UserID};
if ($Pw eq $Pw1 && $Pw) { - $Self->{UserObject}->SetPassword(UserLogin => $Self->{UserLogin}, PW => $Pw); - $Output .= $Self->{LayoutObject}->Redirect( - OP => "Action=CustomerPreferences", + my $password_class_obj = Kernel::System::PasswordValidate->new( + password => $Pw, + min_char_classes => $Self->{ConfigObject}->Get('PasswordClassMinimum'), + min_length => $Self->{ConfigObject}->Get('PasswordLengthMin'), + max_length => $Self->{ConfigObject}->Get('PasswordLengthMax'), + ); + my $password_class_error = $password_class_obj->check(); + my $password_history_obj = Kernel::System::PasswordHistory->new( + dbh => $Self->{DBObject}, + table => 'customer_user_password_history', + password => $Pw, + user => $Self->{UserLogin}, + history_num => $Self->{ConfigObject}->Get('PasswordHistoryMinimum'), ); + my $password_history_error = $password_history_obj->check(); + if (($Self->{ConfigObject}->Get('PasswordClassEnforce')) && ($password_class_error ne 1)) { + $Output .= $Self->{LayoutObject}->CustomerHeader(); + $Output .= $Self->{LayoutObject}->CustomerWarning( + Message => "$password_class_error Please try again!", + Comment => "$password_class_error Please try again!", + ); + $Output .= $Self->{LayoutObject}->CustomerFooter(); + } + elsif (($Self->{ConfigObject}->Get('PasswordHistoryEnforce')) && ($password_history_error ne 1)) { + $Output .= $Self->{LayoutObject}->Header(Area => 'Agent', Title => 'Preferences'); + my %LockedData = $Self->{TicketObject}->GetLockedCount(UserID => $Self->{UserID}); + $Output .= $Self->{LayoutObject}->NavigationBar(LockData => \%LockedData); + $Output .= $Self->{LayoutObject}->Error( + Message => "$password_history_error Please try again!", + Comment => "$password_history_error Please try again!", + ); + $Output .= $Self->Form(); + $Output .= $Self->{LayoutObject}->Footer(); + } + else { + $Self->{UserObject}->SetPassword(UserLogin => $Self->{UserLogin}, PW => $Pw); + $Output .= $Self->{LayoutObject}->Redirect( + OP => "Action=CustomerPreferences", + ); + } } else { $Output .= $Self->{LayoutObject}->CustomerHeader(); $Output .= $Self->{LayoutObject}->CustomerWarning( - Message => 'Passwords dosn\'t match! Please try it again!', - Comment => 'Passwords dosn\'t match! Please try it again!', + Message => "Passwords don't match! Please try again!", + Comment => "Passwords don't match! Please try again!", ); $Output .= $Self->{LayoutObject}->CustomerFooter(); } Index: otrs/Kernel/Output/HTML/Admin.pm diff -u otrs/Kernel/Output/HTML/Admin.pm:1.1.1.1 otrs/Kernel/Output/HTML/Admin.pm:1.2 --- otrs/Kernel/Output/HTML/Admin.pm:1.1.1.1 Sat Nov 13 22:14:15 2004 +++ otrs/Kernel/Output/HTML/Admin.pm Mon Mar 21 15:46:58 2005 @@ -2,7 +2,7 @@ # Kernel/Output/HTML/Admin.pm - provides generic admin HTML output # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: Admin.pm,v 1.1.1.1 2004/11/14 03:14:15 jason Exp $ +# $Id: Admin.pm,v 1.2 2005/03/21 20:46:58 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -14,7 +14,7 @@ use strict; use vars qw($VERSION); -$VERSION = '$Revision: 1.1.1.1 $'; +$VERSION = '$Revision: 1.2 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- @@ -75,6 +75,9 @@ if ($Entry->[0] =~ /^ValidID/i) { $Param{Value} = $Param{'ValidOption'}; } + elsif ($Entry->[0] =~ /^UserPasswor/i) { + $Param{Value} = "[0]\" value=\"\" size=\"35\" maxlength=\"50\" $Param{ReadOnlyType}>"; + } else { my $Value = $Self->{LayoutObject}->Ascii2Html( Text => $Param{$Entry->[0]} || '', Index: otrs/Kernel/System/CustomerUser.pm diff -u otrs/Kernel/System/CustomerUser.pm:1.1.1.1 otrs/Kernel/System/CustomerUser.pm:1.6 --- otrs/Kernel/System/CustomerUser.pm:1.1.1.1 Sat Nov 13 22:14:15 2004 +++ otrs/Kernel/System/CustomerUser.pm Tue Mar 22 13:18:11 2005 @@ -2,7 +2,7 @@ # Kernel/System/CustomerUser.pm - some customer user functions # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: CustomerUser.pm,v 1.1.1.1 2004/11/14 03:14:15 jason Exp $ +# $Id: CustomerUser.pm,v 1.6 2005/03/22 18:18:11 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -14,7 +14,7 @@ use strict; use vars qw(@ISA $VERSION); -$VERSION = '$Revision: 1.1.1.1 $'; +$VERSION = '$Revision: 1.6 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- Index: otrs/Kernel/System/PasswordHistory.pm diff -u /dev/null otrs/Kernel/System/PasswordHistory.pm:1.2 --- /dev/null Tue Apr 5 17:53:44 2005 +++ otrs/Kernel/System/PasswordHistory.pm Mon Apr 4 01:09:42 2005 @@ -0,0 +1,85 @@ +# -- +# Kernel/System/PasswordHistory.pm - password enforcement +# Copyright (C) 2001-2005 Jason Dixon
+# -- +# $Id: PasswordHistory.pm,v 1.2 2005/04/04 05:09:42 jason Exp $ +# -- +# This software comes with ABSOLUTELY NO WARRANTY. For details, see +# the enclosed file COPYING for license information (GPL). If you +# did not receive this file, see http://www.gnu.org/licenses/gpl.txt. +# -- + +package Kernel::System::PasswordHistory; + +use strict; + +use vars qw(@ISA $VERSION); +$VERSION = '$Revision: 1.2 $'; +$VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/; + +=head1 NAME + +Kernel::System::PasswordHistory - Password history check + +=head1 SYNOPSIS + +An object-oriented interface to compare a given password against a database of previous passwords. + +=head1 PUBLIC INTERFACE + +=over 4 + +=cut + +=item new() + +Create a password history object + + use Kernel::System::PasswordHistory; + + my $obj = Kernel::System::PasswordHistory->new( + dbh => $dbh, # database handle object + table => $db_table # password history table + user => 'jason', # user login + password => 'crypt_pw', # crypted password + history_num => 5, # number of entries to review + ); + +=cut + +my $HISTORY_NUM = 5; + +sub new { + my $class = shift; + bless { @_ }, $class; +} + +=item check() + +Reviews the user's password history going back history_num timestamps to check for uniqueness. Any return value other than "1" should be considered an error message. + + my $error = $obj->check; + +=cut + +sub check { + my $self = shift; + my $dbh = $self->{dbh}; + my $query = "SELECT pw FROM $self->{table} WHERE login = \'" . $self->{user} . "\'" . + " ORDER BY timestamp desc LIMIT " . + ($self->{history_num} || $HISTORY_NUM); + $dbh->Prepare(SQL => $query); + my @results; + while (my @result = $dbh->FetchrowArray()) { + push(@results, $result[0]); + } + if (grep { $_ eq $self->{password} } @results) { + my $err_msg = "Password has already been used within the last " . + ($self->{history_num} || $HISTORY_NUM) . " instances."; + return $err_msg; + } else { + return 1; + } +} + +1; Index: otrs/Kernel/System/PasswordValidate.pm diff -u /dev/null otrs/Kernel/System/PasswordValidate.pm:1.1 --- /dev/null Tue Apr 5 17:53:44 2005 +++ otrs/Kernel/System/PasswordValidate.pm Mon Apr 4 02:02:39 2005 @@ -0,0 +1,99 @@ +# -- +# Kernel/System/PasswordValidate.pm - password enforcement +# Copyright (C) 2001-2005 Jason Dixon +# -- +# $Id: PasswordValidate.pm,v 1.1 2005/04/04 06:02:39 jason Exp $ +# -- +# This software comes with ABSOLUTELY NO WARRANTY. For details, see +# the enclosed file COPYING for license information (GPL). If you +# did not receive this file, see http://www.gnu.org/licenses/gpl.txt. +# -- + +package Kernel::System::PasswordValidate; + +use strict; + +use vars qw(@ISA $VERSION); +$VERSION = '$Revision: 1.1 $'; +$VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/; + +=head1 NAME + +Kernel::System::PasswordValidate - Validate password based on character classes + +=head1 SYNOPSIS + +An object-oriented interface to validate password strings. Used to validate against minimum- and maximum-length values and a number of character class requirements (upper- and lower-case, numbers and symbols). + +=head1 PUBLIC INTERFACE + +=over 4 + +=cut + +=item new() + +Create a password object + + use Kernel::System::PasswordValidate; + + my $obj = Kernel::System::PasswordValidate->new( + password => 'changeme', + min_char_classes => 2, + min_length => 6, + max_length => 12, + ); + +=cut + +my $MIN_LENGTH = 6; +my $MAX_LENGTH = 12; +my $MIN_CHAR_CLASSES = 3; + +sub new { + my $class = shift; + bless { @_ }, $class; +} + +=item check() + +Validates the password object based on the parameters passed to the new() method. Any return value other than "1" should be considered an error message. + + my $error = $obj->check; + +=cut + +sub check { + my $self = shift; + my $matching_classes = 0; + # check for minimum length + unless (length($self->{'password'}) >= ($self->{'min_length'} || $MIN_LENGTH)) + { + my $error = "Password must be minimum " . ($self->{min_length} || $MIN_LENGTH) . " characters.\n"; + return $error; + } + # check for maximum length + unless (length($self->{'password'}) <= ($self->{'max_length'} || $MAX_LENGTH)) + { + my $error = "Password must be maximum " . ($self->{max_length} || $MAX_LENGTH) . " characters.\n"; + return $error; + } + # check for uppercase + $self->{'password'} =~ /[A-Z]/ && $matching_classes++; + # check for lowercase + $self->{'password'} =~ /[a-z]/ && $matching_classes++; + # check for numbers + $self->{'password'} =~ /[0-9]/ && $matching_classes++; + # check for symbols + $self->{'password'} =~ /[\W]/ && $matching_classes++; + + unless ($matching_classes >= ($self->{'$min_char_classes'} || $MIN_CHAR_CLASSES)) + { + my $error = "Password does not meet number of character class requirements.\n"; + return $error; + } + + return 1; +} + +1; Index: otrs/Kernel/System/User.pm diff -u otrs/Kernel/System/User.pm:1.1.1.1 otrs/Kernel/System/User.pm:1.2 --- otrs/Kernel/System/User.pm:1.1.1.1 Sat Nov 13 22:14:15 2004 +++ otrs/Kernel/System/User.pm Mon Apr 4 01:09:42 2005 @@ -2,7 +2,7 @@ # Kernel/System/User.pm - some user functions # Copyright (C) 2001-2004 Martin Edenhofer # -- -# $Id: User.pm,v 1.1.1.1 2004/11/14 03:14:15 jason Exp $ +# $Id: User.pm,v 1.2 2005/04/04 05:09:42 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -15,7 +15,7 @@ use Kernel::System::CheckItem; use vars qw(@ISA $VERSION); -$VERSION = '$Revision: 1.1.1.1 $'; +$VERSION = '$Revision: 1.2 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- @@ -283,14 +283,6 @@ close (IO); chomp $CryptedPw; } - # check pw - if ($CryptedPw eq $User{UserPw}) { - $Self->{LogObject}->Log( - Priority => 'notice', - Message => "Not possible to use the same password again!", - ); - return; - } # db quote foreach (keys %Param) { $Param{$_} = $Self->{DBObject}->Quote($Param{$_}); @@ -303,6 +295,11 @@ " $Self->{UserTableUserPW} = '$NewPw' ". " WHERE ". " $Self->{UserTableUser} = '$Param{UserLogin}'", + # add pw to history + ) && $Self->{DBObject}->Do( + SQL => "INSERT INTO system_user_password_history (login, pw, timestamp) VALUES ('" . + $Self->{DBObject}->Quote($Param{UserLogin}) . "', '" . + $Self->{DBObject}->Quote($CryptedPw) . "', NOW())", )) { # log notice $Self->{LogObject}->Log( Index: otrs/Kernel/System/CustomerUser/DB.pm diff -u otrs/Kernel/System/CustomerUser/DB.pm:1.2 otrs/Kernel/System/CustomerUser/DB.pm:1.6 --- otrs/Kernel/System/CustomerUser/DB.pm:1.2 Tue Dec 21 22:46:16 2004 +++ otrs/Kernel/System/CustomerUser/DB.pm Mon Apr 4 01:09:48 2005 @@ -2,7 +2,7 @@ # Kernel/System/CustomerUser/DB.pm - some customer user functions # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: DB.pm,v 1.2 2004/12/22 03:46:16 jason Exp $ +# $Id: DB.pm,v 1.6 2005/04/04 05:09:48 jason Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you @@ -15,7 +15,7 @@ use Kernel::System::CheckItem; use vars qw(@ISA $VERSION); -$VERSION = '$Revision: 1.2 $'; +$VERSION = '$Revision: 1.6 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# -- @@ -389,6 +389,7 @@ } # -- sub CustomerUserUpdate { + my $Self = shift; my %Param = @_; # check ro/rw @@ -399,8 +400,11 @@ # check needed stuff foreach my $Entry (@{$Self->{CustomerUserMap}->{Map}}) { if (!$Param{$Entry->[0]} && $Entry->[4]) { - $Self->{LogObject}->Log(Priority => 'error', Message => "Need $Entry->[0]!"); - return; + # password should not be required field for AdminCustomerUser + if ($Entry->[0] ne 'UserPassword') { + $Self->{LogObject}->Log(Priority => 'error', Message => "Need $Entry->[0]!"); + return; + } } } # check email address @@ -436,7 +440,8 @@ ); # check pw my $GetPw = $UserData{UserPassword} || ''; - if ($GetPw ne $Param{UserPassword}) { + # only update if password is different AND has been submitted in form + if (($GetPw ne $Param{UserPassword}) && (length($Param{UserPassword}) > 0)) { $Self->SetPassword(UserLogin => $Param{UserLogin}, PW => $Param{UserPassword}); } return 1; @@ -498,7 +503,12 @@ " $Param{PasswordCol} = '".$Self->{DBObject}->Quote($CryptedPw)."' ". " WHERE ". " $Param{LoginCol} = '".$Self->{DBObject}->Quote($Param{UserLogin})."'", - )) { + # add pw to history + ) && $Self->{DBObject}->Do( + SQL => "INSERT INTO customer_user_password_history (login, pw, timestamp) VALUES ('" . + $Self->{DBObject}->Quote($Param{UserLogin}) . "', '" . + $Self->{DBObject}->Quote($CryptedPw) . "', NOW())", + )) { # log notice $Self->{LogObject}->Log( Priority => 'notice', Index: otrs/bin/cgi-bin/customer.pl diff -u otrs/bin/cgi-bin/customer.pl:1.1.1.1 otrs/bin/cgi-bin/customer.pl:1.3 --- otrs/bin/cgi-bin/customer.pl:1.1.1.1 Sat Nov 13 22:14:16 2004 +++ otrs/bin/cgi-bin/customer.pl Tue Mar 22 13:18:16 2005 @@ -3,7 +3,7 @@ # customer.pl - the global CGI handle file (incl. auth) for OTRS # Copyright (C) 2001-2004 Martin Edenhofer
# -- -# $Id: customer.pl,v 1.1.1.1 2004/11/14 03:14:16 jason Exp $ +# $Id: customer.pl,v 1.3 2005/03/22 18:18:16 jason Exp $ # -- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ use strict; use vars qw($VERSION @INC); -$VERSION = '$Revision: 1.1.1.1 $'; +$VERSION = '$Revision: 1.3 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;
# --
------------------------------------------------------------------------
_______________________________________________ OTRS mailing list: dev - Webpage: http://otrs.org/ Archive: http://lists.otrs.org/pipermail/dev To unsubscribe: http://lists.otrs.org/cgi-bin/listinfo/dev
participants (3)
-
Alessandro Ranellucci
-
Jason Dixon
-
Martin Edenhofer