[Bps-public-commit] r17776 - in RT-Authen-ExternalAuth/trunk: html/Callbacks/ExternalAuth/autohandler lib/RT/Authen lib/RT/Authen/ExternalAuth

zordrak at bestpractical.com zordrak at bestpractical.com
Fri Jan 16 07:18:57 EST 2009


Author: zordrak
Date: Fri Jan 16 07:18:56 2009
New Revision: 17776

Added:
   RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI/
   RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI/Cookie.pm
Modified:
   RT-Authen-ExternalAuth/trunk/html/Callbacks/ExternalAuth/autohandler/Auth
   RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth.pm
   RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI.pm

Log:
RT::Authen::ExternalAuth // 2009-01-16 Zordrak <zordrak at cpan.org> // CookieAuth pulled in as subpackage of DBI

Modified: RT-Authen-ExternalAuth/trunk/html/Callbacks/ExternalAuth/autohandler/Auth
==============================================================================
--- RT-Authen-ExternalAuth/trunk/html/Callbacks/ExternalAuth/autohandler/Auth	(original)
+++ RT-Authen-ExternalAuth/trunk/html/Callbacks/ExternalAuth/autohandler/Auth	Fri Jan 16 07:18:56 2009
@@ -4,7 +4,7 @@
 <%init>
 
 use RT::Authen::ExternalAuth;
-
+######################################################################################
 # Work around a bug in the RT 3.8.0 and 3.8.1 plugin system (fixed in 3.8.2)
 # Temporarily force RT to reload RT::User, since it isn't being loaded
 # correctly as a plugin.
@@ -18,6 +18,120 @@
 }
 ######################################################################################
 
+
+
+##########################################################
+##################### CA #################################
+##########################################################
+# 
+# If the user is logging in, let's authenticate; if they can auth but don't load
+# (e.g. they don't have an account but external auth succeeds), we'll autocreate
+# their account.
+unless ($session{'CurrentUser'}) {
+    
+    # Check to see if we've been asked to authenticate from cookies
+    # If so, confirm the username authenticated by the cookies
+    if ($RT::UseExternalCookieAuthService){
+        $RT::Logger->debug( "Cookie Authentication (",
+                            $RT::UseExternalCookieAuthService,
+                            ") requested");
+        
+        # Use the package we need for cookie authentication
+
+        my ($cookie_user, $confirmed_by_cookie) = RT::Authen::ExternalAuth::CheckCookies();
+        
+        # If CheckCookies gave us a user, set it as the global user.
+        $user = $cookie_user if defined($cookie_user);
+        
+        # If CheckCookies is happy the user it gave us is authenticated...
+        if ($confirmed_by_cookie) {
+            
+            # This WAS used to stop a pointless LookupExternalUserInfo 
+            # called by UpdateFromExternal later on since it's already
+            # called by RT::User::Create if the user is autocreated
+            # but this has been deprecated pending a little bit of a 
+            # rewrite since I realised that we're not calling
+            # CanonicalizeUserInfo but UpdateFromExternal which is the
+            # only code that checks whether the user is externally
+            # marked as disabled.
+            my $user_autocreated = 0;
+
+            # Create a new CurrentUser for the session and try and load
+            # a known user with the username given by the cookie check.
+            $session{'CurrentUser'} = RT::CurrentUser->new();
+            $session{'CurrentUser'}->Load($user);
+
+            # Unless we loaded a valid user with a UserID,
+            # autocreate a new user
+            unless ($session{'CurrentUser'}->Id) {
+                
+                # Start with a new SystemUser
+                my $UserObj = RT::User->new($RT::SystemUser);
+                
+                # Set the user's name to the one we were given
+                my ($val, $msg) = $UserObj->SetName($user);
+
+                # Commit the created user to the DB
+                ($val, $msg) = 
+                  $UserObj->Create(%{ref($RT::AutoCreate) ? $RT::AutoCreate : {}},
+                                   Name   => $user,
+                                   Gecos  => $user,
+                                  );
+                                   
+                # Log the creation
+                $RT::Logger->info(  "Autocreated authenticated user",
+                                    $UserObj->Name,
+                                    "(",
+                                    $UserObj->Id,
+                                    ")");
+                
+                # Mark that user was created here so that we 
+                # don't bother looking up their information
+                $user_autocreated = 1;
+
+                # Load the newly-created user as the CurrentUser in $session
+                # To RT, this means we now have a valid, authenticated user
+                $session{'CurrentUser'}->Load($user) if $UserObj->Id;
+            }
+        
+        
+            # If we now have a completely valid RT user to play with,
+            # and the user is not disabled in RT, then...
+            if ($session{'CurrentUser'} && $session{'CurrentUser'}->Id) {
+                    
+                # ALWAYS call UpdateFromExternal otherwise externally-disabled
+                # users could be allowed in.
+                $session{'CurrentUser'}->UserObj->UpdateFromExternal();
+
+                # Now their information is up to date,
+                # check if the user is disabled.
+                
+                # If the user is disabled, kill their session,
+                # otherwise, authentication is successful.
+                if($session{'CurrentUser'}->UserObj->Disabled) {
+                    delete $session{'CurrentUser'};
+                } else {     
+                    # Do not delete the session. User stays logged in.
+                    # Log the success.
+                    $RT::Logger->info(  "Successful login for",
+                                        $user,
+                                        "from",
+                                        $ENV{'REMOTE_ADDR'});
+                }
+            } else {
+                # If we have no complete user.
+                delete $session{'CurrentUser'};
+            }
+        }
+    } else {
+        $RT::Logger->debug("RT is capable of External Cookie Auth, but it has not been enabled.");
+    }
+}
+
+############################################################
+############### EA #########################################
+############################################################
+#
 # If the user is logging in, let's authenticate; if they can auth but don't load
 # (e.g. they don't have an account but external auth succeeds), we'll autocreate
 # their account.

Modified: RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth.pm
==============================================================================
--- RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth.pm	(original)
+++ RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth.pm	Fri Jan 16 07:18:56 2009
@@ -20,6 +20,7 @@
 ok(require RT::Authen::ExternalAuth);
 ok(require RT::Authen::ExternalAuth::LDAP);
 ok(require RT::Authen::ExternalAuth::DBI);
+ok(require RT::Authen::ExternalAuth::Cookie);
 
 =end testing
 
@@ -27,6 +28,7 @@
 
 use RT::Authen::ExternalAuth::LDAP;
 use RT::Authen::ExternalAuth::DBI;
+use RT::Authen::ExternalAuth::Cookie;
 use Data::Dumper;
 
 sub UpdateUserInfo {
@@ -346,4 +348,10 @@
     return($found || $RT::AutoCreateNonExternalUsers);
    
 }
+
+sub CheckCookies {
+
+    return RT::Authen::ExternalAuth::Cookie::CheckCookies();    
+
+}
 1;

Modified: RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI.pm
==============================================================================
--- RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI.pm	(original)
+++ RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI.pm	Fri Jan 16 07:18:56 2009
@@ -312,7 +312,7 @@
 
 sub _GetBoundDBIObj {
     
-    # Config as hashref.
+    # Config as hashref.   #### Or is this a hash?
     my $config = shift;
 
     # Extract the relevant information from the config.

Added: RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI/Cookie.pm
==============================================================================
--- (empty file)
+++ RT-Authen-ExternalAuth/trunk/lib/RT/Authen/ExternalAuth/DBI/Cookie.pm	Fri Jan 16 07:18:56 2009
@@ -0,0 +1,127 @@
+package RT::Authen::ExternalAuth::DBI::Cookie;
+
+use strict;
+use RT::Authen::ExternalAuth::DBI;
+use CGI::Cookie;
+
+# {{{ sub CheckCookies
+sub CheckCookies {
+
+# We are not a User object any more!
+#    my $self = RT::User->new($RT::SystemUser);
+
+    $RT::Logger->debug( (caller(0))[3],
+                        "Checking Browser Cookies for an Authenticated User");
+
+    my $confirmed_by_cookie = 0;
+    my $username; # $user changed to $username as not object but string
+
+    # Pull in all cookies from browser within our cookie domain
+    my %cookies = CGI::Cookie->fetch();
+
+    # Get our cookie and database info...
+    my $config = $RT::CookieSettings;
+
+    unless ($RT::UseExternalCookieAuthService){
+        $RT::Logger->debug( "External Cookie Auth is not enabled.",
+                            "Please check your config for \$UseExternalCookieAuthService");
+        return (undef,0);
+    }
+    
+    # The name of the cookie
+    my $cookie_name = $config->{'name'};
+
+    # If the cookie is set, get the value, if it's not set, get out now!
+    my $cookie_value;
+    if (defined $cookies{$cookie_name}) {
+      $cookie_value = $cookies{$cookie_name}->value;
+      $RT::Logger->debug(  "Cookie Found!",
+                           ":: $cookie_name ::",
+                           "Attempting to use for authentication");
+    } else {
+        $RT::Logger->debug( "Cookie Auth Failed:",
+                            "Cookie Not Assigned");
+        return ($user,$confirmed_by_cookie);
+    }
+
+    # The table mapping usernames to the Username Match Key
+    my $u_table     = $config->{'u_table'};
+    # The username field in that table
+    my $u_field     = $config->{'u_field'};
+    # The field that contains the Username Match Key
+    my $u_match_key = $config->{'u_match_key'};
+    
+    # The table mapping cookie values to the Cookie Match Key
+    my $c_table     = $config->{'c_table'};
+    # The cookie field in that table - The same as the cookie name if unspecified
+    my $c_field     = $config->{'c_field'};
+    # The field that connects the Cookie Match Key
+    my $c_match_key = $config->{'c_match_key'};
+
+    # These are random characters to assign as table aliases in SQL
+    # It saves a lot of garbled code later on
+    my $u_table_alias = "u";
+    my $c_table_alias = "c";
+
+    # $tables will be passed straight into the SQL query
+    # I don't see this as a security issue as only the admin may modify the config file anyway
+    my $tables;
+    
+    # If the tables are the same, then the aliases should be the same
+    # and the match key becomes irrelevant. Ensure this all works out 
+    # fine by setting both sides the same. In either case, set an
+    # appropriate value for $tables.
+    if ($u_table eq $c_table) {
+	    $u_table_alias  = $c_table_alias;
+	    $u_match_key    = $c_match_key;
+	    $tables         = "$c_table $c_table_alias";	
+    } else {
+   	    $tables = "$c_table $c_table_alias, $u_table $u_table_alias";
+    }
+
+    my $select_fields = "$u_table_alias.$u_field";
+    my $where_statement = "$c_table_alias.$c_field = ? AND $c_table_alias.$c_match_key = $u_table_alias.$u_match_key";
+
+    my $query = "SELECT $select_fields FROM $tables WHERE $where_statement";
+    my @params = ($cookie_value);
+    my $service = 'Auth';
+
+    # Use this if you need to debug the DBI SQL process
+    # DBI->trace(1,'/tmp/dbi.log');        
+
+    my $dbh = RT::Authen::ExternalAuth::DBI::_GetBoundDBIObj($RT::ExternalSettings->{$config->{'db_service_name'}});
+    my $query_result_arrayref = $dbh->selectall_arrayref($query,{}, at params);
+    $dbh->disconnect();
+    
+    # The log messages say it all here...
+    my $num_rows = scalar @$query_result_arrayref;
+    my $confirmed_user;
+    if ($num_rows < 1) {
+        $RT::Logger->info(  "AUTH FAILED", 
+                            $cookie_name,
+                            "Cookie value not found in database.",
+                            "User passed an authentication token they were not given by us!",
+                            "Is this nefarious activity?");
+    } elsif ($num_rows > 1) {
+        $RT::Logger->error( "AUTH FAILED", 
+                            $cookie_name,
+                            "Cookie's value is duplicated in the database! This should not happen!!");
+    } else {
+        $user = $query_result_arrayref->[0][0];
+        $confirmed_by_cookie = 1;
+    }
+
+    if ($confirmed_by_cookie == 1) {
+        $RT::Logger->debug( "User (",
+                            $user,
+                            ") was authenticated by a browser cookie");
+    } else {
+        $RT::Logger->debug( "No user was authenticated by browser cookie");
+    }
+
+    return ($user,$confirmed_by_cookie);
+}
+
+# }}}
+
+1;



More information about the Bps-public-commit mailing list