Skip to content

Commit

Permalink
(empty commit message)
Browse files Browse the repository at this point in the history
git-svn-id: svn+ssh://svn.bestpractical.com/svn/jifty.org/jifty/trunk@5851 e84bef0a-9b06-0410-84ba-c4c9edb13aeb
  • Loading branch information
sartak committed Sep 16, 2008
1 parent 8ccc9d7 commit dc2fc11
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 0 deletions.
17 changes: 17 additions & 0 deletions Makefile.PL
@@ -0,0 +1,17 @@
use inc::Module::Install;

name 'Jifty-Plugin-AuthzLDAP';
all_from 'lib/Jifty/Plugin/AuthzLDAP.pm';

version_from('../../lib/Jifty.pm');

build_requires 'Test::More';
requires('Net::LDAP');
requires('Cache::MemoryCache');

install_share;

tests('t/*.t');
auto_install;
WriteAll;

212 changes: 212 additions & 0 deletions lib/Jifty/Plugin/AuthzLDAP.pm
@@ -0,0 +1,212 @@
use strict;
use warnings;

=head1 NAME
Jifty::Plugin::AuthzLDAP - Jifty plugin to a add dynamic ldap authorization
=head1 DESCRIPTION
Jifty plugin.
Provide ldap authorization with filters table and cache.
=head1 CONFIGURATION NOTES
in etc/config.yml
Plugins:
- AuthzLDAP:
LDAPbind: cn=testldap,ou=admins,dc=myorg,dc=org #
LDAPpass: test # password
LDAPhost: ldap.myorg.org # ldap host
LDAPbase: ou=people,dc=myorg.. # ldap base
LDAPuid: uid # optional
CacheTimout: 20 # minutes, optional, default 20 minutes
in application create a LDAPFilter model
use base qw/Jifty::Plugin::AuthzLDAP::Model::LDAPFilter/;
in LDAPFilter model create your filters, something like
name |filter |is_group
is_admin|(!eduPersonAffiliation=STUDENT)|0
in_admin|cn=admin,ou=groups,dc=my.org |1
to protect access to /admin
in "TestApp" application create a lib/TestApp/Dispatcher.pm
use strict;
use warnings;
package TestApp::Dispatcher;
use Jifty::Dispatcher -base;
before '/admin/*' => run {
# Authentication
Jifty->web->tangent(url => '/login')
if (! Jifty->web->current_user->id);
# Authorization
my $user = Jifty->web->current_user->user_object->name;
Jifty->web->tangent(url => '/error/AccessDenied')
if (! Jifty::Plugin::AuthzLDAP->ldapvalidate($user,'is_admin') );
};
1
=head1 SEE ALSO
L<Net::LDAP>
=cut


package Jifty::Plugin::AuthzLDAP;
use base qw/Jifty::Plugin/;

our $VERSION = '0.02';

use Net::LDAP;
use Cache::MemoryCache;

{
my ($LDAPFilterClass, $LDAP, $cache, %params);

=head1 METHODS
=head2 init
load config parameters, connect to ldap, create memory cache
=cut

sub init {
my $self = shift;
my %args = @_;

my $appname = Jifty->config->framework('ApplicationName');
$LDAPFilterClass = "Jifty::Plugin::AuthzLDAP::Model::LDAPFilter";

$params{'Hostname'} = $args{LDAPhost};
$params{'base'} = $args{LDAPbase};
$params{'uid'} = $args{LDAPuid} || "uid";
$params{'dn'} = $args{LDAPbind};
$params{'pass'} = $args{LDAPpass};
$params{'timeout'} = $args{CacheTimout} || "20 minutes";

$LDAP = Net::LDAP->new($params{Hostname},async=>1,onerror => 'undef', debug => 0);

$cache = new Cache::MemoryCache( { 'namespace' => $appname.'AuthzLDAP',
'default_expires_in' => $params{'timeout'} } );
}

=head2 BASE CACHE DN LDAP BASE UID PASS LDAPFilterClass
accesors to conf parametres
=cut

sub LDAPFilterClass {
return $LDAPFilterClass;
}

sub LDAP {
return $LDAP;
}

sub DN {
return $params{'dn'};
}

sub PASS {
return $params{'pass'};
}

sub UID {
return $params{'uid'};
}

sub BASE {
return $params{'base'};
}

sub CACHE {
return $cache;
}

}

=head2 bind
Bind to ldap
=cut

sub bind {
my $self = shift;
my $msg = $self->LDAP()->bind($self->DN() ,'password' =>$self->PASS());
unless (not $msg->code) {
Jifty->log->error("Bind to ldap server failed");
return;
}
}

=head2 ldapvalidate NAME FILTERNAME
return 1 if NAME validate FILTER or NAME-FILTERNAME in cache
else return 0
If FILTERNAME is flagged as is_group, search if user is uniquemember of this group
as supported by the Netscape Directory Server
=cut

