Bug 241903: Add Environment Variable Authentication for apache auth and SSO
patch by erik r=joel a=justdave git-svn-id: svn://10.0.0.236/trunk@160665 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
182
mozilla/webtools/bugzilla/Bugzilla/Auth/Login/WWW/Env.pm
Normal file
182
mozilla/webtools/bugzilla/Bugzilla/Auth/Login/WWW/Env.pm
Normal file
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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') %]
|
||||
|
||||
@@ -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 %]
|
||||
|
||||
Reference in New Issue
Block a user