diff options
-rw-r--r-- | Bugzilla/Auth/Login/WWW.pm | 10 | ||||
-rw-r--r-- | Bugzilla/Auth/Login/WWW/CGI.pm | 3 | ||||
-rw-r--r-- | Bugzilla/Auth/Login/WWW/Env.pm | 182 | ||||
-rw-r--r-- | Bugzilla/User.pm | 29 | ||||
-rwxr-xr-x | checksetup.pl | 5 | ||||
-rw-r--r-- | defparams.pl | 39 | ||||
-rw-r--r-- | template/en/default/global/useful-links.html.tmpl | 8 | ||||
-rw-r--r-- | template/en/default/index.html.tmpl | 4 | ||||
-rw-r--r-- | template/en/default/sidebar.xul.tmpl | 2 |
9 files changed, 276 insertions, 6 deletions
diff --git a/Bugzilla/Auth/Login/WWW.pm b/Bugzilla/Auth/Login/WWW.pm index def68df63..8b7724bef 100644 --- a/Bugzilla/Auth/Login/WWW.pm +++ b/Bugzilla/Auth/Login/WWW.pm @@ -36,6 +36,12 @@ sub login_class { return $current_login_class; } +# can_logout determines if a user may log out +sub can_logout { + return 1 if (login_class && login_class->can_logout); + return 0; +} + sub login { my ($class, $type) = @_; @@ -64,6 +70,8 @@ sub login { if ($userid) { $user = new Bugzilla::User($userid); + $user->set_flags('can_logout' => $class->can_logout); + # Compat stuff $::userid = $userid; } else { @@ -74,7 +82,7 @@ sub login { sub logout { my ($class, $user, $option) = @_; - if ($class->login_class) { + if (can_logout) { $class->login_class->logout($user, $option); } } diff --git a/Bugzilla/Auth/Login/WWW/CGI.pm b/Bugzilla/Auth/Login/WWW/CGI.pm index fb00cd018..3b90ec6ad 100644 --- a/Bugzilla/Auth/Login/WWW/CGI.pm +++ b/Bugzilla/Auth/Login/WWW/CGI.pm @@ -186,6 +186,9 @@ sub login { type => $type, }); } +# This auth style allows the user to log out. +sub can_logout { return 1; } + # Logs user out, according to the option provided; this consists of # removing entries from logincookies for the specified $user. sub logout { diff --git a/Bugzilla/Auth/Login/WWW/Env.pm b/Bugzilla/Auth/Login/WWW/Env.pm new file mode 100644 index 000000000..abd176315 --- /dev/null +++ b/Bugzilla/Auth/Login/WWW/Env.pm @@ -0,0 +1,182 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# The Initial Developer of the Original Code is Netscape Communications +# Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): Erik Stambaugh <erik@dasbistro.com> + +package Bugzilla::Auth::Login::WWW::Env; + +use strict; + +use Bugzilla::Config; +use Bugzilla::Error; +use Bugzilla::Util; + +sub login { + my ($class, $type) = @_; + + # XXX This does not currently work correctly with Param('requirelogin'). + # Bug 253636 will hopefully see that param's needs taken care of in a + # parent module, but for the time being, this module does not honor + # the param in the way that CGI.pm does. + + my $matched_userid = ''; + my $matched_extern_id = ''; + my $disabledtext = ''; + + my $dbh = Bugzilla->dbh; + my $sth; + + # Gather the environment variables + my $env_id = $ENV{Param("auth_env_id")}; + my $env_email = $ENV{Param("auth_env_email")}; + my $env_realname = $ENV{Param("auth_env_realname")}; + + # allow undefined values to work with trick_taint + for ($env_id, $env_email, $env_realname) { $_ ||= '' }; + # make sure the email field contains only a valid email address + my $emailregexp = Param("emailregexp"); + $env_email =~ /($emailregexp)/; + $env_email = $1; + # untaint the remaining values + trick_taint($env_id); + trick_taint($env_realname); + + if ($env_id | $env_email) { + # Look in the DB for the extern_id + if ($env_id) { + + # Not having the email address defined but having an ID isn't + # allowed. + return undef unless $env_email; + + $sth = $dbh->prepare("SELECT userid, disabledtext " . + "FROM profiles WHERE extern_id=?"); + $sth->execute($env_id); + my $fetched = $sth->fetch; + if ($fetched) { + $matched_userid = $fetched->[0]; + $disabledtext = $fetched->[1]; + } + } + + unless ($matched_userid) { + # There was either no match for the external ID given, or one was + # not present. + # + # Check to see if the email address is in there and has no + # external id assigned. We test for both the login name (which we + # also sent), and the id, so that we have a way of telling that we + # got something instead of a bunch of NULLs + $sth = $dbh->prepare("SELECT extern_id, userid, disabledtext " . + "FROM profiles WHERE login_name=?"); + $sth->execute($env_email); + + $sth->execute(); + my $fetched = $sth->fetch(); + if ($fetched) { + ($matched_extern_id, $matched_userid, $disabledtext) = @{$fetched}; + } + if ($matched_userid) { + if ($matched_extern_id) { + # someone with a different external ID has that address! + ThrowUserError("extern_id_conflict"); + } + else + { + # someone with no external ID used that address, time to + # add the ID! + $sth = $dbh->prepare("UPDATE profiles " . + "SET extern_id=? WHERE userid=?"); + $sth->execute($env_id, $matched_userid); + } + } + else + { + # Need to create a new user with that email address. Note + # that cryptpassword has been filled in with '*', since the + # user has no DB password. + $sth = $dbh->prepare("INSERT INTO profiles ( " . + "login_name, cryptpassword, " . + "realname, disabledtext " . + ") VALUES ( ?, ?, ?, '' )"); + $sth->execute($env_email, '*', $env_realname); + $sth = $dbh->prepare("SELECT last_insert_id()"); + $sth->execute(); + $matched_userid = $sth->fetch->[0]; + } + } + } + + # now that we hopefully have a username, we need to see if the data + # has to be updated + if ($matched_userid) { + $sth = $dbh->prepare("SELECT login_name, realname " . + "FROM profiles " . + "WHERE userid=?"); + $sth->execute($matched_userid); + my $fetched = $sth->fetch; + my $username = $fetched->[0]; + my $this_realname = $fetched->[1]; + if ( ($username ne $env_email) || + ($this_realname ne $env_realname) ) { + + $sth = $dbh->prepare("UPDATE profiles " . + "SET login_name=?, " . + "realname=? " . + "WHERE userid=?"); + $sth->execute($env_email, + ($env_realname || $this_realname), + $matched_userid); + $sth->execute; + } + } + + # Now we throw an error if the user has been disabled + if ($disabledtext) { + ThrowUserError("account_disabled", + {'disabled_reason' => $disabledtext}); + } + + return $matched_userid; + +} + +# This auth style does not allow the user to log out. +sub can_logout { return 0; } + +1; + +__END__ + +=head1 NAME + +Bugzilla::Auth::Env - Environment Variable Authentication + +=head1 DESCRIPTION + +Many external user authentication systems supply login information to CGI +programs via environment variables. This module checks to see if those +variables are populated and, if so, assumes authentication was successful and +returns the user's ID, having automatically created a new profile if +necessary. + +=head1 SEE ALSO + +L<Bugzilla::Auth> + diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index a40450076..38453841b 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -79,6 +79,7 @@ sub _create { 'name' => '', 'login' => '', 'showmybugslink' => 0, + 'flags' => {}, }; bless ($self, $class); return $self unless $cond && $val; @@ -139,6 +140,19 @@ sub email { $_[0]->{login}; } sub name { $_[0]->{name}; } sub showmybugslink { $_[0]->{showmybugslink}; } +sub set_flags { + my $self = shift; + while (my $key = shift) { + $self->{'flags'}->{$key} = shift; + } +} + +sub get_flag { + my $self = shift; + my $key = shift; + return $self->{'flags'}->{$key}; +} + # Generate a string to identify the user by name + email if the user # has a name or by email only if she doesn't. sub identity { @@ -1109,6 +1123,21 @@ all MySQL supported, this will go away. Returns C<1> if the user can bless at least one group. Otherwise returns C<0>. +=item C<set_flags> +=item C<get_flag> + +User flags are template-accessible user status information, stored in the form +of a hash. For an example of use, when the current user is authenticated in +such a way that they are allowed to log out, the 'can_logout' flag is set to +true (1). The template then checks this flag before displaying the "Log Out" +link. + +C<set_flags> is called with any number of key,value pairs. Flags for each key +will be set to the specified value. + +C<get_flag> is called with a single key name, which returns the associated +value. + =back =head1 SEE ALSO diff --git a/checksetup.pl b/checksetup.pl index c42c23535..703358cec 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -1810,6 +1810,7 @@ $table{profiles} = mybugslink tinyint not null default 1, emailflags mediumtext, refreshed_when datetime not null, + extern_id varchar(64) default null, unique(login_name)'; @@ -3999,6 +4000,10 @@ if (GetFieldDef("group_group_map", "isbless")) { ADD UNIQUE (member_id, grantor_id, grant_type)"); } +# Allow profiles to optionally be linked to a unique identifier in an outside +# login data source +AddField("profiles", "extern_id", "varchar(64)"); + # If you had to change the --TABLE-- definition in any way, then add your # differential change code *** A B O V E *** this comment. # diff --git a/defparams.pl b/defparams.pl index 86cc7f2d5..8260be978 100644 --- a/defparams.pl +++ b/defparams.pl @@ -435,6 +435,36 @@ sub find_languages { default => '', }, + { + name => 'auth_env_id', + desc => 'Environment variable used by external authentication system ' . + 'to store a unique identifier for each user. Leave it blank ' . + 'if there isn\'t one or if this method of authentication ' . + 'is not being used.', + type => 't', + default => '', + }, + + { + name => 'auth_env_email', + desc => 'Environment variable used by external authentication system ' . + 'to store each user\'s email address. This is a required ' . + 'field for environmental authentication. Leave it blank ' . + 'if you are not going to use this feature.', + type => 't', + default => '', + }, + + { + name => 'auth_env_realname', + desc => 'Environment variable used by external authentication system ' . + 'to store the user\'s real name. Leave it blank if there ' . + 'isn\'t one or if this method of authentication is not being ' . + 'used.', + type => 't', + default => '', + }, + # XXX in the future: # # user_verify_class and user_info_class should have choices gathered from @@ -455,9 +485,14 @@ sub find_languages { <dd> Asks for username and password via CGI form interface. </dd> - </dl>', + <dt>Env</dt> + <dd> + Info for a pre-authenticated user is passed in system + environment variables. + </dd> + </dl>', type => 's', - choices => [ 'CGI' ], + choices => [ 'CGI', 'Env', 'Env,CGI' ], default => 'CGI', checker => \&check_multi }, diff --git a/template/en/default/global/useful-links.html.tmpl b/template/en/default/global/useful-links.html.tmpl index c04b60dd6..7e568372c 100644 --- a/template/en/default/global/useful-links.html.tmpl +++ b/template/en/default/global/useful-links.html.tmpl @@ -52,8 +52,12 @@ [% IF user.login %] [% ' | <a href="sanitycheck.cgi">Sanity check</a>' IF user.groups.tweakparams %] - | <a href="relogin.cgi">Log out</a> - [% user.login FILTER html %] + [% IF user.get_flag('can_logout') %] + | <a href="relogin.cgi">Log out</a> + [% ELSE %] + | Logged in as + [% END %] + [% user.login FILTER html %] [% ELSE %] [% IF Param('createemailregexp') %] | <a href="createaccount.cgi">New Account</a> diff --git a/template/en/default/index.html.tmpl b/template/en/default/index.html.tmpl index 5422061fd..d31a51d89 100644 --- a/template/en/default/index.html.tmpl +++ b/template/en/default/index.html.tmpl @@ -66,7 +66,9 @@ function addSidebar() { </p><p> [% IF user.id %] <a href="userprefs.cgi">Change password or user preferences</a><br> - <a href="relogin.cgi">Logout [% user.login FILTER html %]</a><br> + [% IF user.get_flag('can_logout') %] + <a href="relogin.cgi">Logout [% user.login FILTER html %]</a><br> + [% END %] [% ELSE %] <a href="query.cgi?GoAheadAndLogIn=1">Log in to an existing account</a><br> [% IF Param('createemailregexp') %] diff --git a/template/en/default/sidebar.xul.tmpl b/template/en/default/sidebar.xul.tmpl index 60ae33dbd..802fd9f23 100644 --- a/template/en/default/sidebar.xul.tmpl +++ b/template/en/default/sidebar.xul.tmpl @@ -95,7 +95,9 @@ function normal_keypress_handler( aEvent ) { [%- IF user.groups.tweakparams %] <text class="text-link" onclick="load_relative_url('sanitycheck.cgi')" value="sanity check"/> [%- END %] + [%- IF user.get_flag('can_logout') %] <text class="text-link" onclick="load_relative_url('relogin.cgi')" value="logout [% user.login FILTER html %]"/> + [%- END %] <separator class="thin"/> [%- IF user.showmybugslink %] [% filtered_username = user.login FILTER url_quote %] |