lc_ldap/filter.py
2013-04-08 13:06:47 +02:00

257 lines
8.1 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#/usr/bin/env python
# -*- coding: utf8 -*-
#
# filter.py --- Workaround with ldap filters
#
# Redraw filters like attr1=val1&attr2=val2|attr3=val3
# in (|(&(attr1=val1)(attr2=val2))(attr3=val3))
#
# Copyright (C) 2010-2013 Cr@ns <roots@crans.org>
# Author: Pierre-Elliott Bécue <becue@crans.org>
#
#
# License: WTFPL
def human_to_ldap(filtre):
"""
Transforme les filtres "human readables" en
filtres respectant la syntaxe LDAP.
"""
# Piles, quand on croise une parenthèse ouvrante
# on change de pile
stacks = {0:''}
# Position dans la pile
pos = 0
# operateur \in {&, |, &|, }
operateur = ""
# La pile externe, qu'on merge à la pile en cours quand c'est utile.
ext_stack = ""
# Élement de la forme paiement=2012 ou retour de la pile
# supérieure (quand on croise une parenthèse fermante, la
# pile est dumpée dans argument)
# Est systématiquement dumpé dans ext_stack quand on croise
# un opérateur.
argument = ""
# Y a-t-il un ! dans la salle ?
neg = False
# Quand on quitte une pile parenthésée, on veut savoir quel était
# l'opérateur actif avant, pour que le comportement de la fonction
# soit défini.
anciens_operateurs = []
for char in filtre:
if char == "(":
# Une nouvelle stack ne démarre que si le dernier argument a été dumpé
# dans l'ext stack (si ce n'est pas le cas quand on respecte la syntaxe
# des filtres, la fonction est mal codée, donc on plante).
if argument != "":
raise ValueError('Argument entammé non terminé !')
# Un dumpe ext_stack dans la stack en cours.
stacks[pos] += "%s" % (ext_stack)
# On augmente le conteur de la stack
pos = pos + 1
# On (ré)?initialise la stack
stacks[pos] = ''
# On stocke l'opérateur en cours dans la stack inférieure dans
# une liste.
anciens_operateurs.append(operateur)
# On flush operateur
operateur = ""
# On flush ext_stack, l'environnement est enfin propre pour
# bosser dans la nouvelle pile.
ext_stack = ""
elif char == ")":
# Cas classique
if operateur == "|":
ext_stack += "(%s)" % argument
# Moins classique, &| est un opérateur servant à dire qu'on
# a déjà croisé un | dans la stack en cours, le cas échéant,
# celui-ci doit être pris en compte, pour respecter la
# priorité du &. Dans ce cas seulement, une parenthèse
# est ouverte et non fermée (cf elif char == '&' cas
# operateur == "|"), on la ferme ici.
elif operateur == "&|":
argument += ")"
ext_stack += "(%s)" % argument
# Classique
elif operateur == "&":
ext_stack += "(%s)" % argument
# Pas d'opérateur, pas de parenthèse superflue.
else:
ext_stack += "%s" % argument
# On passe la stack en argument, les parenthèses
# l'encadrant seront placées d'un qu'un opérateur
# sera croisé.
argument = "%s%s" % (stacks[pos], ext_stack)
# Ménage
stacks[pos] = ""
ext_stack = ""
pos = pos - 1
# Retour à l'opérateur de la stack précédente.
operateur = anciens_operateurs.pop()
elif char == "|":
if not argument:
raise ValueError('Aucun argument')
# Ce cas permet d'éviter une répétition de la forme :
# (|(a)(|(b)(c))), quand on est déjà dans un ou, on
# rajoute juste l'argument suivant sans remettre de
# symbole.
if operateur == "|":
# neg est True si on a croisé un ! dans la chaîne.
# À améliorer pour qu'il ne marche que pour !=
if neg:
argument = "!(%s)" % argument
neg = False
# Ajout à la stack externe de l'argument
ext_stack += "(%s)" % argument
argument = ""
elif operateur == "&":
if neg:
argument = "!(%s)" % argument
neg = False
ext_stack += "(%s)" % argument
# | prend le relais sur &, on dumpe ext_stack et on commence une nouvelle
# chaîne qui sera ajoutée à droite. Le ou va tout à gauche de la stack
# en cours, pour s'appliquer sur tout son contenu.
stacks[pos] = "%s(%s%s)" % (char, stacks[pos], ext_stack)
ext_stack = ""
argument = ""
operateur = "|"
# C'est un & dans un |, donc on ferme juste la chaîne
# des &, d'où la parenthèse fermante en trop.
elif operateur == "&|":
if neg:
argument = "!(%s)" % argument
neg = False
ext_stack += "(%s)" % argument
argument = ""
stacks[pos] = "%s%s)" % (stacks[pos], ext_stack)
ext_stack = ""
operateur = "|"
# Pas encore d'opérateur annoncé
elif operateur == "":
if neg:
argument = "!(%s)" % argument
neg = False
ext_stack += "%s" % argument
argument = ""
# ouverture
stacks[pos] = "%s(%s%s)" % (char, stacks[pos], ext_stack)
ext_stack = ""
operateur = "|"
else:
raise TypeError('Erreur d\'opérateur.')
elif char == "&":
if not argument:
raise ValueError('Aucun argument')
if operateur == "&":
if neg:
argument = "!(%s)" % argument
neg = False
ext_stack += "(%s)" % argument
argument = ""
# Le cas spécial, on ouvre un & après un |, donc pour respecter
# la priorité de &, on démarre une nouvelle chaîne, mais dans
# l'ext_stack (contrairement à char == '|', operateur == "&")
elif operateur == "|":
if neg:
argument = "!(%s)" % argument
neg = False
ext_stack += "(%s(%s)" % (char, argument)
argument = ""
operateur = "&|"
# On était déjà dans un &|...
elif operateur == "&|":
if neg:
argument = "!(%s)" % argument
neg = False
ext_stack += "(%s)" % argument
argument = ""
# Comme ci-dessus
elif operateur == "":
if neg:
argument = "!(%s)" % argument
neg = False
ext_stack += "%s" % argument
argument = ""
stacks[pos] = "%s(%s%s)" % (char, stacks[pos], ext_stack)
ext_stack = ""
operateur = "&"
else:
raise TypeError('Erreur d\'opérateur.')
elif char == "!":
neg = True
# Remplissage d'argument
else:
argument += char
# Décommenter pour débug.
# En modifiant un peu, encore plus utile pour savoir ce qu'il
# fait à chaque étape !
#print stacks
#print pos, argument, ext_stack, operateur
if pos > 0:
raise Exception("Tu ne sais pas parenthéser, crétin.")
else:
# Comme parenthèse fermante.
if neg:
argument = "!(%s)" % argument
# Surtout ça
if operateur == "&|":
argument += ')'
ext_stack += "(%s)" % argument
argument = ""
stacks[pos] = "(%s%s)" % (stacks[pos], ext_stack)
ext_stack = ""
# On retourne la pile de plus haut niveau
return stacks[0]