add LDAP authentication support
This commit is contained in:
@@ -1,4 +1,8 @@
|
|||||||
2.012007 - 2013-08-23
|
2.013000 - 2013-08-23
|
||||||
|
|
||||||
|
[NEW FEATURES]
|
||||||
|
|
||||||
|
* LDAP authentication support - see Configuration POD for details
|
||||||
|
|
||||||
[ENHANCEMENTS]
|
[ENHANCEMENTS]
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,72 @@ to Netdisco in the C<X-REMOTE_USER> HTTP Header. For example with Apache:
|
|||||||
RequestHeader unset X-REMOTE_USER
|
RequestHeader unset X-REMOTE_USER
|
||||||
RequestHeader set X-REMOTE_USER "%{REMOTE_USER}e" env=REMOTE_USER
|
RequestHeader set X-REMOTE_USER "%{REMOTE_USER}e" env=REMOTE_USER
|
||||||
|
|
||||||
|
=head3 C<ldap>
|
||||||
|
|
||||||
|
Value: Settings Tree. Default: None.
|
||||||
|
|
||||||
|
If set, and a user has the C<ldap> flag also set on their account, then LDAP
|
||||||
|
authentication will be used for their login. You I<must> install the
|
||||||
|
L<Net::LDAP> Perl module in order to use this feature. For example:
|
||||||
|
|
||||||
|
ldap:
|
||||||
|
servers:
|
||||||
|
- 'ad.example.com'
|
||||||
|
user_string: 'MYDOMAIN\%USER%'
|
||||||
|
opts:
|
||||||
|
debug: 3
|
||||||
|
|
||||||
|
There are several options within this setting:
|
||||||
|
|
||||||
|
=head4 C<servers>
|
||||||
|
|
||||||
|
This must be a list of one or more LDAP servers. If using Active Directory
|
||||||
|
these would be your Domain Controllers.
|
||||||
|
|
||||||
|
=head4 C<user_string>
|
||||||
|
|
||||||
|
String to construct the user portion of the DN. C<%USER%> is a variable which
|
||||||
|
will be replaced at runtime with the logon name entered on the logon page of
|
||||||
|
the application.
|
||||||
|
|
||||||
|
Active Directory users may simply use C<MYDOMAIN\%USER%> and skip all other
|
||||||
|
options except C<servers>, as this notation eliminates the need to construct
|
||||||
|
the full distinguished name.
|
||||||
|
|
||||||
|
Examples: C<cn=%USER%> or C<uid=%USER%>.
|
||||||
|
|
||||||
|
=head4 C<base>
|
||||||
|
|
||||||
|
Indicates where in the hierarchy to begin searches. If a proxy user is not
|
||||||
|
defined and anonymous binds are not enabled this value will be appended to the
|
||||||
|
C<user_string> to construct the distinguished name for authentication.
|
||||||
|
|
||||||
|
=head4 C<proxy_user>
|
||||||
|
|
||||||
|
User to bind with to perform searches. If defined as C<anonymous>, then
|
||||||
|
anonymous binds will be performed and C<proxy_pass> will be ignored. For
|
||||||
|
organizations with users in multiple OUs this option can be used to search for
|
||||||
|
the user and construct the DN based upon the result.
|
||||||
|
|
||||||
|
=head4 C<proxy_pass>
|
||||||
|
|
||||||
|
Proxy user password. Ignored if proxy user defined as anonymous.
|
||||||
|
|
||||||
|
=head4 C<opts>
|
||||||
|
|
||||||
|
Hash of options to add to the connect string. Normally only needed if server
|
||||||
|
does not support LDAPv3, or to enable debugging as in the example above.
|
||||||
|
|
||||||
|
=head4 C<tls_opts>
|
||||||
|
|
||||||
|
A hash which, when defined, causes the connection tol use Transport Layer
|
||||||
|
Security (TLS) which provides an encrypted connection. TLS is the preferred
|
||||||
|
method of encryption, ldaps (port 636) is not supported.
|
||||||
|
|
||||||
|
This is only possible if using LDAPv3 and the server supports it. These are
|
||||||
|
the options for the TLS connection. See the L<Net::LDAP> documentation under
|
||||||
|
start_tls for options, but the defaults should work in most cases.
|
||||||
|
|
||||||
=head3 C<path>
|
=head3 C<path>
|
||||||
|
|
||||||
Value: String. Default: None.
|
Value: String. Default: None.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use base 'Dancer::Plugin::Auth::Extensible::Provider::Base';
|
|||||||
# with thanks to yanick's patch at
|
# with thanks to yanick's patch at
|
||||||
# https://github.com/bigpresh/Dancer-Plugin-Auth-Extensible/pull/24
|
# https://github.com/bigpresh/Dancer-Plugin-Auth-Extensible/pull/24
|
||||||
|
|
||||||
use Dancer qw(:syntax);
|
use Dancer ':syntax';
|
||||||
use Dancer::Plugin::DBIC;
|
use Dancer::Plugin::DBIC;
|
||||||
|
|
||||||
use Digest::MD5;
|
use Digest::MD5;
|
||||||
@@ -21,18 +21,6 @@ sub authenticate_user {
|
|||||||
return $self->match_password($password, $user);
|
return $self->match_password($password, $user);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub match_password {
|
|
||||||
my( $self, $password, $user ) = @_;
|
|
||||||
return unless $user and $password and $user->password;
|
|
||||||
|
|
||||||
my $settings = $self->realm_settings;
|
|
||||||
my $password_column = $settings->{users_password_column} || 'password';
|
|
||||||
|
|
||||||
my $sum = Digest::MD5::md5_hex($password);
|
|
||||||
return ($sum eq $user->$password_column ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub get_user_details {
|
sub get_user_details {
|
||||||
my ($self, $username) = @_;
|
my ($self, $username) = @_;
|
||||||
|
|
||||||
@@ -69,4 +57,113 @@ sub get_user_roles {
|
|||||||
return [ $user->$roles->get_column( $role_column )->all ];
|
return [ $user->$roles->get_column( $role_column )->all ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub match_password {
|
||||||
|
my($self, $password, $user) = @_;
|
||||||
|
return unless $user;
|
||||||
|
|
||||||
|
my $settings = $self->realm_settings;
|
||||||
|
my $username_column = $settings->{users_username_column} || 'username';
|
||||||
|
|
||||||
|
return $user->ldap
|
||||||
|
? $self->match_with_ldap($password, $user->$username_column)
|
||||||
|
: $self->match_with_local_pass($password, $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub match_with_local_pass {
|
||||||
|
my($self, $password, $user) = @_;
|
||||||
|
|
||||||
|
my $settings = $self->realm_settings;
|
||||||
|
my $password_column = $settings->{users_password_column} || 'password';
|
||||||
|
|
||||||
|
return unless $password and $user->password_column;
|
||||||
|
|
||||||
|
my $sum = Digest::MD5::md5_hex($password);
|
||||||
|
return ($sum eq $user->$password_column ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub match_with_ldap {
|
||||||
|
my($self, $pass, $user) = @_;
|
||||||
|
|
||||||
|
eval 'require Net::LDAP';
|
||||||
|
if ($@) {error $@; return}
|
||||||
|
|
||||||
|
return unless setting('ldap') and ref {} eq ref setting('ldap');
|
||||||
|
my $conf = setting('ldap');
|
||||||
|
|
||||||
|
my $ldapuser = $conf->{user_string};
|
||||||
|
$ldapuser =~ s/\%USER\%?/$user/egi;
|
||||||
|
|
||||||
|
# If we can bind as anonymous or proxy user,
|
||||||
|
# search for user's distinguished name
|
||||||
|
if ($conf->{proxy_user}) {
|
||||||
|
my $user = $conf->{proxy_user};
|
||||||
|
my $pass = $conf->{proxy_pass};
|
||||||
|
my $attrs = ['distinguishedName'];
|
||||||
|
my $result = _ldap_search($ldapuser, $attrs, $user, $pass);
|
||||||
|
$ldapuser = $result->[0] if ($result->[0]);
|
||||||
|
}
|
||||||
|
# otherwise, if we can't search and aren't using AD and then construct DN by
|
||||||
|
# appending base
|
||||||
|
elsif ($ldapuser =~ m/=/) {
|
||||||
|
$ldapuser = "$ldapuser,$conf->{base}";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $server (@{$conf->{servers}}) {
|
||||||
|
my $opts = $conf->{opts} || {};
|
||||||
|
my $ldap = Net::LDAP->new($server, %$opts) or next;
|
||||||
|
my $msg = undef;
|
||||||
|
|
||||||
|
if ($conf->{tls_opts} ) {
|
||||||
|
$msg = $ldap->start_tls(%{$conf->{tls_opts}});
|
||||||
|
}
|
||||||
|
|
||||||
|
$msg = $ldap->bind($ldapuser, password => $pass);
|
||||||
|
$ldap->unbind(); # take down session
|
||||||
|
|
||||||
|
return 1 unless $msg->code();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _ldap_search {
|
||||||
|
my ($filter, $attrs, $user, $pass) = @_;
|
||||||
|
|
||||||
|
return undef unless defined($filter);
|
||||||
|
return undef if (defined $attrs and ref [] ne ref $attrs);
|
||||||
|
|
||||||
|
return unless setting('ldap') and ref {} eq ref setting('ldap');
|
||||||
|
my $conf = setting('ldap');
|
||||||
|
|
||||||
|
foreach my $server (@{$conf->{server}}) {
|
||||||
|
my $opts = $conf->{opts} || {};
|
||||||
|
my $ldap = Net::LDAP->new($server, %$opts) or next;
|
||||||
|
my $msg = undef;
|
||||||
|
|
||||||
|
if ($conf->{tls_opts}) {
|
||||||
|
$msg = $ldap->start_tls(%{$conf->{tls_opts}});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $user and $user ne 'anonymous' ) {
|
||||||
|
$msg = $ldap->bind($user, password => $pass);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$msg = $ldap->bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
$msg = $ldap->search(
|
||||||
|
base => $conf->{base},
|
||||||
|
filter => "($filter)",
|
||||||
|
attrs => $attrs,
|
||||||
|
);
|
||||||
|
|
||||||
|
$ldap->unbind(); # take down session
|
||||||
|
|
||||||
|
my $entries = [$msg->entries];
|
||||||
|
return $entries unless $msg->code();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ ajax '/ajax/control/admin/users/add' => require_role admin => sub {
|
|||||||
username => param('username'),
|
username => param('username'),
|
||||||
password => Digest::MD5::md5_hex(param('password')),
|
password => Digest::MD5::md5_hex(param('password')),
|
||||||
fullname => param('fullname'),
|
fullname => param('fullname'),
|
||||||
|
ldap => (param('ldap') ? \'true' : \'false'),
|
||||||
port_control => (param('port_control') ? \'true' : \'false'),
|
port_control => (param('port_control') ? \'true' : \'false'),
|
||||||
admin => (param('admin') ? \'true' : \'false'),
|
admin => (param('admin') ? \'true' : \'false'),
|
||||||
});
|
});
|
||||||
@@ -58,6 +59,7 @@ ajax '/ajax/control/admin/users/update' => require_role admin => sub {
|
|||||||
? (password => Digest::MD5::md5_hex(param('password')))
|
? (password => Digest::MD5::md5_hex(param('password')))
|
||||||
: ()),
|
: ()),
|
||||||
fullname => param('fullname'),
|
fullname => param('fullname'),
|
||||||
|
ldap => (param('ldap') ? \'true' : \'false'),
|
||||||
port_control => (param('port_control') ? \'true' : \'false'),
|
port_control => (param('port_control') ? \'true' : \'false'),
|
||||||
admin => (param('admin') ? \'true' : \'false'),
|
admin => (param('admin') ? \'true' : \'false'),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<th class="nd_center-cell">Full Name</th>
|
<th class="nd_center-cell">Full Name</th>
|
||||||
<th class="nd_center-cell">Username</th>
|
<th class="nd_center-cell">Username</th>
|
||||||
<th class="nd_center-cell">Password</th>
|
<th class="nd_center-cell">Password</th>
|
||||||
<!-- <th class="nd_center-cell">External Auth</th> -->
|
<th class="nd_center-cell">LDAP Auth</th>
|
||||||
<th class="nd_center-cell">Port Control</th>
|
<th class="nd_center-cell">Port Control</th>
|
||||||
<th class="nd_center-cell">Administrator</th>
|
<th class="nd_center-cell">Administrator</th>
|
||||||
<th class="nd_center-cell">Action</th>
|
<th class="nd_center-cell">Action</th>
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<td class="nd_center-cell"><input data-form="add" name="fullname" type="text"></td>
|
<td class="nd_center-cell"><input data-form="add" name="fullname" type="text"></td>
|
||||||
<td class="nd_center-cell"><input data-form="add" name="username" type="text"></td>
|
<td class="nd_center-cell"><input data-form="add" name="username" type="text"></td>
|
||||||
<td class="nd_center-cell"><input data-form="add" name="password" type="text"></td>
|
<td class="nd_center-cell"><input data-form="add" name="password" type="text"></td>
|
||||||
|
<td class="nd_center-cell"><input data-form="add" type="checkbox" name="ldap"></td>
|
||||||
<td class="nd_center-cell"><input data-form="add" type="checkbox" name="port_control"></td>
|
<td class="nd_center-cell"><input data-form="add" type="checkbox" name="port_control"></td>
|
||||||
<td class="nd_center-cell"><input data-form="add" type="checkbox" name="admin"></td>
|
<td class="nd_center-cell"><input data-form="add" type="checkbox" name="admin"></td>
|
||||||
<td class="nd_center-cell">
|
<td class="nd_center-cell">
|
||||||
@@ -33,6 +34,9 @@
|
|||||||
<td class="nd_center-cell">
|
<td class="nd_center-cell">
|
||||||
<input data-form="update" name="password" type="text" value="********">
|
<input data-form="update" name="password" type="text" value="********">
|
||||||
</td>
|
</td>
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<input data-form="update" name="ldap" type="checkbox" [% 'checked="checked"' IF row.ldap %]>
|
||||||
|
</td>
|
||||||
<td class="nd_center-cell">
|
<td class="nd_center-cell">
|
||||||
<input data-form="update" name="port_control" type="checkbox" [% 'checked="checked"' IF row.port_control %]>
|
<input data-form="update" name="port_control" type="checkbox" [% 'checked="checked"' IF row.port_control %]>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user