diff -urbN smeserver-samba-0.1.0/createlinks smeserver-samba-0.1.0-062616-update/createlinks --- smeserver-samba-0.1.0/createlinks 2016-07-24 10:25:57.946060238 -0700 +++ smeserver-samba-0.1.0-062616-update/createlinks 2016-06-26 21:04:56.892472467 -0700 @@ -19,7 +19,6 @@ provision-domain-controller bootstrap-initialize-samba)); - ##Links for provision-domain-controller event $event = "provision-domain-controller"; event_link("adjust-samba-down", $event, "01"); @@ -29,6 +28,10 @@ event_link("samba-create-domain-admins", $event,"03"); event_link("adjust-samba-up", $event, "50"); +##Links for user-create +$event = "user-create"; +event_link("user-create-AD", $event, "04"); + ##Links for adjust-samba event $event = "adjust-samba"; event_link("adjust-samba-down", $event, "20"); diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/db/configuration/migrate/dnscache_set_ListenIP_AD smeserver-samba-0.1.0-062616-update/root/etc/e-smith/db/configuration/migrate/dnscache_set_ListenIP_AD --- smeserver-samba-0.1.0/root/etc/e-smith/db/configuration/migrate/dnscache_set_ListenIP_AD 1969-12-31 16:00:00.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/db/configuration/migrate/dnscache_set_ListenIP_AD 2016-06-27 18:15:13.000000000 -0700 @@ -0,0 +1,9 @@ +{ + my $serverRole = $DB->get_prop('smb','ServerRole') || ''; + # Set dnscache listen IP for Samba DNS Loopback routing + if ($serverRole eq 'DC') { + + $DB->set_prop ('dnscache','ListenIP', '127.0.0.3'); + + } +} diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/db/configuration/migrate/tinydns_set_ListenIP_AD smeserver-samba-0.1.0-062616-update/root/etc/e-smith/db/configuration/migrate/tinydns_set_ListenIP_AD --- smeserver-samba-0.1.0/root/etc/e-smith/db/configuration/migrate/tinydns_set_ListenIP_AD 1969-12-31 16:00:00.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/db/configuration/migrate/tinydns_set_ListenIP_AD 2016-06-27 18:13:29.000000000 -0700 @@ -0,0 +1,10 @@ +{ + + my $serverRole = $DB->get_prop('smb','ServerRole') || ''; + # Set tinydns listen IP for Samba DNS Loopback routing + if ($serverRole eq 'DC') { + + $DB->set_prop ('tinydns','ListenIP', '127.0.0.4'); + + } +} diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/events/actions/provision-domain-controller smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/provision-domain-controller --- smeserver-samba-0.1.0/root/etc/e-smith/events/actions/provision-domain-controller 2016-07-24 10:25:57.946060238 -0700 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/provision-domain-controller 2016-06-20 23:26:45.000000000 -0700 @@ -17,18 +17,17 @@ ##Pull arguments my $event = $ARGV [0]; -my $AdminPass = $ARGV [1]; +my $adminPass = $ARGV [1]; -die 'Samba provisioning error: Missing admin password' unless ($AdminPass); +die 'Samba provisioning error: Missing admin password' unless ($adminPass); -##Pull config parameters for DC provision +##Pull domain name from config dbase. This is the samba realm my $cdb = esmith::ConfigDB->open; -my $SystemName = $cdb->get('SystemName')->value || - die 'Samba provisioning error: SystemName not defined'; -my $DomainName = $cdb->get('DomainName')->value || +my $domainName = $cdb->get('DomainName')->value || die 'Samba provisioning error: Primary Domain Name not defined'; -my $WorkGroup = $cdb->get_prop ('smb', 'Workgroup') || +my $workGroup = $cdb->get_prop ('smb', 'Workgroup') || die 'Samba provisioning error: Workgroup not defined'; +my $realm = $workGroup . "." . $domainName; ##Bail if Samba has already been initialized if ($event eq 'bootstrap-initialize-samba' && @@ -61,8 +60,8 @@ ##Initialize Samba Domain warn "Samba domain: Provisining Active Directory."; my $provision = "/usr/bin/samba-tool domain provision --server-role=dc " . - "--domain=$WorkGroup " . - "--realm=$DomainName " . + "--domain=$workGroup " . + "--realm=$realm " . "--adminpass=$provisionPass " . "--dns-backend=SAMBA_INTERNAL " . "--use-rfc2307 " . @@ -86,7 +85,7 @@ ##Change administrator password from the stashed password to the admin password warn "Samba domain: Setting Admin Password\n"; my $set_admin_pass = "/usr/bin/samba-tool user setpassword Administrator " . - "--newpassword=" . $AdminPass . " " . + "--newpassword=" . $adminPass . " " . "-U Administrator\%" . $provisionPass; system ($set_admin_pass); diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/events/actions/samba-create-domain-admins smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/samba-create-domain-admins --- smeserver-samba-0.1.0/root/etc/e-smith/events/actions/samba-create-domain-admins 2016-07-24 10:25:57.946060238 -0700 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/samba-create-domain-admins 2016-06-25 15:31:09.000000000 -0700 @@ -43,14 +43,6 @@ system ($add_root); warn "Unable to create root Samba user\n" if ($? == -1); -my $add_members = "/usr/bin/samba-tool group addmembers " . - "\'Domain Admins\' ". - "root,admin " . - "-U Administrator\%$AdminPass"; -system ($add_members); -warn "Unable to add admin and root users to Domain Admins group\n" if ($? == -1); - - ##Create ad_admin account and add it to domain admins for runtime access to acive directory my $add_ad_admin = "/usr/bin/samba-tool user create " . "ad_admin " . esmith::AD::getADPass() . " " . @@ -58,11 +50,11 @@ system ($add_ad_admin); die "Samba provisioning error: Unable to create ad_admin user in Active Directory.\n" if ($? == -1); -$add_members = "/usr/bin/samba-tool group addmembers " . +my $add_members = "/usr/bin/samba-tool group addmembers " . "\'Domain Admins\' ". - "ad_admin " . + "root,admin,ad_admin " . "-U Administrator\%$AdminPass"; -#system ($add_members); -die "Samba provisioning error: Unable to add ad_admin user to the Domain Admins group.\n" if ($? == -1); +system ($add_members); +warn "Unable to add admin and root users to Domain Admins group\n" if ($? == -1); 1; diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/events/actions/user-AD-disable smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/user-AD-disable --- smeserver-samba-0.1.0/root/etc/e-smith/events/actions/user-AD-disable 1969-12-31 16:00:00.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/user-AD-disable 2016-06-26 19:21:09.000000000 -0700 @@ -0,0 +1,38 @@ +#!/usr/bin/perl -w + +#------------------------------------------------------------ +#This action disables an Active Directory user +# +#Command format: +# +# user-AD-disable event username +# +# event : calling event name +# username : username to disable +# +#Copyright 2016 Koozali Foundation, Inc. +#06/26/2016: G.Zartman +# +#The code contained herein can be distributed under the same +#license as Perl +# +#------------------------------------------------------------ +package esmith::thisaction; + +use strict; +use warnings; +no warnings ('qw'); + +##Pull arguments +my $event = $ARGV [0] || ''; +my $userName = $ARGV [1] || ''; + +die "user-AD-disable error: username not found in action arguments\n" + unless ($userName); + +my $disableUser = '/usr/bin/samba-tool user disable ' . $userName; +system ($disableUser); +die "create-AD-disable error: Unable to disable user:" + if ($? == -1); + +1; diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/events/actions/user-create-AD smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/user-create-AD --- smeserver-samba-0.1.0/root/etc/e-smith/events/actions/user-create-AD 2015-02-12 21:17:53.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/events/actions/user-create-AD 2016-06-26 19:09:07.000000000 -0700 @@ -1,39 +1,36 @@ #!/usr/bin/perl -w #------------------------------------------------------------ -#This action creates a user in the active directory given -#a unique username and a hash reference of specific user data. -#The hash reference must use the following keys or they will be -#ignored: -# -#\%user_data_hash ( -# -# 'description' => Some Description -# 'given-name' => First Name -# 'surname' => Last Name -# 'initials' => User Initials -# 'job-title' => Job Title -# 'company' => Company -# 'department' => Department -# 'mail-address' => Mailing Address -# 'physical-delivery-office' => Physical Address -# 'telephone-number' => Telephone Number -# 'internet-address' => Internet/Web Address -# } +#This action creates an Active Directory user given a username +#from the accountsDB. # -#Copyright 2014 Koozali Foundation, Inc. -#11/25/2014: G.Zartman +#Command format: +# +# user-account-AD event username data: +# +# event : calling event name +# username : unique username +# data : Hash reference to user data to populate the AD +# with. See the note below for +# +#Copyright 2015 Koozali Foundation, Inc. +#06/23/2016: G.Zartman # #The code contained herein can be distributed under the same #license as Perl # #TODO -#- May need ot do a bit of datachecking on the values, unless -# we assume the data comes to this action clean. Initials, -# for example, will error out if more than 3 characters are -# used for the value +# +#- We need to look at some kind of interm solution to use +# acccountsDB in the short term until we figure out where we are +# storing user data long term on SME. For now, we'll build a +# hash of data from the accounts dbase, but also provide a means +# to pass the data to this action with a hashref, which is the +# preferred method for AD. +#- For the hash reference, let's scrub it with the +# esmith::AD::User::ValidData method #- Consider setting the displayName attribute as well, otherwise -# Active directory does it automatically to +# Active Directory does it automatically to # given-name initials surname. In some cases, this looks goofy. # #------------------------------------------------------------ @@ -42,89 +39,109 @@ use strict; use warnings; use esmith::AccountsDB; -use esmith::AD; +use esmith::ConfigDB; +use esmith::AD::User; no warnings ('qw'); +use Data::Dumper; #For debugging ##Pull arguments my $event = $ARGV [0] || ''; my $userName = $ARGV [1] || ''; -my $refUserData = $ARGV [2] || ''; #hash reference -my $verbose = $ARGV [3] || ''; #flag to display user create output +my $data = $ARGV [2] || ''; #Hash ref die "user-create-AD error: username not found in action arguments\n" unless ($userName); -##Check AccountsDB to see if username existsi -my $adb = esmith::AccountsDB->open_ro; -if ($adb->get($userName)){ - die "Error in $event: User exists in esmith::accounts\n"; +##Check AD to see if username exists. If usernae already exists, then +##bail out. +my $ad = esmith::AD::User->new; +die "user-create-AD error: User already exists in Active Directory.\n" + if ($ad->doesUserExist($userName)); + +##---------------------LEGACY MODE-------------------------------------- +##The following block of code will pull data from the +##AccountsDB and copy it into the active directory. Using legacy mode +##will ##ignore user data passed via the $data argument. Once we drop +##support for the AccountsDB, this code can be removed + +my $cdb = esmith::ConfigDB->open_ro; +my $legacy = $cdb->get_prop('smb','legacy'); +$cdb->close; + +if ($legacy eq 'enabled') { + warn "Legacy Mode Enabled.\n"; + + my $adb = esmith::AccountsDB->open_ro; + my $acct = $adb->get($userName); + die "user-create-AD error: $userName not found in accounts database.\n" + unless (defined $acct and $acct->prop('type') eq "user"); + + #build a hashref of AD data from accountdb data + $data= { + 'company' => $acct->prop('Company'), + 'department' => $acct->prop('Dept'), + 'givenName' => $acct->prop('FirstName'), + 'mail' => $acct->prop('ForwardAddress'), + 'physicalDeliveryOfficeName' => $acct->prop('Street'), + 'sn' => $acct->prop('LastName'), + 'telephoneNumber' => $acct->prop('Phone') + }; + + $adb->close; } +##----------------END LEGACY MODE---------------------------------------- + -##Check AD to see if username exists -my $ad = esmith::AD->new; -if ($ad->doesUserExist($userName)){ - die "Error in $event: User exists in Active Directory\n"; +##Validate the user data +if (ref($data) eq 'HASH') { + foreach my $key (keys %$data) { + unless ($ad->validData->{$key}) { + warn "Ignoring $key attribute for update -- Not supported.\n"; + delete $data->{$key}; + } + } } -##Create user in active directory. +##Build Active Directory user create command my $homeDirectory = '/home/e-smith/files/users/' . $userName . '/home/'; -my %userData = (); -if (ref($refUserData) eq 'HASH') {%userData = %$refUserData;} +my $shell = '/usr/bin/rssh'; +if ($data->{'loginShell'}) { + $shell = $data->{'loginShell'}; + delete $data->{'loginShell'}; +} + +##Create AD User +my $adPassword = $ad->getADPass(); my $addUser = "/usr/bin/samba-tool user create $userName " . "--home-directory=$homeDirectory " . - '--login-shell=/usr/bin/rssh ' . + "--login-shell=$shell " . '--random-password ' . - '--description="Koozali User:" '; - -$addUser .= "$userData{'description'} " - if (defined $userData{'description'}); -$addUser .= "--given-name=$userData{'given-name'} " - if (defined $userData{'given-name'}); -$addUser .= "--surname=$userData{'surname'} " - if (defined $userData{'surname'}); -$addUser .= "--initials=$userData{'initials'} " - if (defined $userData{'initials'}); -$addUser .= "--job-title=$userData{'job-title'} " - if (defined $userData{'job-title'}); -$addUser .= "--company=$userData{'company'} " - if (defined $userData{'company'}); -$addUser .= "--department=$userData{'department'} " - if (defined $userData{'department'}); -$addUser .= "--mail-address=$userData{'mail-address'} " - if (defined $userData{'mail-address'}); -$addUser .= "--physical-delivery-office=$userData{'physical-delivery-office'} " - if (defined $userData{'physical-delivery-office'}); -$addUser .= "--telephone-number=$userData{'telephone-number'} " - if (defined $userData{'telephone-number'}); -$addUser .= "--internet-address=$userData{'internet-address'} " - if (defined $userData{'internet-address'}); - -#Run user create command silently so passwords dont show up in log files -my $output = `$addUser 2>&1`; -if ($output =~ /ERROR/) { - die "Error creating $userName account" . ($verbose ? ': ' . $output : ".\n"); -} -else { - warn("Successfully created user $userName in the Active Directory.\n"); + '--description="Koozali User:" ' . + "-U ad_admin\%$adPassword"; +system ($addUser); +die ("Unable to add user #userName to Active Directory\n") if ($? == -1); + +##Make user a Posix User +unless ($ad->setPosixUser($userName)) { + warn ("Unable to set posix objectClass in AD for $userName.\n"); } -##Set Posix settings for user -$ad = esmith::AD->new(); +##Build Unix Attributes my $UID = $ad->createUID($userName) || ''; my %posix = ('uidNumber' => $UID, - 'gidNumber' => $UID, + 'gidNumber' => '513', 'unixHomeDirectory' => $homeDirectory); -unless ($ad->setAttr('user',$userName,\%posix)) { - warn("Unable to set Posix attributes for $userName.\n"); + +##Merge Unix Attributes with any Extended Attributes and save to AD +my $mergedData = \%posix; +if (ref($data) eq 'HASH') { + $mergedData = {%posix,%$data}; } +$ad->setManyAttr($userName,\%posix) || + warn("Unable update user attributes for $userName.\n"); -##Disable user until we enable in server-manager by setting password -my $disableUser = '/usr/bin/samba-tool user disable ' . $userName; -system ($disableUser); -die "create-user-AD error: Unable to disable user after create:" - if ($? == -1); 1; diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/templates/etc/rc.d/init.d/masq/90PreroutingDNScache smeserver-samba-0.1.0-062616-update/root/etc/e-smith/templates/etc/rc.d/init.d/masq/90PreroutingDNScache --- smeserver-samba-0.1.0/root/etc/e-smith/templates/etc/rc.d/init.d/masq/90PreroutingDNScache 1969-12-31 16:00:00.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/templates/etc/rc.d/init.d/masq/90PreroutingDNScache 2016-06-21 01:52:10.000000000 -0700 @@ -0,0 +1,9 @@ +{ + my $dnscache_ip = $dnscache{ListenIP} || '127.0.0.3'; + + ##Preroute to redirect DNS from local IP to dnscache on localhost 127.0.0.x + $OUT .= " /sbin/iptables -t nat -I PREROUTING --in-interface eth0\\\n"; + $OUT .= "\t-p tcp -d $LocalIP --destination-port 53 -j DNAT\\\n"; + $OUT .= "\t--to-destination $dnscache_ip"; +} + diff -urbN smeserver-samba-0.1.0/root/etc/e-smith/templates/etc/smb.conf/12realm smeserver-samba-0.1.0-062616-update/root/etc/e-smith/templates/etc/smb.conf/12realm --- smeserver-samba-0.1.0/root/etc/e-smith/templates/etc/smb.conf/12realm 2014-11-19 23:32:12.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/etc/e-smith/templates/etc/smb.conf/12realm 2016-06-20 23:38:53.000000000 -0700 @@ -1,5 +1,6 @@ { - my $realm = $smb{realm} || $DomainName; + my $workgroup = $smb{Workgroup} || 'sme-server'; + my $realm = $smb{realm} || $workgroup . "." . $DomainName; "realm = $realm"; } diff -urbN smeserver-samba-0.1.0/root/usr/share/perl5/vendor_perl/esmith/AD/OU.pm smeserver-samba-0.1.0-062616-update/root/usr/share/perl5/vendor_perl/esmith/AD/OU.pm --- smeserver-samba-0.1.0/root/usr/share/perl5/vendor_perl/esmith/AD/OU.pm 1969-12-31 16:00:00.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/usr/share/perl5/vendor_perl/esmith/AD/OU.pm 2016-06-26 20:31:35.000000000 -0700 @@ -0,0 +1,84 @@ +package esmith::AD::OU; + +use strict; +use base 'esmith::AD'; +no warnings ('qw'); + +=head1 NAME + +esmith::AD::OU - Sub-Class to the Koozali esmith::AD API + +=head1 SYNOPSIS + + use esmith::AD::OU; + + my $ad = esmith::AD::OU->new(); + my @organizational_units = $ad->OUs; + +=head1 DESCRIPTION + +This module provide esmith::AD::OU methods for management of organizational +units stored in the Koozali SME Server Active Directory. These methods are +the preferred methods to interact with user objects in the Active Directory. +Many methods in this class inherit functionality from the esmith::AD parent +class and provide run-time binding of LDAP query elements to these parent +methods. Methods provided in this module abstract Net::LDAP so that knowledge +of this module is not required. +=cut + +sub new { + my ($class, %params) = @_; + + #Define runtime LDAP query parameters + my $base = { + 'default' => '&(objectClass=ou)(!(objectClass=computer))' + }; + my $set = { + 'all' => '', + 'builtin' => '(!(objectClass=posixAccount))', + 'koozali' => '(objectClass=posixAccount)', + 'single' => '' + }; + my $type = { + 'posix' => ['top','ou','posixAccount'] + }; + + my $attribute = { + 'default' => 'sAMAccountName' + }; + my $self = { + 'base' => $base, + 'set' => $set, + 'attribute' => $attribute, + 'type' => $type + }; + + + #bless paramters into the class + bless ($self, $class); + + return ($self); +} + + +=head2 Methods + +Methods here + +=head1 COPYRIGHT + +Copyright (c) 2016 Koozali Foundation, Inc. +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 AUTHOR + +Greg Zartman, Koozali Foundation + +=head1 SEE ALSO + +Net::LDAP + +=cut + +1; diff -urbN smeserver-samba-0.1.0/root/usr/share/perl5/vendor_perl/esmith/AD/User.pm smeserver-samba-0.1.0-062616-update/root/usr/share/perl5/vendor_perl/esmith/AD/User.pm --- smeserver-samba-0.1.0/root/usr/share/perl5/vendor_perl/esmith/AD/User.pm 2015-02-12 21:15:51.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/usr/share/perl5/vendor_perl/esmith/AD/User.pm 2016-06-25 23:12:35.000000000 -0700 @@ -18,34 +18,46 @@ =head1 DESCRIPTION This module provide esmith::AD::User methods for management of users stored -in the Koozali Active Directory. These methods are the preferred methods to -interact with user objects in the Active Directory. Many methods in this -class inherit functionality from the esmith::AD parent class and provide -run-time binding of user objects to these parent methods. Methods provided -in this module abstract Net::LDAP so that knowledge of this module is not -required. - +in the Koozali SME Server Active Directory. These methods are the preferred +methods to interact with user objects in the Active Directory. Many methods +in this class inherit functionality from the esmith::AD parent class and +provide run-time binding of LDAP query elements to these parent methods. +Methods provided in this module abstract Net::LDAP so that knowledge of +this module is not required. =cut sub new { my ($class, %params) = @_; - my $queryElements = { + + #Define runtime LDAP query parameters + my $base = { + 'default' => '&(objectClass=user)(!(objectClass=computer))' + }; + my $set = { 'all' => '', - 'builtin' => "(!(objectClass=posixAccount))", - 'koozali' => "(objectClass=posixAccount)", - 'member' => '', - 'noMachine' => "(!(objectClass=computer))", - 'object' => "&(objectClass=user)", + 'builtin' => '(!(objectClass=posixAccount))', + 'koozali' => '(objectClass=posixAccount)', 'single' => '' }; - my $objectClass = ['top','person','organizationalPerson','user', - 'posixAccount']; + my $type = { + 'posix' => ['top','person','organizationalPerson', + 'user','posixAccount'] + }; + + my $attribute = { + 'default' => 'sAMAccountName' + }; my $self = { - 'query' => $queryElements, - 'objectClass' => $objectClass + 'base' => $base, + 'set' => $set, + 'attribute' => $attribute, + 'type' => $type }; + + #bless paramters into the class bless ($self, $class); + return ($self); } @@ -69,7 +81,7 @@ } -=head3 createUID($name) +=head3 createUID($name)* This method calculates a unix UID for an Active Directory user based upon the SID for the Active Directory object. @@ -85,30 +97,30 @@ unless $name; #Pull SID from the AD - return (0) unless (my $SID = $self->getSID('user',$name)); + return (0) unless (my $SID = $self->getSID($name)); #Return UID = RID + 2000 my $RID = (split(/-/,$SID))[7]; return($RID+2000); } -=head3 doesUserExist($username) +=head3 doesUserExist($username)* This method checks the Active Directory for the existance of a username passed as a parameter to the method. The method returns 1 if the username exists in the Active Directory and 0 if it does not. -Usage: $user_check = $ad->doesUserExist('group_name'); +Usage: $user_check = $ad->doesUserExist('user_name'); =cut sub doesUserExist { my ($self,$user) = @_; - my @users = $self->listUsers('all'); + my ($userRecord) = $self->queryObjects($user); - if (grep { $user eq $_ } @users) { return(1); } - else{return(0);} + if (defined $userRecord) {return(1);} + else {return(0);} } =head3 getUID($username) @@ -132,7 +144,7 @@ This method returns the value of the specified attribute for a user listed in the Active Directory. -Usage: $value = $ad->getUserAttr('uername','attribute'); +Usage: $value = $ad->getUserAttr('username','attribute'); =cut @@ -173,7 +185,7 @@ return (map {(split(/[=,]/,$_))[1]} @results); } -=head3 getUserStatus($username) +=head3 getUserStatus($username)* This method returns user account status, enabled or disabled, for a specified username. @@ -188,23 +200,22 @@ return ($self->getStatus($user)); } -=head3 listUsers($set) +=head3 listUsers($set)* -This method is an alias method to the esmith::AD::listObjects method. -It returns a list of usernames from the Active Directory, and also -subsets of users: +This method returns a list of usernames in the active directory +depending upon the search set sent to the method. Valid search +sets include: - - builtin: Users defined during the Active Directory + - builtin : Users defined during the Active Directory provisioning (e.g., Administrator); - - koozali: Users defined in the Koozali API. - - all: All users (default). + - koozali : Users defined in the Koozali API. + - all : All users (default). Usage: use esmith::AD::User; my $ad = esmith::AD::User->new(); my @users = $ad->listUsers('koozali'); - =cut sub listUsers { @@ -213,61 +224,15 @@ return($self->listObjects($set)); } -=head3 queryMembers($set,$value) - -###FIX ME: Need to rewire -This method is an alias method to the esmith::AD::listObjects method. -It returns a list of usernames from the Active Directory, and also -subsets of users: - -=cut - -sub queryMembers { - my ($self,$set,$value)=@_; - - my $queryFilter = $self->buildQuery('member',$set,$value) || ''; - - return ($self->runQuery($queryFilter)); -} - -=head3 queryObjects($set,$value) - -###FIX ME: Need to rewire -This method is an alias method to the esmith::AD::listObjects method. -It returns a list of usernames from the Active Directory, and also -subsets of users: - - - builtin: Users defined during the Active Directory - provisioning (e.g., Administrator); - - koozali: Users defined in the Koozali API. - - all: All users (default). - -Usage: - use esmith::AD::User; - - my $ad = esmith::AD::User->new(); - my @users = $ad->listUsers('koozali'); - -=cut - -sub queryObjects { - my ($self,$set,$value)=@_; - - my $class = 'object'; - if (defined $value) {$class = 'single'}; - - my $queryFilter = $self->buildQuery($class,$set,$value) || ''; - - return ($self->runQuery($queryFilter)); -} - -=head3 setPostixUser ($user) +=head3 setPosixUser($user) * This method designates a user as a Posix User. The Posix user type -represents the users managed by Koozali and are those groups that are -displayed in the server-manager +represents the users managed by Koozali and displayed in the +server-manager. This method returns 1 if successful and 0 if something +went wrong. -Usage: $ad->setPosixUser('username') +Usage: $ad = esmith::AD::User; + $ad->setPosixUser('username') =cut @@ -278,22 +243,6 @@ } -=head3 setUserAttr($user,$attr,$value) - -This method sets or modifies an attribute value for the specified -username. - -Usage: $ad->setUserAttr('username','attribute','value') - -=cut - -sub setUserAttr { - my ($self,$user,$attr,$value) = @_; - - if ($self->setAttr($user,{$attr=>$value})) { return(1); } - else { die->error("Unable to modify \"$attr\" for user \"$user\".\n");} -} - =head3 setUserGroups($username,$groupRef) This mthod sets the groups that a user is a member of give the username @@ -379,7 +328,7 @@ =head1 COPYRIGHT -Copyright (c) 2014 Koozali Foundation, Inc. +Copyright (c) 2016 Koozali Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff -urbN smeserver-samba-0.1.0/root/usr/share/perl5/vendor_perl/esmith/AD.pm smeserver-samba-0.1.0-062616-update/root/usr/share/perl5/vendor_perl/esmith/AD.pm --- smeserver-samba-0.1.0/root/usr/share/perl5/vendor_perl/esmith/AD.pm 2015-02-12 21:14:42.000000000 -0800 +++ smeserver-samba-0.1.0-062616-update/root/usr/share/perl5/vendor_perl/esmith/AD.pm 2016-06-26 18:54:06.000000000 -0700 @@ -6,11 +6,15 @@ use esmith::ConfigDB; use esmith::AccountsDB; +#This package is to aid in debugging. Can remove +use Data::Dumper; + no warnings ('qw'); =head1 NAME -esmith::AD - Interface to Samba 4 Active Directory for Koozali +esmith::AD - Interface to Samba 4 Active Directory for + Koozali SME Server =head1 SYNOPSIS @@ -33,7 +37,7 @@ This module provides an abstracted interface to Samba 4 Active Directory for the Koozali SME Server. -Methods provided by this interface interacts with Samba 4 Active Directory +Methods provided by this interface interact with Samba 4 Active Directory using the Net::LDAP perl module. Understanding of Net::LDAP syntax is not required to utilize the primary functionality provided by this interface, but the full suite of Net::LDAP methods is provided by this module should @@ -51,15 +55,15 @@ =head2 Fully Abstraced Methods -For all intents and purposes, this interface is the preferred approach to -interact with the Koozali Active Directory. Included in this API are child -classes for managing the different object types in the Active Directory +For all intents and purposes, fully abstracted methods are the the preferred +approach to interact with the Koozali Active Directory. Included in this API +are child classes for managing the different object types in the Active Directory including users, groups, and DNS entries. Methods provided in this parent class should generally not be called directly (unless noted in the method -specific documentation), but from one of these child classies. This is because +specific documentation), but from one of these child classes. This is because this API provides run-time binding of many of these parent methods to the object types they are presented with. See esmith::AD::User, esmith::AD::Group, -and esmith::AD::DNS for more details. +esmith::AD:OU, and esmith::AD::DNS for more details. Usage example: use esmith::AD::User; @@ -69,10 +73,10 @@ =head3 getADRecord {$name) -This method returns a hash reference of attribute / value pairs +This method returns a hash reference of attribute/value pairs from the Active Directory for attributes supported by this method, -given an object name. This method should not be called from this -class, but one of the esmith::AD child classes. +given an object name. This method must be called from a child +method or this code will return a runtime error. Usage: $hashref = $ad->getADRecord($name); @@ -102,9 +106,8 @@ =head3 SID($name) This method retrieves the SID for a specified Active Directory -object name, decrypts it, and then returns it. This method -must be called from a esmith::AD child and not directly -from this class. +object name, decrypts it, and then returns it. This method must be +called from a child method or this code will return a runtime error. Usage: my $sid = $ad->getSID('username'); @@ -118,7 +121,7 @@ if (ref($self) eq 'esmith::AD'); die $self->error("Object name not specified.\n") unless $name; - my ($record) = $self->queryObjects('single',$name); + my ($record) = $self->queryObjects($name); die $self->error("Unable to find \"$name\" in the Active Directory.\n") unless ($record); @@ -128,11 +131,12 @@ return($self->sidToString($encryptedSID)); } + =head3 getStatus($name) This method returns the status, enabled or disabled, for various entries -in the Active Directory. This method must be called from a esmith::AD -child and not directly from this class. +in the Active Directory. This method must be called from a child +method or this code will return a runtime error. Usage: my $status=$ad->getStatus('username'); @@ -147,7 +151,7 @@ die $self->error("Object name not specified.\n") unless $name; #Lookup entry in the active directory - my ($record) = $self->queryObjects('single',$name); + my ($record) = $self->queryObjects($name); die $self->error("Unable to find \"$name\" in the Active Directory.\n") unless ($record); @@ -161,19 +165,26 @@ =head3 listObjects($set) -This method returns a list of object names (cn) from the Active Directory. -The method is meant to be called from an esmith::AD child and not directly -from the parent class, esmith::AD. This method also queriesi subsets of -objects: +This method returns a list of object names (sAMAccountName) from the Active +Directory. It will return one of the following subsets of AD objects: - - all: All objects within the search objectClass; + - all: All searchable objects; - builtin: Objects defined during the AD provisioning; - - koozali: Objects defined by this API. + - koozali: Objects defined by this API; -Usage: use esmith::AD::User; +Usage example 1: Get a list of koozali users + use esmith::AD::User; my $ad = esmith::AD;User->new(); - my @objects = $ad->listObjects('koozali'); + my @kooazli_users = $ad->listObjects('koozali'); + +Usage example 2: Get list of buildin groups + + usee esmith::AD:Group; + my $ad = esmith::AD:Group->new(); + my @builtin_groups = $ad->listObjects('builtin'); + +This method must be called from a child method or this code will return a runtime error. =cut @@ -192,50 +203,172 @@ die $self->error("Unable to query objects from Active Directory.\n") unless (@records); - my @objects = map { $_->get_value('cn') } @records; + my @objects = map { $_->get_value('sAMAccountName') } @records; return(@objects); } -=head3 setAttr($type,$name,$hashRef) -This method sets or modifies attribute / value pairs for an Active Directory -object given object type, object name, and a hash reference to the values -to set or modify. It should be noted that this method will only update -attribute / value pairs as defined the validData method. - -NOTE: This method dies is data fails validation check. This may need - to be changed to provide better feedback to server-panels and - what not. - -FIXME: 1)NEED TO FIX THE CODE THAT VALIDATES DAT APRIOR TO UPDATING - 2)The name attribute is actually givenName + sn (surname) attribute - with a space between. We should consider forcing this instead of - letting it be changed free form as we are now. + + +=head3 queryMembers($set,$value) + +###FIX ME: Need to rewite POD +This method is an alias method to the esmith::AD::listObjects method. +It returns a list of usernames from the Active Directory, and also +subsets of users: + +=cut + +sub queryMembers { + my ($self,$set,$value)=@_; + + ##Need to rewrite this to use the queryObjectsmethod + my $queryFilter = $self->BuildQuery('member',$set,$value) || ''; + + return ($self->runQuery($queryFilter)); +} + + +=head3 queryObjects($self,$value,$attribute,$base) + +This is the primary query method for this API and returns Net::LDAP record +objects from the Active Directory in list context. Many of the query elements +for an AD query are set via runtime parameters in the child class calling +this method. If nothing is passed to this method, then this method returns +a list of all user records in the Active Directory. + +$value defines what records this method will search for, given default query +elements set in the child class. Setting $value to the following will search +for specific groups of user records in the AD: + + - all: All user account records in the active directory + - buildin: Builtin user account records. Example: Administrator + - koozali: All user accoun records defined in this UI. These are the typical + user accounts setup in the server-manager for via the commandline. + +To search for a single user record, set $value to the Active Directory attribute +value for the record you'd like to search for. The default search attribute is +the username (e.g., sAMAccountName). + +This method can also search for single records given other AD attributes, +but other search attributes must be defined in child classes. See child +class perldoc for details on support AD attributes. + + + #Search for a user record by username + use esmith::AD::User; + my $ad = esmith::AD->new; + my ($userRecord) = $ad->QueryObjects('username'); + + #Search all Koozali users + user esmith::AD::User; + my @koozaliUser = $ad-QueryObjects('koozali'); + +=cut + +sub queryObjects { + my ($self,$setValue,$attribute,$base)=@_; + + ##Error out if this method called from this class and not a child + die $self->error("Must call this method on a esmith::AD child object.\n") + if (ref($self) eq 'esmith::AD'); + + ##Check for changes to the runtime query parameters. + $attribute = 'default' + unless ($attribute && defined($self->{'attribute'}->{$attribute})); + $base = 'default' + unless ($base && defined($self->{'base'}->{$base})); + + ##Assume we are searching for all user accounts unless the $value tells us + ##otherwise + my $set = 'all'; + my $searchValue = ''; + + ##Look at $setValue and determine query set + if (grep {$setValue eq $_} qw(all builtin koozali)) { + $set = $setValue; + } + else { + $set = 'single'; + $searchValue = $setValue; + }; + + + ##Build Query Filter + my $queryFilter = $self->{'base'}->{"$base"} . + $self->{'set'}->{"$set"}; + if ($set eq 'single') { + $queryFilter .= "(" . + $self->{'attribute'}->{$attribute} . + "=" . + $searchValue . + ")"; + } + + return ($self->runQuery($queryFilter)); +} + + +=head3 setAttr($name,$attr,$value) + +This method sets or modifies a single attribute/value pair for a +specified object given the object sAMAccount Name and a attribute +value pair. See setManyAttr for more details about updating object +attributes. + +Usage: $ad->setAttr('name','attribute','value') + =cut sub setAttr { + my ($self,$name,$attr,$value) = @_; + + if ($self->setManyAttr($name,{$attr=>$value})) { return(1); } + else { die->error("Unable to modify \"$attr\" for \"$name\".\n");} +} + + +=head3 setManyAttr($name,$hashRef) + +This method sets or modifies multiple attribute / value pairs for +an Active Directory object sAMAccount. The method uses +runtime binding to determine which type of object is being updated. +Update attribute pairs need to be passed to the method as a hash +reference. It should be noted that this method will only update +attribute / value pairs supported by this class. All other pairs will +be ignored. Supported attribute/value paris are defined in the +validData method. This method returns 1 if successful and will typically +die with appropriate error information if unsucessful. + +NOTE: This method currently dies if data fails validation check. + This may need to be changed to provide better feedback to + server-panels in the future. +=cut + +sub setManyAttr { my ($self,$name,$attrRef) = @_; #Don't allow this method from this class die $self->error("Must call this method on a esmith::AD child object.\n") if (ref($self) eq 'esmith::AD'); + + #Check to make sure we have enough data to perform an update die $self->error("Entity to modify not specified.\n") unless ($name); die $self->error("No attributes set to modify for $name\n") unless (ref($attrRef) eq 'HASH'); - - ##Validate attributes and values to update + #Validate that we support the attributes for update foreach my $key (keys %$attrRef) { - die $self->error("Unsupported attribute \"$key\".\n") - unless ($self->validData->{$key}); - - ###FIX ME: Add regex check for actual values + unless ($self->validData->{$key}) { + warn "Ignoring $key attribute for update -- Not supported.\n"; + delete $attrRef->{$key}; + } } #Query Record to update from AD - my ($record) = $self->queryObject($name); - die $self->error("Unable to query \"$name\" from Active Directory\n.") + my ($record) = $self->queryObjects($name); + die $self->error("Unable to query \"$name\" from Active Directory.\n.") unless ($record); my $result = $self->updateAD($record,$attrRef); @@ -244,11 +377,10 @@ return (1); } -=head3 setPosix($type,$name) +=head3 setPosix($type,$name) * This method designates an Active Directory object as a Posix object given -object type and name. Currently, this method support the types user and -group. +and object sAMAccount name (e.g., username). =cut @@ -260,13 +392,15 @@ if (ref($self) eq 'esmith::AD'); #Pull record from AD - my ($record) = $self->queryObject($name); + my ($record) = $self->queryObjects($name); die $self->error("Unable to find \"$name\" in the Active Directory.\n") unless ($record); + ##Localize posix type array reference to shorten LDAP update syntax + my $typeRef = {'objectClass' => $self->{'type'}->{'posix'}}; + #Set the Posix objectClass - my $result = $self->updateAD($record, - {'objectClass'=>$self->{'objectClass'}}); + my $result = $self->updateAD($record,$typeRef); die $self->error("Unable to update \"$name\" record.\n") unless ($result); return(1); @@ -278,11 +412,11 @@ Methods providing a thin abstracted connection to Koozali Active Directory using Net::LDAP. These methods are designed to connect directly to the Koozali Active Directory and return Net::LDAP objects. The developer should -generally not need to call these Partiall Abstracted Methods directly, as -this class includes Fully Abstracted Methods that provide Active Directory -functionality needed to manage the Koozali SME Server. However, the -developer may utilize these Partially Abstracted Methods for more fine -grained interacting with the Active Directory using Net::LDAP. +generally not need to call these Partially Abstracted Methods directly, as +this class includes Fully Abstracted Methods to more directly and specifically +access data in the Active Directly. However, the developer may utilize these +Partially Abstracted Methods for more fine grained interacting with the +Active Directory using Net::LDAP. =head3 connect() @@ -308,6 +442,7 @@ my $domain = $self->domain; my $sme_ad_host=$self->hostIP; + #Pull AD password from local storage my $ad_password = $self->getADPass; @@ -316,6 +451,7 @@ #Bind to the AD and return the connection object my $bind = $ad->bind($domain . "\\ad_admin", password=>$ad_password); + if($bind->code) { die $self->error($bind->server_error); } @@ -341,17 +477,21 @@ sub runQuery { my ($self,$filter) = @_; - die $self->error('Query filter no specified.') unless $filter; + die $self->error('Query filter not specified.') unless $filter; - ##Connect to AD + ##Bind to AD my $ad = $self->connect; - ##Send query to the Active Directory + ##Get LDAP base DN for AD query -- we need to build it. + my $base = $self->baseDN; + + ##Query the Active Directory my $query=$ad->search( - base => 'DC=domain,DC=com', + base => "$base", filter => "$filter" ); $ad->unbind; + if($query->code) { die $self->error($query->server_error);} return ($query->entries); @@ -362,7 +502,7 @@ This method updates an Active Directory record, given a Net::LDAP::Entry object and a hash reference of data to update. This method does not check the contents of the record to update nor the hash reference prior to trying -to upate the Active Directory other than schema checks done by the LDAP +to update the Active Directory, other than schema checks done by the LDAP server. The method will return 1 if the update was successful and will die with a LDAP server error is there was a problem. @@ -375,7 +515,7 @@ die $self->error("Record object to update not specified.\n") unless ($record =~ /Net::LDAP::Entry/); - die $self->error("Update hash reference no specified.\n") + die $self->error("Update hash reference not specified.\n") unless (ref($attrRef) eq "HASH"); #Bind to AD and update @@ -397,27 +537,27 @@ ##other than the note comments contained in this code, as they ##are meant to support other methods contained in this class. ##The developer may use these methods, but should examine the -##code closely to achieved desired results. +##code closely to achieve desired results. ##----------------------------------------------------------- #------------------------------------------------------------ -#Method: buildFilter($set,$value) -# This method builds a query filter by run-time binding -# to esmith::AD child query elements +#Method: baseDN() +# This method builds the Base DN for LDAP query of the +# Active Directory. The Base DN is build from the +# primary machine domain name, which is considered the +# realm by Samba. +# #----------------------------------------------------------- -sub buildQuery { - my ($self,$class,$set,$value)=@_; - - #Set dynamic LDAP query elements - $self->{'query'}->{'single'} = "&(cn=$value)(objectClass=group)"; - $self->{'query'}->{'member'} = "&(objectClass=group)(memberOf=$value)"; +sub baseDN { + my ($self)=@_; - $set = 'all' unless (grep {$set eq $_} qw(all builtin koozali)); + my $realm = $self->realm; + my @elements = split (/\./, $realm); - my $filter = $self->{'query'}->{$class} . $self->{'query'}->{$set}; - $filter .= $self->{'query'}->{'noMachine'}; + my $base = ''; + foreach (@elements) {$base .= "DC=" . $_ . ","}; - return($filter) + return(substr ($base,0,-1)); } #------------------------------------------------------------ @@ -426,24 +566,48 @@ sub error { my ($self,$error)=@_; + $error = '' unless $error; + return ((caller(1))[3] . " error: " . $error); } #------------------------------------------------------------ #Method: domain() -# This method returns the Samba domain from the smb -# configuration dbase. This method is not meant to be -# used external to this class. +# This method returns the Samba domain, which is stored +# in the configuration dbase as Workgroup #------------------------------------------------------------ sub domain { my $self = shift; my $cdb = esmith::ConfigDB->open_ro; my $domain = $cdb->get_prop('smb','Workgroup') || - die $self->error("Unable to determine AD domain name from ConfigDB\n"); + die $self->error("Unable to determine the Workgroup from ConfigDB\n"); + $cdb->close; + + return ($domain); +} + +#------------------------------------------------------------ +#Method: realm() +# This method returns the realm which is the SME +# domain name stored in the configuration dbase +# This method is not meant to be used external +# to this class. +#------------------------------------------------------------ +sub realm { + my $self = shift; + + my $cdb = esmith::ConfigDB->open_ro; + my $realm = $cdb->get_prop('smb','realm'); + return ($realm) if defined $realm; + + my $domainName = $cdb->get_value('DomainName') || + die $self->error("Unable to determine Domain Name from ConfigDB\n"); $cdb->close; - return($domain); + my $ad_domain = $self->domain(); + + return($ad_domain . '.' . $domainName); } #------------------------------------------------------------ @@ -512,6 +676,12 @@ return $sid_str; } +=head1 COPYRIGHT + +Copyright (c) 2016 Koozali Foundation, Inc. +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + =head1 AUTHOR Greg Zartman, Koozali Foundation