sub ldapvalidate {
my ($self, $user, $filtername) = @_;
my $response = 'nok';

my $cachekey = $user.'-'.$filtername;
my $cache = $self->CACHE->get($cachekey);
return ($cache eq 'ok')?1:0 if (defined $cache);

my $record = $self->LDAPFilterClass()->new();
$record->load_by_cols( name => $filtername);

# (?) allow use of writing filter in filtername
# TODO: filtername must be cleanned
# my $filter = ($record->filter)?$record->filter:$filtername;
my $filter = $record->ldapfilter;

$user = $self->UID().'='.$user.','.$self->BASE();

# (??) how to catch AuthLDAP bind if it's used?
$self->bind();

my $msg;
# manage group as supported by the Netscape Directory Server
if ($record->is_group) {
$msg = $self->LDAP()->compare( $filter, attr=>"uniquemember", value=>$user );
Jifty->log->debug("search grp: ".$msg->code);
$response = 'ok' if ( $msg->code == 6 );
} else {
$filter = '('. $filter .')' if ( $filter !~ /^\(/ );
$msg = $self->LDAP()->search( base => $user, filter => $filter );
Jifty->log->debug("search: ".$msg->count);
$response = 'ok' if (! $msg->code && $msg->count );
}

$self->CACHE->set($cachekey,$response);

return ( $response eq 'ok' )?1:0;
}

=head1 AUTHOR
Yves Agostini, <yvesago@cpan.org>
=head1 LICENSE
Copyright 2007-2008 Yves Agostini. All Rights Reserved.
This program is free software and may be modified and distributed under the same terms as Perl itself.
=cut

1;
81 changes: 81 additions & 0 deletions lib/Jifty/Plugin/AuthzLDAP/Action/LDAPValidate.pm
@@ -0,0 +1,81 @@
use warnings;
use strict;

=head1 NAME
Jifty::Plugin::AuthzLDAP::Action::LDAPValidate - action to validate filter
=cut

package Jifty::Plugin::AuthzLDAP::Action::LDAPValidate;
use base qw/Jifty::Action Jifty::Plugin::AuthzLDAP/;

our $VERSION = '0.01';

=head2 arguments
Return the ticket form field
=cut

sub arguments {
return (
{
name => {
mandatory => 1
},

filter => {
mandatory => 1
},

}
);

}

=head2 take_action
Bind on ldap to check the user's password. If it's right, log them in.
Otherwise, throw an error.
=cut

sub take_action {
my $self = shift;
my $user = $self->argument_value('name');
my $filter = $self->argument_value('filter');

Jifty->log->debug("action: $user $filter");

# Bind on ldap
#my $msg = $self->bind();

my $msg = $self->ldapvalidate($user,$filter);

Jifty->log->debug("validate: $msg");

if (not $msg) {
$self->result->error(
_('Access denied.') );
return;}

return 1;
}

=head1 AUTHOR
Yves Agostini, <yvesago@cpan.org>
=head1 LICENSE
Copyright 2007-2008 Yves Agostini. All Rights Reserved.
This program is free software and may be modified and distributed under the same terms as Perl itself.
=cut

1;


85 changes: 85 additions & 0 deletions lib/Jifty/Plugin/AuthzLDAP/Model/LDAPFilter.pm
@@ -0,0 +1,85 @@
package Jifty::Plugin::AuthzLDAP::Model::LDAPFilter;
use Jifty::DBI::Schema;
use Scalar::Defer;

our $VERSION = '0.02';

=head1 NAME
Jifty::Plugin::AuthzLDAP::Model::LDAPFilter - model for filters
=cut

use Jifty::Record schema {
column
name => type is 'text',
label is 'Name',
is mandatory,
is distinct;

column
ldapfilter => type is 'text',
label is 'Filter',
is mandatory;

column
is_group => type is 'boolean',
label is 'Group';

column 'created_on' =>
type is 'datetime',
is immutable,
default is defer { DateTime->now },
filters are 'Jifty::DBI::Filter::DateTime';
};

=head2 create
=cut

sub create {
my $self = shift;
my %args = (@_);
my (@ret) = $self->SUPER::create(%args);

return (@ret);
}


=head2 current_user_can ACTION
Only superuser can create or edit filters.
Logged-in users can read.
=cut

sub current_user_can {
my $self = shift;
my $type = shift;

if ($type eq 'create' || $type eq 'update') {
return 0 if
!$self->current_user->is_superuser;
return 1;
} elsif($type eq 'read') {
return 1 if
$self->current_user->id || $self->current_user->is_superuser;
return 0;
}

return $self->SUPER::current_user_can($type, @_);
}

=head1 AUTHOR
Yves Agostini, <yvesago@cpan.org>
=head1 LICENSE
Copyright 2007-2008 Yves Agostini. All Rights Reserved.
This program is free software and may be modified and distributed under the same terms as Perl itself.
=cut

1;

0 comments on commit dc2fc11

Please sign in to comment.