whos_lc.py 9.95 KB
Newer Older
1
#!/bin/bash /usr/scripts/python.sh
2
# -*- mode: python; coding: utf-8 -*-
3 4 5 6 7
#
# Auteur : Pierre-Elliott Bécue <becue@crans.org>
# Licence : GPLv3
# Date : 01/09/2014

8
import sys
9
import argparse
10 11
import re
import time
12 13
from ldap import SIZELIMIT_EXCEEDED

14
from gestion.config import encoding
15 16
from gestion import affichage
from gestion import annuaires_pg
17

18
import lc_ldap.shortcuts
19 20 21 22
import lc_ldap.objets
import lc_ldap.filter2 as lfilter
import lc_ldap.crans_utils

23
ENCODING = encoding.in_encoding
24

25 26 27 28
MACRO_FILTRE_PRISE = re.compile(r'\(prise=(?P<prise>[a-zA-Z][0-9]{3})\)')
MACRO_FILTRE_ADHESION = re.compile(r'\(adhesion=ok\)')
MACRO_FILTRE_CONNEXION = re.compile(r'\(connexion=ok\)')

29 30 31 32 33
def __help(parser, retcode=0):
    """Pour éviter la redondance de code"""
    parser.print_help()
    sys.exit(retcode)

34 35
def print_data(data, args):
    """Récupère les données et les affiche"""
36 37
    dataLen = sum([len(elem) for elem in data.itervalues()])
    if dataLen:
38
        for (key, elem) in data.iteritems():
39 40 41
            if len(elem) == 0:
                continue

42 43
            _header = affichage.style(u"Résultats de type %s trouvés dans la base." % (key,), ['cyan'])
            print _header.encode(ENCODING)
44
            if len(elem) == 1:
45 46 47 48 49 50 51 52
                elem[0].display(
                    historique=args.historique,
                    blacklist=args.blacklist,
                    disp_adresse=args.adresse,
                    disp_telephone=args.telephone,
                    sshfp=args.sshfp,
                    ipsec=args.ipsec,
                    )
53
            else:
54
                print lc_ldap.printing.sprint_list(elem).encode(ENCODING)
55 56
                print "%s résultats" % len(elem)

57
def explore_db(args, ldap):
58
    """Utilise le contenu de args pour décider comment explorer la base de données."""
59
    data = search_ldap(args, ldap)
60 61 62
    data = macro_expand(data, args)
    return data

63
def search_ldap(args, ldap):
64
    """Cherche et trie"""
65 66
    data = {}
    if args.ldap:
67
        search_filter = args.filtre.decode(ENCODING)
68
    else:
69 70
        search_filter = lfilter.human_to_ldap(args.filtre.decode(ENCODING))

71 72
    if args.macro_filtre:
        search_filter = filter_macro(search_filter)
73
    elif args.wild and '=' not in search_filter:
74
        search_filter = wild_search_filter(search_filter)
75

76
    try:
77
        resultats = ldap.search(search_filter, sizelimit=args.limite)
78
    except SIZELIMIT_EXCEEDED:
79
        raise EnvironmentError("La limite de résultats LDAP (%s) a été dépassée. Vous pouvez l'augmenter avec l'option -l" % (args.limite,))
80 81

    for elem in resultats:
82 83
        if elem not in data.get(elem.__class__.__name__, []):
            data.setdefault(elem.__class__.__name__, []).append(elem)
84

85 86
    return data

87 88 89 90 91
def macro_expand(data, args):
    """Gère les macros spécifiques à whos_lc.
    Permet de récupérer les propriétaires d'objets cherchés,
    ou les factures ou les machines. De chercher par prise,
    etc etc"""
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

    new_data = dict(data)

    if args.proprietaire:
        for elem_list in data.itervalues():
            for elem in elem_list:
                if hasattr(elem, 'proprio'):
                    _proprio = elem.proprio()

                    # On a pas besoin du Crans dans le dico
                    if isinstance(_proprio, lc_ldap.objets.AssociationCrans):
                        continue

                    if _proprio not in data.get(_proprio.__class__.__name__, []):
                        new_data.setdefault(_proprio.__class__.__name__, []).append(_proprio)
        args.adherent = True

    return new_data

