200 lines
6.8 KiB
Perl
200 lines
6.8 KiB
Perl
# -*- 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): Terry Weissman <terry@mozilla.org>
|
|
# Dan Mosedale <dmose@mozilla.org>
|
|
# Joe Robins <jmrobins@tgix.com>
|
|
# Dave Miller <justdave@syndicomm.com>
|
|
# Christopher Aillon <christopher@aillon.com>
|
|
# Gervase Markham <gerv@gerv.net>
|
|
# Christian Reis <kiko@async.com.br>
|
|
# Bradley Baetz <bbaetz@acm.org>
|
|
# Erik Stambaugh <erik@dasbistro.com>
|
|
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
|
|
|
package Bugzilla::Auth::Verify::LDAP;
|
|
use strict;
|
|
use base qw(Bugzilla::Auth::Verify);
|
|
use fields qw(
|
|
ldap
|
|
);
|
|
|
|
use Bugzilla::Constants;
|
|
use Bugzilla::Error;
|
|
use Bugzilla::User;
|
|
use Bugzilla::Util;
|
|
|
|
use Net::LDAP;
|
|
|
|
use constant admin_can_create_account => 0;
|
|
use constant user_can_create_account => 0;
|
|
|
|
sub check_credentials {
|
|
my ($self, $params) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
|
|
# We need to bind anonymously to the LDAP server. This is
|
|
# because we need to get the Distinguished Name of the user trying
|
|
# to log in. Some servers (such as iPlanet) allow you to have unique
|
|
# uids spread out over a subtree of an area (such as "People"), so
|
|
# just appending the Base DN to the uid isn't sufficient to get the
|
|
# user's DN. For servers which don't work this way, there will still
|
|
# be no harm done.
|
|
$self->_bind_ldap_for_search();
|
|
|
|
# Now, we verify that the user exists, and get a LDAP Distinguished
|
|
# Name for the user.
|
|
my $username = $params->{username};
|
|
my $dn_result = $self->ldap->search(_bz_search_params($username),
|
|
attrs => ['dn']);
|
|
return { failure => AUTH_ERROR, error => "ldap_search_error",
|
|
details => {errstr => $dn_result->error, username => $username}
|
|
} if $dn_result->code;
|
|
|
|
return { failure => AUTH_NO_SUCH_USER } if !$dn_result->count;
|
|
|
|
my $dn = $dn_result->shift_entry->dn;
|
|
|
|
# Check the password.
|
|
my $pw_result = $self->ldap->bind($dn, password => $params->{password});
|
|
return { failure => AUTH_LOGINFAILED } if $pw_result->code;
|
|
|
|
# And now we fill in the user's details.
|
|
|
|
# First try the search as the (already bound) user in question.
|
|
my $user_entry;
|
|
my $error_string;
|
|
my $detail_result = $self->ldap->search(_bz_search_params($username));
|
|
if ($detail_result->code) {
|
|
# Stash away the original error, just in case
|
|
$error_string = $detail_result->error;
|
|
} else {
|
|
$user_entry = $detail_result->shift_entry;
|
|
}
|
|
|
|
# If that failed (either because the search failed, or returned no
|
|
# results) then try re-binding as the initial search user, but only
|
|
# if the LDAPbinddn parameter is set.
|
|
if (!$user_entry && Bugzilla->params->{"LDAPbinddn"}) {
|
|
$self->_bind_ldap_for_search();
|
|
|
|
$detail_result = $self->ldap->search(_bz_search_params($username));
|
|
if (!$detail_result->code) {
|
|
$user_entry = $detail_result->shift_entry;
|
|
}
|
|
}
|
|
|
|
# If we *still* don't have anything in $user_entry then give up.
|
|
return { failure => AUTH_ERROR, error => "ldap_search_error",
|
|
details => {errstr => $error_string, username => $username}
|
|
} if !$user_entry;
|
|
|
|
|
|
my $mail_attr = Bugzilla->params->{"LDAPmailattribute"};
|
|
if ($mail_attr) {
|
|
if (!$user_entry->exists($mail_attr)) {
|
|
return { failure => AUTH_ERROR,
|
|
error => "ldap_cannot_retreive_attr",
|
|
details => {attr => $mail_attr} };
|
|
}
|
|
|
|
my @emails = $user_entry->get_value($mail_attr);
|
|
|
|
# Default to the first email address returned.
|
|
$params->{bz_username} = $emails[0];
|
|
|
|
if (@emails > 1) {
|
|
# Cycle through the adresses and check if they're Bugzilla logins.
|
|
# Use the first one that returns a valid id.
|
|
foreach my $email (@emails) {
|
|
if ( login_to_id($email) ) {
|
|
$params->{bz_username} = $email;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
$params->{bz_username} = $username;
|
|
}
|
|
|
|
$params->{realname} ||= $user_entry->get_value("displayName");
|
|
$params->{realname} ||= $user_entry->get_value("cn");
|
|
|
|
$params->{extern_id} = $username;
|
|
|
|
return $params;
|
|
}
|
|
|
|
sub _bz_search_params {
|
|
my ($username) = @_;
|
|
return (base => Bugzilla->params->{"LDAPBaseDN"},
|
|
scope => "sub",
|
|
filter => '(&(' . Bugzilla->params->{"LDAPuidattribute"}
|
|
. "=$username)"
|
|
. Bugzilla->params->{"LDAPfilter"} . ')');
|
|
}
|
|
|
|
sub _bind_ldap_for_search {
|
|
my ($self) = @_;
|
|
my $bind_result;
|
|
if (Bugzilla->params->{"LDAPbinddn"}) {
|
|
my ($LDAPbinddn,$LDAPbindpass) =
|
|
split(":",Bugzilla->params->{"LDAPbinddn"});
|
|
$bind_result =
|
|
$self->ldap->bind($LDAPbinddn, password => $LDAPbindpass);
|
|
}
|
|
else {
|
|
$bind_result = $self->ldap->bind();
|
|
}
|
|
ThrowCodeError("ldap_bind_failed", {errstr => $bind_result->error})
|
|
if $bind_result->code;
|
|
}
|
|
|
|
# We can't just do this in new(), because we're not allowed to throw any
|
|
# error from anywhere under Bugzilla::Auth::new -- otherwise we
|
|
# could create a situation where the admin couldn't get to editparams
|
|
# to fix his mistake. (Because Bugzilla->login always calls
|
|
# Bugzilla::Auth->new, and almost every page calls Bugzilla->login.)
|
|
sub ldap {
|
|
my ($self) = @_;
|
|
return $self->{ldap} if $self->{ldap};
|
|
|
|
my @servers = split(/[\s,]+/, Bugzilla->params->{"LDAPserver"});
|
|
ThrowCodeError("ldap_server_not_defined") unless @servers;
|
|
|
|
foreach (@servers) {
|
|
$self->{ldap} = new Net::LDAP(trim($_));
|
|
last if $self->{ldap};
|
|
}
|
|
ThrowCodeError("ldap_connect_failed", { server => join(", ", @servers) })
|
|
unless $self->{ldap};
|
|
|
|
# try to start TLS if needed
|
|
if (Bugzilla->params->{"LDAPstarttls"}) {
|
|
my $mesg = $self->{ldap}->start_tls();
|
|
ThrowCodeError("ldap_start_tls_failed", { error => $mesg->error() })
|
|
if $mesg->code();
|
|
}
|
|
|
|
return $self->{ldap};
|
|
}
|
|
|
|
1;
|