
package OneUnified::isADGroupMember;

# Created 2007/03/08
# Copyright 2007 One Unified
# Updated 2007/03/27:  Added default return 0 for CheckMembership
# Updated 2007/03/30:  Fixed initialization of $base

require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(isMember);

use strict;
use Net::LDAP;
use OneUnified::Const;

# need to validate user is still active
# use 'apt-get install libnet-ldap-perl' for dependencies

#my $base = 'DC=example,DC=net';	# directory search base
#my $binduser = 'servicacct@example.net';	# service account userid for ad binding
#my $bindpwd  = 'password';     		# password for ad binding
#my $bindaddress = 'ad.example.net';  	# bind address go global catalog server

my ( $base, $bindaddress, $binduser, $bindpwd );

sub isMember($$) {

  my ( $user, $group ) = @_;

  ( $base, $bindaddress, $binduser, $bindpwd ) = getADParams();

  #
  # Connect and bind
  #



  my $ad = Net::LDAP->new($bindaddress)
           or die "Could not connect!";

  my $mesg = $ad->bind($binduser, password=>$bindpwd, version => 3 );
#  print( 'Bind Name: ' . $mesg->error_name() . "\n" );
#  print( 'Bind Message: ' . $mesg->error_text() . "" );
#  print( 'Bind Error: ' . $mesg->error() . "\n" );
#  print( 'Bind DN: ' . $mesg->dn() . "\n" );
#  print( 'Bind isError: ' . $mesg->is_error() . "\n" );

  #
  # perform lookup
  #

  my $return = CheckMembership( $ad, $user, $group );

  #
  # finish up and return
  #

  $mesg = $ad->unbind;

  return $return;
}

sub CheckMembership($$$) {

  my ( $ad, $targetuser, $targetgroup ) = @_;

  my ( $filter, $attrs );
  my ( $mesg, $count );

  my %examinedgroups; # groups that have been examined for contained group members
  my @newgroups;      # groups that need to be examined for contained group members

  # will use a loop to keep processing new groups until all groups have been examined
  # this provides a list of examined groups
  # the list of examined groups is evaluated to see if the user is a member of any of them

  my ( $userentry, $groupentry );
  my ( $userdn, $groupdn );

  # obtain distinguishedName for targetuser

  $attrs = ['distinguishedName','memberOf'];
  $filter = "&(samaccountname=$targetuser)(objectClass=user)";

  $mesg = $ad->search( base => $base, filter => $filter, attrs => $attrs );
  if ( 1 != $mesg->count ) {
    # didn't find a single user
    return 0;
  }

  $userentry = $mesg->entry( 0 );
  $userdn = $userentry->dn();

  # obtain distinguishedName targetgroup

  $filter = "&(samaccountname=$targetgroup)(objectClass=group)";
  $attrs = ['distinguishedName'];

  $mesg = $ad->search( base => $base, filter => $filter, attrs => $attrs );

  if ( 1 != $mesg->count ) {
    # didn't find a single group
    return 0;
  }

  $groupentry = $mesg->entry(0);
  $groupdn = $groupentry->dn();

  #
  # start to evaluate user membership of groups
  #

  if ( $userentry->exists( 'memberOf' ) ) {
    foreach my $val ( $userentry->get_value( 'memberOf' ) ) {
      push( @newgroups, $val );
    }
  }


  $attrs = ['memberOf'];

  my ( $dn, $entry );
  while ( $dn = pop( @newgroups ) ) {
    if ( $dn eq $groupdn ) {
      return 1;
    }

    $examinedgroups{$dn} = 1; # assign to examined groups

    # retrieve members
    $filter = "&(distinguishedName=$dn)(objectClass=group)";
    $mesg = $ad->search( base => $base, filter => $filter, attrs => $attrs );
    $count =  $mesg->count;
    if ( 1 == $count ) {
      $entry = $mesg->entry( 0 );
      if ( $entry->exists( 'memberOf' ) ) {
        foreach my $val ( $entry->get_value( 'memberOf' ) ) {
          if ( !defined( $examinedgroups{ $val } ) ) {
            push( @newgroups, $val );
          }
        }
      }
    }

  }
  return 0;
}

1;


