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