From 22a59154ba39e6f7ff50fb0b7c99947dd716fe29 Mon Sep 17 00:00:00 2001 From: Daniel STAN <daniel.stan@crans.org> Date: Thu, 24 May 2012 12:16:01 +0200 Subject: [PATCH] Vers une version qui marche --- cranspasswords-server.py | 64 +++++++++------- cranspasswords.py | 156 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 190 insertions(+), 30 deletions(-) diff --git a/cranspasswords-server.py b/cranspasswords-server.py index 3cd8750..7c98e9f 100755 --- a/cranspasswords-server.py +++ b/cranspasswords-server.py @@ -2,6 +2,9 @@ # -*- encoding: utf-8 -*- """cranspasswords-server.py: Serveur pour cranspasswords""" +MYDIR = '/home/dstan/crans/cranspasswords/' +STORE = '/home/dstan/crans/passwords/v2/' + import glob import os import pwd @@ -15,7 +18,6 @@ if MYUID == 'root': KEYS = { "aza-vallina": ("Damien.Aza-Vallina@crans.org", None), "dandrimont": ("nicolas.dandrimont@crans.org", "66475AAF"), - "nicolasd": (None, None), "blockelet": ("blockelet@crans.org", "AF087A52"), "chambart": ("pierre.chambart@crans.org", "F2530FCE"), "dimino": ("jdimino@dptinfo.ens-cachan.fr", "2127F85A"), @@ -25,8 +27,32 @@ KEYS = { "lagorce": ("xavier.lagorce@crans.org", "0BF3708E"), "parret-freaud": ("parret-freaud@crans.org", "7D980513"), "tvincent": ("vincent.thomas@crans.org", "C5C4ACC0"), + "iffrig": ("iffrig@crans.org","5BEC9A2F"), + "becue": ("becue@crans.org", "194974E2"), + "dstan": ("daniel.stan@crans.org", "6E1C820B"), + "cauderlier": ("cauderlier@crans.org",None), #Méchant pas beau + "maioli": ("maioli@crans.org","9E5026E8") } +RTC=[ + "dandrimont", + "iffrig" + ] +NOUNOUS=RTC+[ + "blockelet", + "becue", + "dstan", + "chambart", + "dimino", + "durand-gasselin", + "glondu", + "huber", + "lagorce", + "parret-freaud", + "cauderlier", + "maioli" + ] + ROLES = { "bureau": [ "aza-vallina", @@ -37,31 +63,19 @@ ROLES = { "durand-gasselin", "lagorce", ], - "rtc": [ - "dandrimont", - "nicolasd", - ], - "nounou": [ - "blockelet", - "chambart", - "dandrimont", - "dimino", - "durand-gasselin", - "glondu", - "huber", - "lagorce", - "parret-freaud", - "tvincent", - ], + "rtc": RTC, + "nounous": NOUNOUS, + "nounous-w": NOUNOUS #Or maybe RTC ? } - -MYDIR = '/var/local/cranspasswords/' -STORE = MYDIR + 'store/' -def validate(roles): - """Valide que l'appelant appartient bien aux roles précisés""" + +def validate(roles,mode='r'): + """Valide que l'appelant appartient bien aux roles précisés + Si mode mode='w', recherche un rôle en écriture + """ for role in roles: - if MYUID in ROLES[role]: + if mode == 'w': role+='-w' + if ROLES.has_key(role) and MYUID in ROLES[role]: return True return False @@ -127,7 +141,7 @@ def putfile(filename): except TypeError: pass else: - if not validate(oldroles): + if not validate(oldroles,'w'): return False writefile(filepath, json.dumps({'roles': roles, 'contents': contents})) @@ -140,7 +154,7 @@ def rmfile(filename): except TypeError: return True else: - if validate(roles): + if validate(roles,'w'): os.remove(getpath(filename)) else: return False diff --git a/cranspasswords.py b/cranspasswords.py index fa2e5ae..a32336e 100755 --- a/cranspasswords.py +++ b/cranspasswords.py @@ -5,6 +5,9 @@ import sys import subprocess import json +import tempfile +import os +import atexit ###### ## GPG Definitions @@ -17,6 +20,9 @@ GPG_ARGS = { 'receive-keys': ['--recv-keys'], } +DEBUG=False +CLIPBOARD=False # Par défaut, place-t-on le mdp dans le presse-papier ? + def gpg(command, args = None): """Lance gpg pour la commande donnée avec les arguments donnés. Renvoie son entrée standard et sa sortie standard.""" @@ -24,11 +30,19 @@ def gpg(command, args = None): full_command.extend(GPG_ARGS[command]) if args: full_command.extend(args) + if DEBUG: + stderr=sys.stderr + else: + stderr=subprocess.PIPE + full_command.extend(['--debug-level=1']) + #print full_command proc = subprocess.Popen(full_command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, - stderr = sys.stderr, + stderr = stderr, close_fds = True) + if not DEBUG: + proc.stderr.close() return proc.stdin, proc.stdout ###### @@ -36,7 +50,7 @@ def gpg(command, args = None): SSH = '/usr/bin/ssh' SSH_HOST = 'localhost' -REMOTE_COMMAND = ['/home/nicolasd/cranspasswords-server.py'] +REMOTE_COMMAND = ['/home/dstan/crans/cranspasswords/cranspasswords-server.py'] def ssh(command, arg = None): """Lance ssh avec les arguments donnés. Renvoie son entrée @@ -106,7 +120,9 @@ def check_keys(): for mail, key in keys.values(): if key: _, stdout = gpg("fingerprint", [key]) - if "<%s>" % mail.lower() not in stdout.read().lower(): + if DEBUG: print "Checking %s" % mail + if str("<%s>" % mail.lower()) not in stdout.read().lower(): + if DEBUG: print "-->Fail on %s" % mail break else: return True @@ -133,7 +149,12 @@ def encrypt(roles, contents): stdin, stdout = gpg("encrypt", email_recipients) stdin.write(contents) stdin.close() - return stdout.read() + out = stdout.read() + if out == '': + if DEBUG: print "Échec de chiffrement" + return None + else: + return out def decrypt(contents): """Déchiffre le contenu""" @@ -146,11 +167,136 @@ def put_password(name, roles, contents): """Dépose le mot de passe après l'avoir chiffré pour les destinataires donnés""" enc_pwd = encrypt(roles, contents) - return put_file(name, roles, enc_pwd) + if enc_pwd <> None: + return put_file(name, roles, enc_pwd) + else: + return False def get_password(name): """Récupère le mot de passe donné par name""" remotefile = get_file(name) return decrypt(remotefile['contents']) +## Interface +def usage(): + print """Cranspasswords 2 Usage: + cranspasswords [options] [<filename>] + cranspasswords <filename> Télécharge le fichier + cranspasswords Mode interactif + +Options: + --view Télécharge le fichier +# --upload Upload un nouveau fichier depuis stdin + --edit Lance $EDITOR sur le fichier +# --roles=<role1>,<role2>… Définit les rôles +# --roles+=<role> Ajoute un rôle +# --roles-=<role> Supprime un rôle +# --edit-roles Lance $EDITOR sur les rôles +# --rm Supprime le fichier + --update-keys Mets à jour les clés + --check-keys Vérifie les clés + -l, --list Liste les fichiers disponibles + --list-roles Liste des rôles disponibles + -c, --clipboard mot de passe en presse papier""" + +def editor(texte): + """ Lance $EDITOR sur texte""" + f = tempfile.NamedTemporaryFile() + atexit.register(f.close) + f.write(texte) + f.flush() + proc = subprocess.Popen(os.getenv('EDITOR') + ' ' + f.name,shell=True) + os.waitpid(proc.pid,0) + f.seek(0) + ntexte = f.read() + f.close() + return texte <> ntexte and ntexte or None + +def show_files(): + print """Liste des fichiers disponibles""" + for fname in all_files(): + print " * " + fname + +def show_roles(): + print """Liste des roles disponibles""" + for role in all_roles().keys(): + if role.endswith('-w'): continue + print " * " + role + +def clipboard(texte): + proc =subprocess.Popen(['xclip','-selection','clipboard'],\ + stdin=subprocess.PIPE,stdout=sys.stdout,stderr=sys.stderr) + proc.stdin.write(texte) + proc.stdin.close() + print "[Le mot de passe a été mis dans le presse papier]" + + +def show_file(fname): + value = get_file(fname) + if value == False: + print "Fichier introuvable"; return + print "Fichier %s:" % fname + (sin,sout) = gpg('decrypt') + sin.write(value['contents']) + sin.close() + texte = sout.read() + if CLIPBOARD: # Ça ne va pas plaire à tout le monde + lines = texte.split('\n') + if len(lines) == 2: + clipboard(lines[0]) + else: + for line in lines: + if line.startswith('pass:'): + clipboard(line[5:].strip(' \t\r\n')) + else: + print line + else: + print texte + print "-----" + print "Visible par: %s" % ','.join(value['roles']) + # Todo: some clipboard facility + +def edit_file(fname): + value = get_file(fname) + if value == False: + print "Fichier introuvable"; return + (sin,sout) = gpg('decrypt') + sin.write(value['contents']) + sin.close() + texte = sout.read() + ntexte = editor(texte) + if ntexte == None: + print "Pas de modifications effectuées" + else: + if put_password(fname,value['roles'],ntexte): + print "Modifications enregistrées" + else: + print "Erreur lors de l'enregistrement (avez-vous les droits suffisants ?)" + + +if __name__ == "__main__": + argv = sys.argv[1:] + if '-c' in argv or '--clipboard' in argv: + CLIPBOARD=True + action = show_file + if '--edit' in argv: + action = edit_file + if '-v' in argv: #Verbose ! + DEBUG = True + for arg in argv: + if arg in ['--list','-l']: + show_files() + elif not arg.startswith('-'): + action(arg) + elif arg == '--check-keys': + print check_keys() and "Base de clés ok" or "Erreurs dans la base" + elif arg == '--update-keys': + print update_keys() + elif arg == '--list-roles': + show_roles() + elif arg in ['-c','--clipboard','--view','--edit','-v']: + pass + else: + usage() + break -- GitLab