# -*- 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