Files
Audit-gpresult-machine/setup.py
T

287 lines
6.8 KiB
Python

# -*- coding: utf-8 -*-
"""Audit GPResult machine : resultat structure dans audit-gpresult.computer-gpo.
L'installation est vide. La collecte, l'analyse XML et l'enregistrement
des donnees sont realises uniquement lors de l'execution de audit().
"""
from setuphelpers import *
import os
import tempfile
import xml.etree.ElementTree as ET
# Categorie et cle des donnees d'audit WAPT.
AUDIT_SECTION = "audit-gpresult"
AUDIT_NAME = "computer-gpo"
def install():
"""Point d'entree d'installation ; aucune configuration a appliquer."""
pass
def _clean_tag(tag):
"""Retire l'espace de noms XML eventuel d'une balise."""
return tag.split("}")[-1]
def _text(element, tag, default=""):
"""Lit le premier texte non vide d'une balise dans tout le sous-arbre.
La recherche est recursive et insensible a la casse. Si aucune valeur
n'est trouvee, retourne la valeur par defaut.
"""
if element is None:
return default
for child in element.iter():
if _clean_tag(child.tag).lower() == tag.lower():
if child.text:
return child.text.strip()
return default
def _to_bool(value):
"""Convertit les valeurs reconnues en True ; toute autre valeur est False."""
return str(value).strip().lower() in (
"true",
"1",
"yes",
"oui"
)
def _find_computer_results(root):
"""Recherche la section machine sans dependre des espaces de noms XML."""
for element in root.iter():
if _clean_tag(element.tag) == "ComputerResults":
return element
return None
def _get_gpos(computer_results):
"""Classe les GPO machine en appliquees et non appliquees.
Les doublons sont elimines selon le triplet nom/version annuaire/
version SYSVOL. Les GPO sans nom sont ignorees.
"""
applied = []
denied = []
seen = set()
if computer_results is None:
return applied, denied
# Conserver la recherche recursive de la version de reference.
for element in computer_results.iter():
if _clean_tag(element.tag) != "GPO":
continue
name = _text(element, "Name")
if not name:
continue
enabled = _to_bool(
_text(element, "Enabled")
)
valid = _to_bool(
_text(element, "IsValid")
)
filter_allowed = _to_bool(
_text(element, "FilterAllowed")
)
access_denied = _to_bool(
_text(element, "AccessDenied")
)
version_directory = _text(
element,
"VersionDirectory"
)
version_sysvol = _text(
element,
"VersionSysvol"
)
identity = (
name,
version_directory,
version_sysvol
)
if identity in seen:
continue
seen.add(identity)
# Une GPO est appliquee uniquement si les trois indicateurs
# positifs sont vrais et si aucun refus d'acces n'est indique.
# denied regroupe toutes les autres entrees retenues.
if (
enabled
and valid
and filter_allowed
and not access_denied
):
applied.append(name)
else:
denied.append(name)
return applied, denied
def audit():
"""Exporte le GPResult machine, enregistre les donnees et retourne un statut."""
# Nom temporaire fixe conserve de la version fournie.
xml_file = os.path.join(
tempfile.gettempdir(),
"wapt-gpresult-computer.xml"
)
try:
# Eviter de lire le rapport d'une execution precedente.
if os.path.isfile(xml_file):
os.remove(xml_file)
# /SCOPE COMPUTER cible les GPO machine ; /X produit le XML ;
# /F autorise le remplacement du fichier de destination.
command = '"%s" /SCOPE COMPUTER /X "%s" /F' % (
makepath(system32(), "gpresult.exe"),
xml_file
)
print("Running GPResult COMPUTER...")
run(
command,
timeout=120,
return_stderr=False
)
if not os.path.isfile(xml_file):
raise Exception(
"GPResult did not generate XML report"
)
tree = ET.parse(xml_file)
root = tree.getroot()
computer_results = _find_computer_results(root)
if computer_results is None:
raise Exception(
"ComputerResults section not found"
)
applied_gpos, denied_gpos = _get_gpos(
computer_results
)
# Essayer DomainName, puis Domain si aucune valeur n'est trouvee.
domain = _text(
computer_results,
"DomainName"
)
if not domain:
domain = _text(
computer_results,
"Domain"
)
audit_data = {
"status": "OK",
"computer": {
"name": os.environ.get(
"COMPUTERNAME",
""
),
"domain": domain,
"site": _text(
computer_results,
"Site"
),
},
"summary": {
"applied_gpo_count": len(applied_gpos),
"denied_gpo_count": len(denied_gpos),
"total_gpo_count": (
len(applied_gpos)
+ len(denied_gpos)
),
},
"applied_gpos": applied_gpos,
"denied_gpos": denied_gpos,
}
# Transmettre le dictionnaire directement pour conserver une
# donnee structuree, plutot qu'une chaine de texte JSON.
WAPT.write_audit_data_if_changed(
AUDIT_SECTION,
AUDIT_NAME,
audit_data,
keep_days=365,
max_count=1
)
print(
"GPResult COMPUTER: %s applied / %s denied"
% (
len(applied_gpos),
len(denied_gpos)
)
)
return "OK"
except Exception as e:
# La meme cle est mise a jour avec l'erreur : les anciennes
# listes ne sont pas recopiees comme resultat de cet audit.
WAPT.write_audit_data_if_changed(
AUDIT_SECTION,
AUDIT_NAME,
{
"status": "ERROR",
"message": str(e)
},
keep_days=365,
max_count=1
)
print(
"GPResult COMPUTER audit error: %s"
% str(e)
)
return "ERROR"
finally:
# Nettoyage tente apres succes comme apres erreur.
# Un echec de suppression est ignore dans la version conservee.
try:
if os.path.isfile(xml_file):
os.remove(xml_file)
except:
pass