|
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
|
|
# Date update: 14.11.2023
|
|
|
|
|
# Email: aleksandr.egai@red-soft.ru
|
|
|
|
|
# (c) RED SOFT
|
|
|
|
|
|
|
|
|
|
import sys,re,socket,subprocess,os
|
|
|
|
|
from PyQt5.QtWidgets import QApplication, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QCheckBox, QTabWidget, QMessageBox
|
|
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
|
|
|
|
|
|
class DomainForm(QWidget):
|
|
|
|
|
def __init__(self, width, height, show_parameters_tab):
|
|
|
|
|
QWidget.__init__(self)
|
|
|
|
|
self.width = width
|
|
|
|
|
self.height = height
|
|
|
|
|
self.show_parameters_tab = show_parameters_tab
|
|
|
|
|
self.initUI()
|
|
|
|
|
|
|
|
|
|
def initUI(self):
|
|
|
|
|
self.setFixedSize(self.width, self.height)
|
|
|
|
|
|
|
|
|
|
layout = QVBoxLayout()
|
|
|
|
|
|
|
|
|
|
tab_widget = QTabWidget(self)
|
|
|
|
|
|
|
|
|
|
# Вкладка для основных данных
|
|
|
|
|
tab1 = QWidget()
|
|
|
|
|
tab_widget.addTab(tab1, "Основные данные")
|
|
|
|
|
|
|
|
|
|
# Определяем имя домена
|
|
|
|
|
domain = None
|
|
|
|
|
with open("/etc/resolv.conf", "r") as resolv_file:
|
|
|
|
|
for line in resolv_file:
|
|
|
|
|
if line.strip().startswith("search"):
|
|
|
|
|
domain = line.split()[1]
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
self.label_domain = QLabel("Имя домена:")
|
|
|
|
|
self.entry_domain = QLineEdit(self)
|
|
|
|
|
self.entry_domain.setText(domain)
|
|
|
|
|
|
|
|
|
|
self.label_name_pc = QLabel("Имя компьютера:")
|
|
|
|
|
self.entry_name_pc = QLineEdit(self)
|
|
|
|
|
self.entry_name_pc.setMaxLength(15)
|
|
|
|
|
self.entry_name_pc.setText(socket.gethostname().split(".")[0])
|
|
|
|
|
|
|
|
|
|
self.label_admin = QLabel("Имя администратора домена:")
|
|
|
|
|
self.entry_admin = QLineEdit(self)
|
|
|
|
|
|
|
|
|
|
self.label_password = QLabel("Пароль администратора домена:")
|
|
|
|
|
self.entry_password = QLineEdit(self)
|
|
|
|
|
self.entry_password.setEchoMode(QLineEdit.Password) # Скрыть ввод пароля звёздочками
|
|
|
|
|
|
|
|
|
|
tab_layout1 = QVBoxLayout()
|
|
|
|
|
tab_layout1.addWidget(self.label_domain)
|
|
|
|
|
tab_layout1.addWidget(self.entry_domain)
|
|
|
|
|
tab_layout1.addWidget(self.label_name_pc)
|
|
|
|
|
tab_layout1.addWidget(self.entry_name_pc)
|
|
|
|
|
tab_layout1.addWidget(self.label_admin)
|
|
|
|
|
tab_layout1.addWidget(self.entry_admin)
|
|
|
|
|
tab_layout1.addWidget(self.label_password)
|
|
|
|
|
tab_layout1.addWidget(self.entry_password)
|
|
|
|
|
tab1.setLayout(tab_layout1)
|
|
|
|
|
|
|
|
|
|
if self.show_parameters_tab:
|
|
|
|
|
# Вкладка для параметров, если show_parameters_tab == True
|
|
|
|
|
tab2 = QWidget()
|
|
|
|
|
tab_widget.addTab(tab2, "Параметры")
|
|
|
|
|
|
|
|
|
|
self.checkbox1 = QCheckBox("Параметр 1")
|
|
|
|
|
self.checkbox1.setChecked(True) # Активировать параметр
|
|
|
|
|
self.checkbox2 = QCheckBox("Параметр 2")
|
|
|
|
|
self.checkbox3 = QCheckBox("Параметр 3")
|
|
|
|
|
|
|
|
|
|
tab_layout2 = QVBoxLayout()
|
|
|
|
|
tab_layout2.addWidget(self.checkbox1)
|
|
|
|
|
tab_layout2.addWidget(self.checkbox2)
|
|
|
|
|
tab_layout2.addWidget(self.checkbox3)
|
|
|
|
|
tab2.setLayout(tab_layout2)
|
|
|
|
|
|
|
|
|
|
layout.addWidget(tab_widget)
|
|
|
|
|
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
button_layout.setAlignment(Qt.AlignHCenter)
|
|
|
|
|
|
|
|
|
|
self.close_button = QPushButton("Закрыть")
|
|
|
|
|
self.submit_button = QPushButton("ОК")
|
|
|
|
|
|
|
|
|
|
self.close_button.setFixedWidth(100)
|
|
|
|
|
self.close_button.setFixedHeight(40)
|
|
|
|
|
self.close_button.clicked.connect(self.close)
|
|
|
|
|
|
|
|
|
|
self.submit_button.setFixedWidth(100)
|
|
|
|
|
self.submit_button.setFixedHeight(40)
|
|
|
|
|
self.submit_button.clicked.connect(self.on_submit)
|
|
|
|
|
|
|
|
|
|
button_layout.addWidget(self.close_button)
|
|
|
|
|
button_layout.addWidget(self.submit_button)
|
|
|
|
|
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
|
|
|
|
def is_valid_name_pc(self, name_pc):
|
|
|
|
|
pattern = r'^(?:[a-zA-Z0-9](?:(?:[a-zA-Z0-9\-]){0,14}[a-zA-Z0-9\-])+[a-zA-Z0-9])$'
|
|
|
|
|
if re.match(pattern, name_pc) and len(name_pc) <= 15:
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def check_domain_name(self, admin, domain, name_pc, password):
|
|
|
|
|
has_errors = False # Флаг для отслеживания ошибок
|
|
|
|
|
try:
|
|
|
|
|
check_command = f"adcli show-computer -U {admin} --domain={domain} {name_pc} --stdin-password"
|
|
|
|
|
|
|
|
|
|
# Открываем файлы здесь
|
|
|
|
|
with open("/tmp/join_check.txt", "w") as output_file, \
|
|
|
|
|
open("/tmp/join_check.txt", "r") as check_file, \
|
|
|
|
|
open("/var/log/join-to-domain.log", "a") as log_file:
|
|
|
|
|
|
|
|
|
|
process = subprocess.Popen(check_command, shell=True, stdin=subprocess.PIPE, stdout=output_file, stderr=subprocess.STDOUT, text=True)
|
|
|
|
|
process.communicate(input=password)
|
|
|
|
|
|
|
|
|
|
# Считываем содержимое /tmp/join_check.txt
|
|
|
|
|
v_check = check_file.read()
|
|
|
|
|
|
|
|
|
|
log_file.write("Проверка аутентификации в домене:\n")
|
|
|
|
|
log_file.write(v_check)
|
|
|
|
|
log_file.flush() # Очистить буфер вывода
|
|
|
|
|
|
|
|
|
|
if "sAMAccountName" in v_check:
|
|
|
|
|
message = f"Ошибка! В домене уже существует компьютер {name_pc}\n"
|
|
|
|
|
log_message = f"Ошибка! В домене уже существует компьютер {name_pc}"
|
|
|
|
|
log_file.write(log_message)
|
|
|
|
|
log_file.write("\n")
|
|
|
|
|
QMessageBox.critical(self, "Ошибка!", message)
|
|
|
|
|
has_errors = True
|
|
|
|
|
if "Couldn't authenticate" in v_check:
|
|
|
|
|
message = "Неверное имя администратора домена или пароль!"
|
|
|
|
|
log_message = "Ошибка! Неверное имя администратора домена или пароль!"
|
|
|
|
|
log_file.write(log_message)
|
|
|
|
|
QMessageBox.critical(self, "Ошибка!", message)
|
|
|
|
|
has_errors = True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error: {e}")
|
|
|
|
|
has_errors = True
|
|
|
|
|
return has_errors # Возвращаем флаг ошибок
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def on_submit(self):
|
|
|
|
|
log_file_path = '/var/log/join-to-domain.log'
|
|
|
|
|
domain = self.entry_domain.text()
|
|
|
|
|
name_pc = self.entry_name_pc.text()
|
|
|
|
|
admin = self.entry_admin.text().split('@')[0]
|
|
|
|
|
password = self.entry_password.text()
|
|
|
|
|
|
|
|
|
|
param1 = self.checkbox1.isChecked() if self.show_parameters_tab else None
|
|
|
|
|
param2 = self.checkbox2.isChecked() if self.show_parameters_tab else None
|
|
|
|
|
param3 = self.checkbox3.isChecked() if self.show_parameters_tab else None
|
|
|
|
|
if not self.is_valid_name_pc(name_pc):
|
|
|
|
|
QMessageBox.critical(self, "Ошибка!", f"{name_pc} - это недопустимое имя компьютера.", QMessageBox.Ok)
|
|
|
|
|
with open(log_file_path, 'a') as log_file:
|
|
|
|
|
log_file.write(f"Ошибка! {name_pc} - это недопустимое имя компьютера." + '\n')
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if domain and name_pc and admin and password:
|
|
|
|
|
|
|
|
|
|
# Проверка доступности домена
|
|
|
|
|
try:
|
|
|
|
|
socket.gethostbyname(domain)
|
|
|
|
|
except socket.gaierror:
|
|
|
|
|
QMessageBox.critical(self, "Ошибка!", f"Домен {domain} недоступен! Проверьте настройки сети.")
|
|
|
|
|
with open(log_file_path, 'a') as log_file:
|
|
|
|
|
log_file.write(f"Ошибка! Домен {domain} недоступен! Проверьте настройки сети." + '\n')
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Получаем имя контроллера домена из adcli
|
|
|
|
|
def get_domain_controller(domain_name):
|
|
|
|
|
try:
|
|
|
|
|
result = subprocess.check_output(["adcli", "info", domain], stderr=subprocess.STDOUT, text=True)
|
|
|
|
|
return result
|
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
|
return str(e)
|
|
|
|
|
|
|
|
|
|
controller_info = get_domain_controller(domain)
|
|
|
|
|
|
|
|
|
|
# Парсинг информации о контроллере домена
|
|
|
|
|
controller_lines = [line.strip() for line in controller_info.split("\n")]
|
|
|
|
|
controller_name = None
|
|
|
|
|
for line in controller_lines:
|
|
|
|
|
if line.startswith("domain-controller = "):
|
|
|
|
|
controller_name = line.split(" = ")[1]
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if controller_name:
|
|
|
|
|
# Короткое имя домена
|
|
|
|
|
v_short_dc = controller_name.split(".")[0]
|
|
|
|
|
else:
|
|
|
|
|
QMessageBox.critical(self, "Ошибка!", "Информация о контроллере домена не найдена.")
|
|
|
|
|
with open(log_file_path, 'a') as log_file:
|
|
|
|
|
log_file.write(f"Ошибка! Информация о контроллере домена не найдена." + '\n')
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if v_short_dc == name_pc:
|
|
|
|
|
QMessageBox.critical(self, "Ошибка!", f"Имя компьютера {name_pc} не должно совпадать с именем контроллера домена!")
|
|
|
|
|
with open(log_file_path, 'a') as log_file:
|
|
|
|
|
log_file.write(f"Ошибка! Имя компьютера {name_pc} не должно совпадать с именем контроллера домена!" + '\n')
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if self.check_domain_name(admin, domain, name_pc, password):
|
|
|
|
|
return
|
|
|
|
|
result = f"{domain},{name_pc},{admin},{password},{param1},{param2},{param3}"
|
|
|
|
|
print(result) # Вывести результат в стандартный вывод (stdout)
|
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
self.close()
|
|
|
|
|
else:
|
|
|
|
|
QMessageBox.critical(self, "Ошибка!", "Заполните все поля, включая пароль!", QMessageBox.Ok)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
app = QApplication(sys.argv)
|
|
|
|
|
# Параметры окна
|
|
|
|
|
width = 420
|
|
|
|
|
height = 380
|
|
|
|
|
file_name = '/usr/bin/join-to-domain.sh'
|
|
|
|
|
|
|
|
|
|
# Поиск версии join-to-domain
|
|
|
|
|
version = None
|
|
|
|
|
if os.path.exists(file_name):
|
|
|
|
|
with open(file_name, 'r', encoding='utf-8') as file:
|
|
|
|
|
for line in file:
|
|
|
|
|
if "Версия:" in line:
|
|
|
|
|
version = line.split("Версия:", 1)[1].strip()
|
|
|
|
|
break
|
|
|
|
|
if version is None:
|
|
|
|
|
version=""
|
|
|
|
|
show_parameters_tab = False # True или False, чтобы показать/скрыть вкладку "Параметры"
|
|
|
|
|
form = DomainForm(width, height, show_parameters_tab)
|
|
|
|
|
form.setWindowTitle(f"Ввод в домен {version}")
|
|
|
|
|
|
|
|
|
|
form.show()
|
|
|
|
|
sys.exit(app.exec_())
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|