Skip to content

Commit

Permalink
Item14348: Fixed array usermapping.
Browse files Browse the repository at this point in the history
- Extracted user -> dbuser/dbpassword mapping code into a separate
method getDBUser().

- Added _getConnection() method to fetch and validate a cached
connection at single point of failure.

- Added test_usermap in DatabaseContribOOTests
  • Loading branch information
vrurg committed Apr 6, 2017
1 parent 7840e21 commit 14ea913
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 48 deletions.
107 changes: 60 additions & 47 deletions lib/Foswiki/Contrib/DatabaseContrib.pm
Expand Up @@ -219,6 +219,20 @@ sub _find_mapping {
return $found ? $match : undef;
}

sub _getConnection {
my $self = shift;
my $conname = shift;

my $connection = $self->_cache('connections')->{$conname};
unless ( defined $connection ) {
return undef
if $self->_fail(
"No connection `$conname' defined in the cofiguration");
}

return $connection;
}

sub new {
my $self = bless {}, shift;

Expand Down Expand Up @@ -301,7 +315,7 @@ sub dbh {
sub _set_codepage {
my $self = shift;
my ($conname) = @_;
my $connection = $self->_cache('connections')->{$conname};
my $connection = $self->_getConnection($conname);
my $dbh = $self->dbh($conname);

if ( $connection->{codepage} ) {
Expand Down Expand Up @@ -358,16 +372,7 @@ sub access_allowed {
$checked_atoms->{$access_type} = 1;
push @{ $checked_atoms->{_order} }, $access_type;

my $connection = $self->_cache('connections')->{$conname};

#say STDERR " " x $nesting, "access_allowed(", join(",", map {$_ // '*undef*'} @_), ")";

unless ( defined $connection ) {

#say STDERR " " x $nesting, "No $conname in connections";
return undef
if $self->_fail("No connection $conname in the configuration");
}
my $connection = $self->_getConnection($conname);

my $acl_inheritance = $self->_cache('acl_inheritance') // {};

Expand Down Expand Up @@ -445,32 +450,13 @@ sub access_allowed {
return $match;
}

sub connect {
my $self = shift;
my $conname = shift;

my $connection = $self->_cache('connections')->{$conname};
unless ( defined $connection ) {
return
if _failure "No connection `$conname' defined in the cofiguration";
}

my $dbh = $self->dbh($conname);

return $dbh if defined $dbh;
sub map2dbuser {
my $self = shift;
my ( $conname, $user ) = @_;

my @required_fields = qw(database driver);
my $connection = $self->_getConnection($conname);

unless ( defined $connection->{dsn} ) {
foreach my $field (@required_fields) {
unless ( defined $connection->{$field} ) {
return undef
if $self->_fail(
"Required field $field is not defined for database connection $conname.\n"
);
}
}
}
return () unless defined $connection;

my ( $dbuser, $dbpass ) =
( $connection->{user} // "", $connection->{password} // "" );
Expand All @@ -489,32 +475,59 @@ sub connect {
@maps =
sort { ( $a =~ /Group$/ ) <=> ( $b =~ /Group$/ ) }
keys %{ $connection->{usermap} };
$connection->{_maphash} = $connection->{usermap};
}
elsif ( ref( $connection->{usermap} ) eq 'ARRAY' ) {
my ( @u, @g ); # User and group mappings
foreach my $ent ( @{ $connection->{usermap} } ) {
if ( $ent =~ /Group$/ ) {
push @g, $ent;
}
else {
push @u, $ent;
}
my @m = @{ $connection->{usermap} };
delete $connection->{_maphash};
foreach my $i ( 0 .. ( scalar(@m) / 2 - 1 ) ) {
my $ent = $m[ $i * 2 ];
push @maps, $ent;
$connection->{_maphash}{$ent} = $m[ $i * 2 + 1 ]
unless defined $connection->{_maphash}{$ent};
}
@maps = ( @u, @g );
}
else {
return undef
if $self->_fail(
"Connection '$conname' usermap is neither hash nor arrayref");
}

my $usermap_key = $self->_find_mapping( \@maps );
my $usermap_key = $self->_find_mapping( \@maps, $user );
if ($usermap_key) {
$dbuser = $connection->{usermap}{$usermap_key}{user};
$dbpass = $connection->{usermap}{$usermap_key}{password};
$dbuser = $connection->{_maphash}{$usermap_key}{user};
$dbpass = $connection->{_maphash}{$usermap_key}{password};
}
}

return ( $dbuser, $dbpass );
}

sub connect {
my $self = shift;
my $conname = shift;

my $connection = $self->_getConnection($conname);

my $dbh = $self->dbh($conname);

return $dbh if defined $dbh;

my @required_fields = qw(database driver);

unless ( defined $connection->{dsn} ) {
foreach my $field (@required_fields) {
unless ( defined $connection->{$field} ) {
return undef
if $self->_fail(
"Required field $field is not defined for database connection $conname.\n"
);
}
}
}

my ( $dbuser, $dbpass ) = $self->map2dbuser($conname);

# CONNECTING TO $conname, ", (defined $connection->{dbh} ? $connection->{dbh} : "*undef*"), ", ", (defined $dbi_connections{$conname}{dbh} ? $dbi_connections{$conname}{dbh} : "*undef*"), "\n";

# CONNECTING TO $conname\n";
Expand Down
74 changes: 73 additions & 1 deletion test/unit/DatabaseContrib/DatabaseContribOOTests.pm
Expand Up @@ -136,7 +136,9 @@ sub test_permissions {

foreach my $bunch (qw(valid invalid)) {
foreach my $access_type ( keys %{ $this->{check_pairs}{$bunch} } ) {
foreach my $test_pair ( @{ $this->{check_pairs}{$bunch}{$access_type} } ) {
foreach
my $test_pair ( @{ $this->{check_pairs}{$bunch}{$access_type} } )
{
if ( $bunch eq 'valid' ) {
$this->assert(
$dbc->access_allowed(
Expand Down Expand Up @@ -179,4 +181,74 @@ sub test_permissions {

}

sub test_usermap {
my $this = shift;

my $query = Unit::Request->new("");
$this->createNewFoswikiSession( $Foswiki::cfg{AdminUserLogin}, $query );

$Foswiki::cfg{Extensions}{DatabaseContrib}{connections}{sample_connection}
{usermap} = [
AdminGroup => {
user => "dbadmin",
password => "admin_pass",
},
MightyAdmin => {
user => "madmin",
password => "ma_pass",
},
DummyGroup => {
user => "dummy",
password => "nopassword",
},
JohnSmith => {
user => "jsmith",
password => "js_pass",
},
];

my $dbc;
$this->assert_not_null(
$dbc = Foswiki::Contrib::DatabaseContrib->new,
"Failed to create a new DatabaseContrib object",
);

my ( $dbuser, $dbpass );

( $dbuser, $dbpass ) =
$dbc->map2dbuser( "sample_connection", 'MightyAdmin' );

# Must not be madmin/ma_pass because AdminGroup is defined first.
$this->assert_equals( 'dbadmin', $dbuser );
$this->assert_equals( 'admin_pass', $dbpass );

( $dbuser, $dbpass ) = $dbc->map2dbuser( "sample_connection", 'JohnSmith' );

$this->assert_equals( 'jsmith', $dbuser );
$this->assert_equals( 'js_pass', $dbpass );

Foswiki::Func::addUserToGroup( 'JohnSmith', 'DummyGroup', 0 );

# Now group shall override user's mapping because the group comes first in
# the list.
( $dbuser, $dbpass ) = $dbc->map2dbuser( "sample_connection", 'JohnSmith' );

$this->assert_equals( 'dummy', $dbuser );
$this->assert_equals( 'nopassword', $dbpass );

$this->assert(
Foswiki::Func::removeUserFromGroup( 'JohnSmith', 'DummyGroup' ),
"Failed to remove user JohnSmith from DummyGroup"
);

# And it must map to the user again because he is not a group member
# anymore.
( $dbuser, $dbpass ) = $dbc->map2dbuser( "sample_connection", 'JohnSmith' );

$this->assert_equals( 'jsmith', $dbuser );
$this->assert_equals( 'js_pass', $dbpass );

say STDERR "$dbuser, $dbpass";
}

1;

0 comments on commit 14ea913

Please sign in to comment.