def filter_macro(filtre):
    """Étend les macros du filtre passé en argument"""

    thetime = lc_ldap.crans_utils.to_generalized_time_format(time.time())

    prise_match = MACRO_FILTRE_PRISE.match(filtre)
    adhesion_match = MACRO_FILTRE_ADHESION.match(filtre)
    connexion_match = MACRO_FILTRE_CONNEXION.match(filtre)

    if prise_match:
        prise = prise_match.groupdict()['prise']
        bat = prise[0]
        chbre = bat + annuaires_pg.reverse(bat, prise[1:])[0]
        filtre = filtre.replace(u"(prise=%s)" % (prise,), u"(chbre=%s)" % (chbre,))

    if adhesion_match:
        args.limite = 0
        filtre = filtre.replace(u"(adhesion=ok)", u"(&(finAdhesion>=%s)(|(aid=*)(cid=*)))" % (thetime,))

    if connexion_match:
        args.limite = 0
        filtre = filtre.replace(u"(connexion=ok)", u"(&(finConnexion>=%s)(|(aid=*)(cid=*)))" % (thetime,))

    return filtre
135

136 137 138 139 140
def wild_search_filter(filtre):
    """Recherche sur un gros volume d'attributs dans la base de données"""

    kw = filtre[1:-1]

141 142 143 144
    attr_list = [
        u'nom', u'prenom', u'tel', u'mail', u'chbre',
        u'mailAlias', u'canonicalAlias', u'mailExt',
        u'uid', u'host', u'hostAlias', u'ip6HostNumber',
145
        u'macAddress', u'ipHostNumber',
146 147 148
    ]

    filtre = u"(|"
149
    for elem in attr_list:
150 151 152 153 154
        filtre += u"(%s=%s*)" % (elem, kw)
    filtre += u")"

    return filtre

155
def limits(data, args):
156
    """Applique les limitations dans la recherche.
157
    Les cas sont a priori conflictuels.
158

159 160 161 162
    """
    data_restricted = {}
    data_restricted.update(data)
    contentFilter = []
163

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
    if args.adherent:
        contentFilter = ["adherent"]
    elif args.club:
        contentFilter = ["club"]
    elif args.machine:
        contentFilter = ["machineFixe", "machineWifi", "machineCrans", "switchCrans", "borneWifi"]
    elif args.crans:
        contentFilter = ["machineCrans", "switchCrans", "borneWifi"]
    elif args.switch:
        contentFilter = ["switchCrans"]
    elif args.borne:
        contentFilter = ["borneWifi"]
    # Special cases.
    elif args.adm:
        contentFilter = []
        out = []
        for machine in data.get('machineCrans', []):
            if lc_ldap.crans_utils.find_rid_plage(machine['rid'][0].value)[0].startswith('adm'):
                out.append(machine)
        data_restricted = {'machineCrans' : out,}
    elif args.special:
        contentFilter = []
        out = []
        for machine in data.get('machineCrans', []):
            if lc_ldap.crans_utils.find_rid_plage(machine['rid'][0].value)[0].startswith('special'):
                out.append(machine)
        data_restricted = {'machineCrans' : out,}
    elif args.serveur:
        contentFilter = []
        out = []
        for machine in data.get('machineCrans', []):
            if lc_ldap.crans_utils.find_rid_plage(machine['rid'][0].value)[0].startswith('serveur'):
                out.append(machine)
        data_restricted = {'machineCrans' : out,}
198

199 200
    if contentFilter:
        data_restricted = {a: data.get(a, []) for a in contentFilter}
201

202 203 204
    return data_restricted

if __name__ == "__main__":
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    PARSER = argparse.ArgumentParser(description="Recherche dans la base des adhérents", add_help=False)
    PARSER.add_argument('-A', '--adresse', help="Affiche l'adresse de l'adhérent.", action="store_true")
    PARSER.add_argument('-d', '--blacklist', type=int, help="Choix du nombre d'entrées blacklist à afficher pour les entrées détaillées.", action="store", default=10)
    PARSER.add_argument('-h', '--help', help="Affiche ce message et quitte.", action="store_true")
    PARSER.add_argument('-i', '--ipsec', help="Affichage de la clef wifi de la machine.", action="store_true")
    PARSER.add_argument('-l', '--limite', type=int, help="Modifier la taille limite de recherche dans la base LDAP", action="store", default=1000)
    PARSER.add_argument('-L', '--historique', type=int, help="Choix du nombre d'entrées d'historique à afficher pour les entrées détaillées.", action="store", default=10)
    PARSER.add_argument('-s', '--sshfp', help="Affiche les fingerprint SSH si elles existent.", action="store_true")
    PARSER.add_argument('-t', '--ldap', help="Utiliser les filtres tels que définis dans ldap", action="store_true")
    PARSER.add_argument('-T', '--telephone', help="Afficher le numéro de téléphone de l'adhérent.", action="store_true")
    PARSER.add_argument('--test', help="Se connecter à la base de test", action="store_true")
    PARSER.add_argument('-v', '--verbose', help="Rend le script (très) verbeux.", action="store_true")
    PARSER.add_argument('filtre', type=str, nargs="?", help="Le filtre whos à utiliser")

    MACRO_GROUP = PARSER.add_mutually_exclusive_group(required=False)
    MACRO_GROUP.add_argument('-f', '--macro-filtre', help="Flag activant la gestion des macros pour le filtre LDAP.", action="store_true")
    MACRO_GROUP.add_argument('-w', '--wild', help="Cherche de façon agressive dans la base de données à partir du filtre", action="store_true")

    TYPE_GROUP = PARSER.add_mutually_exclusive_group(required=False)
    TYPE_GROUP.add_argument('-a', '--adherent', help="Limite l'affichage aux adhérents.", action="store_true")
    TYPE_GROUP.add_argument('--adm', help="Limite l'affichage aux machines adm.", action="store_true")
    TYPE_GROUP.add_argument('-b', '--borne', help="Limite l'affichage aux bornes.", action="store_true")
    TYPE_GROUP.add_argument('-c', '--club', help="Limite l'affichage aux clubs.", action="store_true")
    TYPE_GROUP.add_argument('--crans', help="Limite l'affichage aux machines crans.", action="store_true")
    TYPE_GROUP.add_argument('-m', '--machine', help="Limite l'affichage aux machines.", action="store_true")
    TYPE_GROUP.add_argument('-P', '--proprietaire', help="Récupère le propriétaire de l'objet cherché.", action="store_true")
    TYPE_GROUP.add_argument('--serveur', help="Limite l'affichage aux serveurs.", action="store_true")
    TYPE_GROUP.add_argument('--special', help="Limite l'affichage aux machines spéciales.", action="store_true")
    TYPE_GROUP.add_argument('--switch', help="Limite l'affichage aux switches (pas encore implémenté).", action="store_true")

    ARGS = PARSER.parse_args()

    if ARGS.help:
        __help(PARSER)

240 241 242 243
    if not ARGS.filtre:
        import this
        __help(PARSER, 1)

244
    if ARGS.test:
245
        LDAP = lc_ldap.shortcuts.lc_ldap_test()
246
    else:
247 248
        LDAP = lc_ldap.shortcuts.lc_ldap_readonly()

249 250 251
    DATA = explore_db(ARGS, LDAP)
    DATA = limits(DATA, ARGS)
    print_data(DATA, ARGS)