site/cgi-bin/modules/BSE/MessageScanner.pm
site/cgi-bin/modules/BSE/NLFilter/SQL.pm
site/cgi-bin/modules/BSE/NotifyFiles.pm
+site/cgi-bin/modules/BSE/Password.pod
+site/cgi-bin/modules/BSE/Password/Crypt.pm
+site/cgi-bin/modules/BSE/Password/CryptMD5.pm
+site/cgi-bin/modules/BSE/Password/CryptSHA256.pm
+site/cgi-bin/modules/BSE/Password/Plain.pm
+site/cgi-bin/modules/BSE/Passwords.pm
site/cgi-bin/modules/BSE/Permissions.pm
site/cgi-bin/modules/BSE/ProductImportXLS.pm
site/cgi-bin/modules/BSE/Report.pm
base_id integer not null,
logon varchar(60) not null,
name varchar(255) not null,
- password varchar(80) not null,
+ password varchar(255) not null,
perm_map varchar(255) not null,
+ password_type varchar(20) not null default 'plain',
primary key (base_id),
unique (logon)
);
and return $class->_service_error($req, undef, \%errors, "FIELD");
require BSE::TB::AdminUsers;
my $user = BSE::TB::AdminUsers->getBy(logon=>$logon);
- $user && $user->{password} eq $password
+ my $error;
+ my $match = $user->check_password($password, \$error);
+ if (!$match && $error eq "LOAD") {
+ $errors{logon} = "Could not load password check module for type ".$user->password_type;
+ return $class->_service_error($req, undef, \%errors, "FIELD");
+ }
+ $user && $match
or return $class->_service_error($req, "Invalid logon or password", {}, "INVALID");
$req->session->{adminuserid} = $user->{id};
delete $req->session->{csrfp};
$$rmsg = "Cannot create work userfile $work: $!";
return;
}
+ my $bad_count = 0;
+ my $bad_user;
for my $user (@users) {
- my $salt = join '', @saltchars[rand(@saltchars), rand(@saltchars)];
- my $cryptpw = crypt $user->{password}, $salt;
+ my $cryptpw;
+ if ($user->password_type eq "plain") {
+ my $salt = join '', @saltchars[rand(@saltchars), rand(@saltchars)];
+ $cryptpw = crypt $user->{password}, $salt;
+ }
+ elsif ($user->password_type =~ /^crypt/) {
+ $cryptpw = $user->password;
+ }
+ else {
+ $bad_user = $user;
+ ++$bad_count;
+ }
print USERS "$user->{logon}:$cryptpw\n";
}
+ if ($bad_count) {
+ $req->flash("Cannot handle password type " . $bad_user->password_type . " in userfile for up to $bad_count users");
+ }
close USERS;
chmod 0644, $work;
unless (rename $work, $userfile) {
password => $password,
perm_map => '',
);
- my @cols = BSE::TB::AdminUser->columns;
- shift @cols;
- my $user = BSE::TB::AdminUsers->add(@user{@cols});
+ my $user = BSE::TB::AdminUsers->make(%user);
my $msg = "User $logon created";
or $errors{template_set} =
$req->text(bse_invalid_group_template_set =>
'Please select a valid template_set');
-
+
keys %errors
and return $class->req_addgroupform($req, undef, \%errors);
my $old = BSE::TB::AdminGroups->getBy(name=>$name)
&& length $password) {
if (length $confirm) {
if ($password eq $confirm) {
- $user->{password} = $password;
+ $user->changepw($password);
}
else {
$errors{confirm} = "Password and confirmation password didn't match"
$errors{oldpassword} = "Enter your current password";
}
else {
- $oldpw eq $user->{password}
+ $user->check_password($oldpw)
or $errors{oldpassword} = "Your old password is incorrect";
}
if (!defined $newpw || $newpw eq '') {
keys %errors
and return $class->req_form($req, undef, \%errors);
- $user->{password} = $newpw;
+ $user->changepw($newpw);
$user->save;
my $r = $cgi->param('r');
select bs.*, us.* from admin_base bs, admin_users us
where bs.id = us.base_id and bs.id = ?
SQL
- addAdminUser => 'insert into admin_users values(?,?,?,?,?)',
- replaceAdminUser => 'replace into admin_users values(?,?,?,?,?)',
+ addAdminUser => 'insert into admin_users values(?,?,?,?,?,?)',
+ replaceAdminUser => 'replace into admin_users values(?,?,?,?,?,?)',
deleteAdminUser => 'delete from admin_users where base_id = ?',
adminUsersGroups => <<SQL,
select bs.*, gr.*
--- /dev/null
+=head1 NAME
+
+BSE::Password - interface for password hashing and checking.
+
+=head1 SYNOPSIS
+
+ my $pass_handler = BSE::Password::Foo->new;
+ my $hash = $pass_handler->hash($password);
+ if ($pass_handler->check($hash, $password)) {
+ # success
+ }
+
+=head1 DESCRIPTION
+
+This module implements a simple mechanism for hashing passwords.
+
+Sub-classes should implement three methods:
+
+=over
+
+=item new()
+
+Create a new handler. Should return nothing if this mechanism is not
+available, eg. if modules required to implement it aren't available.
+
+=item hash($password)
+
+Return a hash to store for later checks.
+
+=item check($hash, $password)
+
+Check that the password matches the hash. Return true on a match.
+
+=back
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>
+
+=cut
--- /dev/null
+package BSE::Password::Crypt;
+use strict;
+
+sub new {
+ my ($class) = @_;
+
+ return bless {}, $class;
+}
+
+sub _salt {
+ return join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]
+}
+
+sub hash {
+ my ($self, $password) = @_;
+
+ my $salt = $self->_salt;
+
+ return crypt($password, $salt);
+}
+
+sub check {
+ my ($self, $hash, $password) = @_;
+
+ return crypt($password, $hash) eq $hash;
+}
+
+1;
--- /dev/null
+package BSE::Password::CryptMD5;
+use strict;
+use base "BSE::Password::Crypt";
+
+sub new {
+ my ($class) = @_;
+
+ crypt("test", '$1$00000000') eq '$1$00000000$/6RgkLRMOYgcMlYPGNjSy.'
+ or return;
+
+ return $class->SUPER::new();
+}
+
+sub _salt {
+ return '$1$' . join "",
+ ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[map rand 64, 1..8]
+}
+
+1;
--- /dev/null
+package BSE::Password::CryptSHA256;
+use strict;
+use base "BSE::Password::Crypt";
+
+sub new {
+ my ($class) = @_;
+
+ crypt("test", '$5$00000000') eq '$5$00000000$rlV/Cvf6KAPRr28eKJSvYDnUm9rmJztOXHH0jx7zhnB'
+ or return;
+
+ return $class->SUPER::new();
+}
+
+sub _salt {
+ return '$5$' . join "",
+ ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[map rand 64, 1..16]
+}
+
+1;
--- /dev/null
+package BSE::Password::Plain;
+use strict;
+
+sub new {
+ my ($class) = @_;
+
+ return bless {}, $class;
+}
+
+sub hash {
+ my ($self, $password) = @_;
+
+ return $password;
+}
+
+sub check {
+ my ($self, $hash, $password) = @_;
+
+ return $hash eq $password;
+}
+
+1;
--- /dev/null
+package BSE::Passwords;
+use strict;
+
+# wrapper around using the BSE::Password classes
+
+sub new_password_hash {
+ my ($self, $password) = @_;
+
+ my ($obj, $name) = $self->new_password_handler;
+
+ return ( $obj->hash($password), $name );
+}
+
+sub check_password_hash {
+ my ($self, $hash, $type, $password, $error) = @_;
+
+ my $obj = $self->_load($type);
+ unless ($obj) {
+ $$error = "LOAD";
+ return;
+ }
+
+ unless ($obj->check($hash, $password)) {
+ $$error = "INVALID";
+ return;
+ }
+
+ return 1;
+}
+
+sub new_password_handler {
+ my ($self) = @_;
+
+ my $cfg = BSE::Cfg->single;
+ my @handlers = split /,/, $cfg->entry("basic", "passwords", "cryptSHA256,cryptMD5,crypt,plain");
+
+ for my $handler (@handlers) {
+ my $obj = $self->_load($handler);
+ $obj and return ($obj, $handler);
+ }
+
+ require BSE::Password::Plain;
+ return ( BSE::Password::Plain->new, "plain" );
+}
+
+sub _load {
+ my ($self, $handler) = @_;
+
+ my $class = "BSE::Password::\u$handler";
+ my $file = "BSE/Password/\u$handler.pm";
+
+ eval { require $file; 1 }
+ or return;
+
+ my $obj = $class->new
+ or return;
+
+ return $obj;
+}
+
+1;
+
sub columns {
return ($_[0]->SUPER::columns,
- qw/base_id logon name password perm_map/);
+ qw/base_id logon name password perm_map password_type/);
}
sub bases {
BSE::TB::AdminGroups->getSpecial(forUser => $self->{id});
}
+sub changepw {
+ my ($self, $password) = @_;
+
+ require BSE::Passwords;
+
+ my ($hash, $type) = BSE::Passwords->new_password_hash($password);
+
+ $self->set_password($hash);
+ $self->set_password_type($type);
+
+ 1;
+}
+
+sub check_password {
+ my ($self, $password, $error) = @_;
+
+ require BSE::Passwords;
+ return BSE::Passwords->check_password_hash($self->password, $self->password_type, $password, \$error);
+}
+
1;
return 'BSE::TB::AdminUser';
}
+sub make {
+ my ($self, %opts) = @_;
+
+ require BSE::Passwords;
+ my $password = delete $opts{password};
+ my ($hash, $type) = BSE::Passwords->new_password_hash($password);
+
+ $opts{password} = $hash;
+ $opts{password_type} = $type;
+
+ return $self->SUPER::make(%opts);
+}
+
1;
Column base_id;int(11);NO;NULL;
Column logon;varchar(60);NO;NULL;
Column name;varchar(255);NO;NULL;
-Column password;varchar(80);NO;NULL;
+Column password;varchar(255);NO;NULL;
Column perm_map;varchar(255);NO;NULL;
+Column password_type;varchar(20);NO;plain;
Index PRIMARY;1;[base_id]
Index logon;1;[logon]
Table article