diff --git a/html/pfappserver/root/src/composables/useInputValidator.js b/html/pfappserver/root/src/composables/useInputValidator.js
index 61f2176654f2..2b5907f6da16 100644
--- a/html/pfappserver/root/src/composables/useInputValidator.js
+++ b/html/pfappserver/root/src/composables/useInputValidator.js
@@ -40,7 +40,10 @@ export const useInputValidator = (props, value, recursive = false) => {
let localState = ref(unref(state))
let localInvalidFeedback = ref(unref(invalidFeedback))
let localValidFeedback = ref(unref(validFeedback))
- let localApiFeedback = ref(unref(apiFeedback))
+ let localApiFeedback = ref(undefined)
+ watch(apiFeedback, () => {
+ localApiFeedback.value = apiFeedback.value
+ }, { immediate: true })
// yup | https://github.com/jquense/yup
let localValidator = validator
diff --git a/html/pfappserver/root/src/views/Configuration/domains/_components/TheForm.vue b/html/pfappserver/root/src/views/Configuration/domains/_components/TheForm.vue
index e4af7e2c989c..022afcac4743 100644
--- a/html/pfappserver/root/src/views/Configuration/domains/_components/TheForm.vue
+++ b/html/pfappserver/root/src/views/Configuration/domains/_components/TheForm.vue
@@ -52,6 +52,7 @@
@@ -289,12 +290,27 @@ export const setup = (props, context) => {
)
})
+ const isDefaultOU = computed(() => {
+ const { ou } = form.value
+ return !!ou && /^computers$/i.test(ou) || !ou
+ })
+
+ const ouFeedback = computed(() => {
+ if (isDefaultOU.value) {
+ return undefined
+ }
+ return i18n.t(`Non-default OU is defined. LDAPS service on port 636 is required in Domain Controller.`)
+ })
+
+
return {
schema,
formGroupComputedMachineAccountPassword,
isMachineAccountHash,
machineAccountFeedback,
- machineAccountBind
+ machineAccountBind,
+ isDefaultOU,
+ ouFeedback
}
}
diff --git a/lib/pf/UnifiedApi/Controller/Config/Domains.pm b/lib/pf/UnifiedApi/Controller/Config/Domains.pm
index 396c61e5bd68..59bf7b4cfd7b 100644
--- a/lib/pf/UnifiedApi/Controller/Config/Domains.pm
+++ b/lib/pf/UnifiedApi/Controller/Config/Domains.pm
@@ -35,9 +35,9 @@ use Encode qw(encode);
use Net::DNS;
use JSON;
-=head2 test_join
+=head2 create
-Test if a domain is properly joined
+create machine account and domain config file.
=cut
@@ -87,6 +87,7 @@ sub create {
my $dns_name = $item->{dns_name};
my $workgroup = $item->{workgroup};
my $real_computer_name = $item->{server_name};
+ my $ou = $item->{ou};
if ($computer_name eq "%h") {
$real_computer_name = hostname();
@@ -122,16 +123,18 @@ sub create {
return $self->render_error(422, "Unable to determine AD server's IP address.\n")
}
- my $baseDN = $dns_name;
- $baseDN = generate_baseDN($dns_name);
-
if (!is_nt_hash_pattern($computer_password)) {
- my ($add_status, $add_result) = pf::domain::add_computer(" ", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $baseDN, $workgroup, $workgroup, $bind_dn, $bind_pass);
+ my ($add_status, $add_result) = pf::domain::add_computer(" ", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $dns_name, $workgroup, $ou, $bind_dn, $bind_pass);
if ($add_status == $FALSE) {
if ($add_result =~ /already exists(.+)use \-no\-add/) {
- ($add_status, $add_result) = pf::domain::add_computer("-no-add", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $baseDN, $workgroup, $workgroup, $bind_dn, $bind_pass);
+ ($add_status, $add_result) = pf::domain::add_computer("-delete", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $dns_name, $workgroup, $ou, $bind_dn, $bind_pass);
+ if ($add_status == $FALSE) {
+ $self->render_error(422, "Unable to add machine account: removing existing machine account failed with following error: $add_result");
+ return 0;
+ }
+ ($add_status, $add_result) = pf::domain::add_computer(" ", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $dns_name, $workgroup, $ou, $bind_dn, $bind_pass);
if ($add_status == $FALSE) {
- $self->render_error(422, "Unable to add machine account with following error: $add_result");
+ $self->render_error(422, "Unable to add machine account: recreating machine account with following error: $add_result");
return 0;
}
}
@@ -188,6 +191,7 @@ sub update {
my $dns_name = $new_item->{dns_name};
my $workgroup = $old_item->{workgroup};
my $real_computer_name = $old_item->{server_name};
+ my $ou = $new_item->{ou};
if ($computer_name eq "%h") {
$real_computer_name = hostname();
@@ -224,24 +228,25 @@ sub update {
}
if (!is_nt_hash_pattern($new_data->{machine_account_password}) && ($new_data->{machine_account_password} ne $old_item->{machine_account_password})) {
- my $baseDN = $dns_name;
- $baseDN = generate_baseDN($dns_name);
-
- my ($add_status, $add_result) = pf::domain::add_computer("-no-add", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $baseDN, $workgroup, $workgroup, $bind_dn, $bind_pass);
+ my ($add_status, $add_result) = pf::domain::add_computer("-delete", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $dns_name, $workgroup, $ou, $bind_dn, $bind_pass);
if ($add_status == $FALSE) {
- if ($add_result =~ /Account.+not found in/) {
- ($add_status, $add_result) = pf::domain::add_computer(" ", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $baseDN, $workgroup, $workgroup, $bind_dn, $bind_pass);
- if ($add_status == $FALSE) {
- $self->render_error(422, "Unable to add machine account with following error: $add_result");
- return 0;
- }
- }
- else {
- $self->render_error(422, "Unable to add machine account with following error: $add_result");
+ unless ($add_result =~ /Account (.+) not found in/) {
+ $self->render_error(422, "Unable to update - remove existing machine account with following error: $add_result");
return 0;
}
}
+
+ ($add_status, $add_result) = pf::domain::add_computer(" ", $real_computer_name, $computer_password, $ad_server_ip, $ad_server_host, $dns_name, $workgroup, $ou, $bind_dn, $bind_pass);
+ if ($add_status == $FALSE) {
+ $self->render_error(422, "Unable to add machine account with following error: $add_result");
+ return 0;
+ }
+
$new_data->{machine_account_password} = md4_hex(encode("utf-16le", $new_data->{machine_account_password}));
+ $new_data->{ou} = $new_item->{ou}
+ }
+ else {
+ $new_data->{ou} = $old_item->{ou}
}
$new_data->{server_name} = $computer_name;
@@ -255,19 +260,6 @@ sub update {
$self->render(status => 200, json => $self->update_response($form));
}
-sub generate_baseDN {
- my $ret = "";
-
- my ($dns_name) = @_;
- my @array = split(/\./, $dns_name);
-
- foreach my $element (@array) {
- $ret .= "DC=$element,";
- }
- $ret =~ s/,$//;
- return $ret;
-}
-
sub is_nt_hash_pattern {
my ($password) = @_;
$password =~ s/^\s+|\s+$//g;
diff --git a/lib/pf/domain.pm b/lib/pf/domain.pm
index b164468b2237..801be32809ac 100644
--- a/lib/pf/domain.pm
+++ b/lib/pf/domain.pm
@@ -26,7 +26,7 @@ use Encode qw(encode);
use File::Slurp;
# This is to create the templates for the domain info
-our $TT_OPTIONS = {ABSOLUTE => 1};
+our $TT_OPTIONS = { ABSOLUTE => 1 };
our $template = Template->new($TT_OPTIONS);
our $ADD_COMPUTERS_BIN = '/usr/local/pf/bin/impacket-addcomputer';
@@ -43,7 +43,7 @@ sub run {
my $result = `$cmd`;
my $code = $? >> 8;
- return ($code , $result);
+ return ($code, $result);
}
=head2 test_join
@@ -54,16 +54,32 @@ Executes the command in the OS to test the domain join
sub add_computer {
my $option = shift;
- my ($computer_name, $computer_password, $domain_controller_ip, $domain_controller_host, $baseDN, $computer_group, $workgroup, $bind_dn, $bind_pass) = @_;
+ my ($computer_name, $computer_password, $domain_controller_ip, $domain_controller_host, $dns_name, $workgroup, $ou, $bind_dn, $bind_pass) = @_;
+
+ if (!defined($ou)) {
+ $ou = ""
+ }
+
+ $ou =~ s/^\s+|\s+$//g;
+ $ou =~ s/^['"]|['"]$//g;
+
+ my $method = "LDAPS";
+ if (uc($ou) eq "COMPUTERS" || $ou eq "") {
+ $method = "SAMR"
+ }
$computer_name = escape_bind_user_string($computer_name) . "\$";
$computer_password = escape_bind_user_string($computer_password);
- my $domain_auth = escape_bind_user_string("$workgroup/$bind_dn");
- my $nt_hash = md4_hex(encode("utf-16le", $bind_pass));
+ my $domain_auth = escape_bind_user_string("$dns_name/$bind_dn:$bind_pass");
+ my $baseDN = generate_base_dn($dns_name);
+ my $computer_group = generate_computer_group($dns_name, $ou);
+
+ $baseDN = escape_bind_user_string($baseDN);
+ $computer_group = escape_bind_user_string($computer_group);
my $result;
eval {
- my $command = "$ADD_COMPUTERS_BIN -computer-name $computer_name -computer-pass '$computer_password' -dc-ip $domain_controller_ip -dc-host '$domain_controller_host' -baseDN '$baseDN' -computer-group $computer_group '$domain_auth' -hashes ':$nt_hash' $option";
+ my $command = "$ADD_COMPUTERS_BIN -computer-name '$computer_name' -computer-pass '$computer_password' -dc-ip $domain_controller_ip -dc-host '$domain_controller_host' -baseDN '$baseDN' -computer-group '$computer_group' '$domain_auth' $option -method=$method";
$result = pf_run($command, accepted_exit_status => [ 0 ]);
};
if ($@) {
@@ -92,7 +108,7 @@ sub add_computer {
=head2 escape_bind_user_string
Escapes the bind user string for any simple quote
-
+' -> '\''
=cut
sub escape_bind_user_string {
@@ -101,7 +117,40 @@ sub escape_bind_user_string {
return $s;
}
+sub generate_base_dn {
+ my $ret = "";
+
+ my ($dns_name) = @_;
+ my @array = split(/\./, $dns_name);
+
+ foreach my $element (@array) {
+ $ret .= "DC=$element,";
+ }
+ $ret =~ s/,$//;
+ return $ret;
+}
+
+sub generate_computer_group {
+ my $ret = "";
+ my ($dns_name, $ou) = @_;
+ my $base_dn = generate_base_dn($dns_name);
+
+ # for OU=Computer or OU="", we put the machine account to CN=Computers.
+ if (!defined($ou) || uc($ou) eq "COMPUTERS" || $ou eq "") {
+ return "CN=Computers," . $base_dn;
+ }
+
+ # Handle real OU strings
+ my @array = split(/\//, $ou);
+ my $dn_ou = "";
+
+ foreach my $element (@array) {
+ $dn_ou = "OU=$element," . $dn_ou;
+ }
+ $dn_ou =~ s/,$//;
+ return $dn_ou . ",$base_dn";
+}