Skip Menu |

This queue is for tickets about the RT-Authen-ExternalAuth CPAN distribution.

Report information
The Basics
Id: 92514
Status: new
Priority: 0/
Queue: RT-Authen-ExternalAuth

People
Owner: Nobody in particular
Requestors: cbrandt [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.17
Fixed in: (no value)



Subject: DBI plugin doesn't normalize case
Using the DBI plugin to authenticate against a database, if you try to create a user with a mixed case email address like User1@example.com, the DB can return data in a datastructure keyed by user1@example.com. This will fail to match, key fields like Name won't be set, and RT will fail to create a new user account. It works if the provided username gets the casing right and it matches. The correct fix is that RT-Authen-ExternalAuth needs to normalize data. One attempt is attached, but this didn't quite solve it. More investigation needed.
Subject: 0001-Work-in-progress.patch
From e00eb8ad529742a6aacf2d11b17f56679edeb330 Mon Sep 17 00:00:00 2001 From: Kevin Falcone <falcone@bestpractical.com> Date: Mon, 27 Jan 2014 12:25:51 -0500 Subject: [PATCH] Work in progress. Start using NAME_lc to force the hashref to come back with a predictable key. This isn't working locally, may be DBD::Sqlite, may be me misreading DBI.pm. It may actually be easier to just switch to the arrayref syntax. --- lib/RT/Authen/ExternalAuth/DBI.pm | 29 ++++++++++++++++++++++++++--- xt/sqlite.t | 4 ++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/lib/RT/Authen/ExternalAuth/DBI.pm b/lib/RT/Authen/ExternalAuth/DBI.pm index 235c3a4..9039dfb 100644 --- a/lib/RT/Authen/ExternalAuth/DBI.pm +++ b/lib/RT/Authen/ExternalAuth/DBI.pm @@ -175,6 +175,12 @@ sub GetAuth { my $dbh = _GetBoundDBIObj($config); return 0 unless $dbh; + RT->Logger->debug("DBI Search ", + "== Query:", + $query, + "== Params:", + join(',',@params)); + my $results_hashref = $dbh->selectall_hashref($query,$db_u_field,{},@params); $dbh->disconnect(); @@ -201,7 +207,7 @@ sub GetAuth { } # Get the user's password from the database query result - my $pass_from_db = $results_hashref->{$username}->{$db_p_field}; + my $pass_from_db = $results_hashref->{lc $username}->{$db_p_field}; if ( $db_p_check ) { unless ( ref $db_p_check eq 'CODE' ) { @@ -330,11 +336,19 @@ sub CanonicalizeUserInfo { my $query = "SELECT $fields FROM $table WHERE $where_key=?"; my @bind_params = ($where_value); + RT->Logger->debug("DBI Search ", + "== Query:", + $query, + "== Params:", + join(',',@bind_params)); + # Uncomment this to trace basic DBI throughput in a log # DBI->trace(1,'/tmp/dbi.log'); my $dbh = _GetBoundDBIObj($config); my $results_hashref = $dbh->selectall_hashref($query,$key,{},@bind_params); $dbh->disconnect(); + use Data::Dumper; RT->Logger->debug(Dumper $results_hashref); + if ((scalar keys %$results_hashref) != 1) { # If returned users <> 1, we have no single unique user, so prepare to die @@ -363,7 +377,8 @@ sub CanonicalizeUserInfo { # We haven't dropped out, so DB search must have succeeded with # exactly 1 result. Get the result and set $found to 1 - my $result = $results_hashref->{$value}; + my $result = $results_hashref->{lc $value}; + use Data::Dumper; RT->Logger->debug(Dumper $result); # Use the result to populate %params for every key we're given in the config foreach my $key (keys(%{$config->{'attr_map'}})) { @@ -371,6 +386,7 @@ sub CanonicalizeUserInfo { } $found = 1; + use Data::Dumper; RT->Logger->debug(Dumper \%params); return ($found, %params); } @@ -476,7 +492,7 @@ sub UserDisabled { # otherwise all should be well # $user_db_disable_value = The value for "disabled" returned from the DB - my $user_db_disable_value = $results_hashref->{$username}->{$disable_field}; + my $user_db_disable_value = $results_hashref->{lc $username}->{$disable_field}; # For each of the values in the (list of values that we consider to mean the user is disabled).. foreach my $disable_value (@{$disable_values_list}){ @@ -619,6 +635,13 @@ sub _GetBoundDBIObj { my $dbh = DBI->connect($dsn, $db_user, $db_pass,{RaiseError => 1, AutoCommit => 0 }) or die $DBI::errstr; + + # We love using selectall_hashref, which uses a columnkey which comes back in + # the case the database is stored in. On a case insensitive database, we + # have no guarantee that this will match the user supplied input, so we force + # it all to lowercase. This might break if you were relying on an external + # database source with two users named user1 and User1 who were distcint. + $dbh->{FetchHashKeyName} = 'NAME_lc'; # If we didn't die, return the DBI object handle # and hope it's treated sensibly and correctly # destroyed by the calling code diff --git a/xt/sqlite.t b/xt/sqlite.t index 179861f..50a93a1 100644 --- a/xt/sqlite.t +++ b/xt/sqlite.t @@ -18,14 +18,14 @@ my $dbh = DBI->connect("dbi:SQLite:$dbname"); my $password = Digest::MD5::md5_hex('password'); my $schema = <<"EOF"; CREATE TABLE users ( - username varchar(200) NOT NULL, + username varchar(200) NOT NULL collate nocase, password varchar(40) NULL, email varchar(16) NULL ); EOF $dbh->do( $schema ); $dbh->do( -"INSERT INTO $table VALUES ( 'testuser', '$password', 'testuser\@invalid.tld')" +"INSERT INTO $table VALUES ( 'Testuser', '$password', 'testuser\@invalid.tld')" ); RT->Config->Set( ExternalAuthPriority => ['My_SQLite'] ); -- 1.8.2.3