Vers une version qui marche
This commit is contained in:
parent
8eb2e3e21d
commit
22a59154ba
2 changed files with 190 additions and 30 deletions
|
@ -2,6 +2,9 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
"""cranspasswords-server.py: Serveur pour cranspasswords"""
|
"""cranspasswords-server.py: Serveur pour cranspasswords"""
|
||||||
|
|
||||||
|
MYDIR = '/home/dstan/crans/cranspasswords/'
|
||||||
|
STORE = '/home/dstan/crans/passwords/v2/'
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
@ -15,7 +18,6 @@ if MYUID == 'root':
|
||||||
KEYS = {
|
KEYS = {
|
||||||
"aza-vallina": ("Damien.Aza-Vallina@crans.org", None),
|
"aza-vallina": ("Damien.Aza-Vallina@crans.org", None),
|
||||||
"dandrimont": ("nicolas.dandrimont@crans.org", "66475AAF"),
|
"dandrimont": ("nicolas.dandrimont@crans.org", "66475AAF"),
|
||||||
"nicolasd": (None, None),
|
|
||||||
"blockelet": ("blockelet@crans.org", "AF087A52"),
|
"blockelet": ("blockelet@crans.org", "AF087A52"),
|
||||||
"chambart": ("pierre.chambart@crans.org", "F2530FCE"),
|
"chambart": ("pierre.chambart@crans.org", "F2530FCE"),
|
||||||
"dimino": ("jdimino@dptinfo.ens-cachan.fr", "2127F85A"),
|
"dimino": ("jdimino@dptinfo.ens-cachan.fr", "2127F85A"),
|
||||||
|
@ -25,8 +27,32 @@ KEYS = {
|
||||||
"lagorce": ("xavier.lagorce@crans.org", "0BF3708E"),
|
"lagorce": ("xavier.lagorce@crans.org", "0BF3708E"),
|
||||||
"parret-freaud": ("parret-freaud@crans.org", "7D980513"),
|
"parret-freaud": ("parret-freaud@crans.org", "7D980513"),
|
||||||
"tvincent": ("vincent.thomas@crans.org", "C5C4ACC0"),
|
"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 = {
|
ROLES = {
|
||||||
"bureau": [
|
"bureau": [
|
||||||
"aza-vallina",
|
"aza-vallina",
|
||||||
|
@ -37,31 +63,19 @@ ROLES = {
|
||||||
"durand-gasselin",
|
"durand-gasselin",
|
||||||
"lagorce",
|
"lagorce",
|
||||||
],
|
],
|
||||||
"rtc": [
|
"rtc": RTC,
|
||||||
"dandrimont",
|
"nounous": NOUNOUS,
|
||||||
"nicolasd",
|
"nounous-w": NOUNOUS #Or maybe RTC ?
|
||||||
],
|
|
||||||
"nounou": [
|
|
||||||
"blockelet",
|
|
||||||
"chambart",
|
|
||||||
"dandrimont",
|
|
||||||
"dimino",
|
|
||||||
"durand-gasselin",
|
|
||||||
"glondu",
|
|
||||||
"huber",
|
|
||||||
"lagorce",
|
|
||||||
"parret-freaud",
|
|
||||||
"tvincent",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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:
|
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 True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -127,7 +141,7 @@ def putfile(filename):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if not validate(oldroles):
|
if not validate(oldroles,'w'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
writefile(filepath, json.dumps({'roles': roles, 'contents': contents}))
|
writefile(filepath, json.dumps({'roles': roles, 'contents': contents}))
|
||||||
|
@ -140,7 +154,7 @@ def rmfile(filename):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if validate(roles):
|
if validate(roles,'w'):
|
||||||
os.remove(getpath(filename))
|
os.remove(getpath(filename))
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
import atexit
|
||||||
|
|
||||||
######
|
######
|
||||||
## GPG Definitions
|
## GPG Definitions
|
||||||
|
@ -17,6 +20,9 @@ GPG_ARGS = {
|
||||||
'receive-keys': ['--recv-keys'],
|
'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):
|
def gpg(command, args = None):
|
||||||
"""Lance gpg pour la commande donnée avec les arguments
|
"""Lance gpg pour la commande donnée avec les arguments
|
||||||
donnés. Renvoie son entrée standard et sa sortie standard."""
|
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])
|
full_command.extend(GPG_ARGS[command])
|
||||||
if args:
|
if args:
|
||||||
full_command.extend(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,
|
proc = subprocess.Popen(full_command,
|
||||||
stdin = subprocess.PIPE,
|
stdin = subprocess.PIPE,
|
||||||
stdout = subprocess.PIPE,
|
stdout = subprocess.PIPE,
|
||||||
stderr = sys.stderr,
|
stderr = stderr,
|
||||||
close_fds = True)
|
close_fds = True)
|
||||||
|
if not DEBUG:
|
||||||
|
proc.stderr.close()
|
||||||
return proc.stdin, proc.stdout
|
return proc.stdin, proc.stdout
|
||||||
|
|
||||||
######
|
######
|
||||||
|
@ -36,7 +50,7 @@ def gpg(command, args = None):
|
||||||
|
|
||||||
SSH = '/usr/bin/ssh'
|
SSH = '/usr/bin/ssh'
|
||||||
SSH_HOST = 'localhost'
|
SSH_HOST = 'localhost'
|
||||||
REMOTE_COMMAND = ['/home/nicolasd/cranspasswords-server.py']
|
REMOTE_COMMAND = ['/home/dstan/crans/cranspasswords/cranspasswords-server.py']
|
||||||
|
|
||||||
def ssh(command, arg = None):
|
def ssh(command, arg = None):
|
||||||
"""Lance ssh avec les arguments donnés. Renvoie son entrée
|
"""Lance ssh avec les arguments donnés. Renvoie son entrée
|
||||||
|
@ -106,7 +120,9 @@ def check_keys():
|
||||||
for mail, key in keys.values():
|
for mail, key in keys.values():
|
||||||
if key:
|
if key:
|
||||||
_, stdout = gpg("fingerprint", [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
|
break
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
@ -133,7 +149,12 @@ def encrypt(roles, contents):
|
||||||
stdin, stdout = gpg("encrypt", email_recipients)
|
stdin, stdout = gpg("encrypt", email_recipients)
|
||||||
stdin.write(contents)
|
stdin.write(contents)
|
||||||
stdin.close()
|
stdin.close()
|
||||||
return stdout.read()
|
out = stdout.read()
|
||||||
|
if out == '':
|
||||||
|
if DEBUG: print "Échec de chiffrement"
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return out
|
||||||
|
|
||||||
def decrypt(contents):
|
def decrypt(contents):
|
||||||
"""Déchiffre le contenu"""
|
"""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
|
"""Dépose le mot de passe après l'avoir chiffré pour les
|
||||||
destinataires donnés"""
|
destinataires donnés"""
|
||||||
enc_pwd = encrypt(roles, contents)
|
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):
|
def get_password(name):
|
||||||
"""Récupère le mot de passe donné par name"""
|
"""Récupère le mot de passe donné par name"""
|
||||||
remotefile = get_file(name)
|
remotefile = get_file(name)
|
||||||
return decrypt(remotefile['contents'])
|
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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue