getfile*s*; putfile*s* et utilisation pour --recrypt-files
On peut récupérer/envoyer plusieurs fichiers à la fois. A priori, le serveur n'est plus rétro-compatible avec les clients non à jour. Conflicts: client.py server.py
This commit is contained in:
parent
0f415a0d97
commit
3589ef41ab
2 changed files with 112 additions and 50 deletions
109
client.py
109
client.py
|
@ -21,6 +21,7 @@ import re
|
|||
import random
|
||||
import string
|
||||
import datetime
|
||||
import gnupg
|
||||
try:
|
||||
import gnupg #disponible seulement sous wheezy
|
||||
except ImportError:
|
||||
|
@ -68,7 +69,7 @@ CLIPBOARD = bool(os.getenv('DISPLAY')) and os.path.exists('/usr/bin/xclip')
|
|||
#: Mode «ne pas demander confirmation»
|
||||
FORCED = False
|
||||
#: Droits à définir sur le fichier en édition
|
||||
NROLES = None
|
||||
NEWROLES = None
|
||||
#: Serveur à interroger (peuplée à l'exécution)
|
||||
SERVER = None
|
||||
|
||||
|
@ -128,10 +129,11 @@ def remote_command(command, arg = None, stdin_contents = None):
|
|||
commande"""
|
||||
|
||||
sshin, sshout = ssh(command, arg)
|
||||
if stdin_contents:
|
||||
if not stdin_contents is None:
|
||||
sshin.write(json.dumps(stdin_contents))
|
||||
sshin.close()
|
||||
return json.loads(sshout.read())
|
||||
raw_out = sshout.read()
|
||||
return json.loads(raw_out)
|
||||
|
||||
@simple_memoize
|
||||
def all_keys():
|
||||
|
@ -148,14 +150,13 @@ def all_files():
|
|||
"""Récupère les fichiers du serveur distant"""
|
||||
return remote_command("listfiles")
|
||||
|
||||
def get_file(filename):
|
||||
"""Récupère le contenu du fichier distant"""
|
||||
return remote_command("getfile", filename)
|
||||
def get_files(filenames):
|
||||
"""Récupère le contenu des fichiers distants"""
|
||||
return remote_command("getfiles", stdin_contents=filenames)
|
||||
|
||||
def put_file(filename, roles, contents):
|
||||
"""Dépose le fichier sur le serveur distant"""
|
||||
return remote_command("putfile", filename, {'roles': roles,
|
||||
'contents' : contents})
|
||||
def put_files(files):
|
||||
"""Dépose les fichiers sur le serveur distant"""
|
||||
return remote_command("putfiles", stdin_contents=files)
|
||||
|
||||
def rm_file(filename):
|
||||
"""Supprime le fichier sur le serveur distant"""
|
||||
|
@ -238,8 +239,7 @@ def get_dest_of_roles(roles):
|
|||
for rec in get_recipients_of_roles(roles) if allkeys[rec][1]]
|
||||
|
||||
def encrypt(roles, contents):
|
||||
"""Chiffre le contenu pour les roles donnés"""
|
||||
|
||||
"""Chiffre ``contents`` pour les ``roles`` donnés"""
|
||||
allkeys = all_keys()
|
||||
recipients = get_recipients_of_roles(roles)
|
||||
|
||||
|
@ -270,20 +270,20 @@ def put_password(name, roles, contents):
|
|||
"""Dépose le mot de passe après l'avoir chiffré pour les
|
||||
destinataires donnés"""
|
||||
success, enc_pwd_or_error = encrypt(roles, contents)
|
||||
if NROLES != None:
|
||||
roles = NROLES
|
||||
if NEWROLES != None:
|
||||
roles = NEWROLES
|
||||
if VERB:
|
||||
print(u"Pas de nouveaux rôles".encode("utf-8"))
|
||||
if success:
|
||||
enc_pwd = enc_pwd_or_error
|
||||
return put_file(name, roles, enc_pwd)
|
||||
return put_files([{'filename' : name, 'roles' : roles, 'contents' : enc_pwd}])[0]
|
||||
else:
|
||||
error = enc_pwd_or_error
|
||||
return [False, error]
|
||||
|
||||
def get_password(name):
|
||||
"""Récupère le mot de passe donné par name"""
|
||||
gotit, remotefile = get_file(name)
|
||||
gotit, remotefile = get_files([name])[0]
|
||||
if gotit:
|
||||
remotefile = decrypt(remotefile['contents'])
|
||||
return [gotit, remotefile]
|
||||
|
@ -367,7 +367,7 @@ def clipboard(texte):
|
|||
|
||||
def show_file(fname):
|
||||
"""Affiche le contenu d'un fichier"""
|
||||
gotit, value = get_file(fname)
|
||||
gotit, value = get_files([fname])[0]
|
||||
if not gotit:
|
||||
print(value.encode("utf-8")) # value contient le message d'erreur
|
||||
return
|
||||
|
@ -385,7 +385,7 @@ def show_file(fname):
|
|||
line = clipboard(catchPass.group(1))
|
||||
ntexte += line + '\n'
|
||||
showbin = "cat" if hidden else "less"
|
||||
proc = subprocess.Popen(showbin, stdin=subprocess.PIPE, shell=True)
|
||||
proc = subprocess.Popen([showbin], stdin=subprocess.PIPE)
|
||||
out = proc.stdin
|
||||
raw = u"Fichier %s:\n\n%s-----\nVisible par: %s\n" % (fname, ntexte, ','.join(value['roles']))
|
||||
out.write(raw.encode("utf-8"))
|
||||
|
@ -395,7 +395,7 @@ def show_file(fname):
|
|||
|
||||
def edit_file(fname):
|
||||
"""Modifie/Crée un fichier"""
|
||||
gotit, value = get_file(fname)
|
||||
gotit, value = get_files([fname])[0]
|
||||
nfile = False
|
||||
annotations = u""
|
||||
if not gotit and not "pas les droits" in value:
|
||||
|
@ -423,7 +423,8 @@ Enregistrez le fichier vide pour annuler.\n"""
|
|||
sin.write(value['contents'].encode("utf-8"))
|
||||
sin.close()
|
||||
texte = sout.read().decode("utf-8")
|
||||
value['roles'] = NROLES or value['roles']
|
||||
# On récupère les nouveaux roles si ils ont été précisés, sinon on garde les mêmes
|
||||
value['roles'] = NEWROLES or value['roles']
|
||||
|
||||
annotations += u"""Ce fichier sera chiffré pour les rôles suivants :\n%s\n
|
||||
C'est-à-dire pour les utilisateurs suivants :\n%s""" % (
|
||||
|
@ -433,7 +434,7 @@ C'est-à-dire pour les utilisateurs suivants :\n%s""" % (
|
|||
|
||||
ntexte = editor(texte, annotations)
|
||||
|
||||
if ((not nfile and ntexte in [u'', texte] and NROLES == None) or # Fichier existant vidé ou inchangé
|
||||
if ((not nfile and ntexte in [u'', texte] and NEWROLES == None) or # Fichier existant vidé ou inchangé
|
||||
(nfile and ntexte == u'')): # Nouveau fichier créé vide
|
||||
print(u"Pas de modification effectuée".encode("utf-8"))
|
||||
else:
|
||||
|
@ -470,26 +471,50 @@ def my_update_keys():
|
|||
|
||||
def recrypt_files():
|
||||
"""Rechiffre les fichiers"""
|
||||
roles = None
|
||||
# Ici, la signification de NEWROLES est : on ne veut rechiffrer que les fichiers qui ont au moins un de ces roles
|
||||
rechiffre_roles = NEWROLES
|
||||
my_roles = get_my_roles()
|
||||
if roles == None:
|
||||
# On ne conserve que les rôles qui finissent par -w
|
||||
roles = [ r[:-2] for r in my_roles if r.endswith('-w')]
|
||||
if type(roles) != list:
|
||||
roles = [roles]
|
||||
|
||||
for (fname, froles) in all_files().iteritems():
|
||||
if set(roles).intersection(froles) == set([]):
|
||||
continue
|
||||
print((u"Rechiffrement de %s" % fname).encode("utf-8"))
|
||||
_, password = get_password(fname)
|
||||
put_password(fname, froles, password)
|
||||
my_roles_w = [r for r in my_roles if r.endswith("-w")]
|
||||
if rechiffre_roles == None:
|
||||
# Sans précisions, on prend tous les roles qu'on peut
|
||||
rechiffre_roles = my_roles
|
||||
# On ne conserve que les rôles en écriture
|
||||
rechiffre_roles = [ r[:-2] for r in rechiffre_roles if r.endswith('-w')]
|
||||
|
||||
# La liste des fichiers
|
||||
allfiles = all_files()
|
||||
# On ne demande que les fichiers dans lesquels on peut écrire
|
||||
# et qui ont au moins un role dans ``roles``
|
||||
askfiles = [filename for (filename, fileroles) in allfiles.iteritems()
|
||||
if set(fileroles).intersection(roles) != set()
|
||||
and set(fileroles).intersection(my_roles_w) != set()]
|
||||
files = get_files(askfiles)
|
||||
# Au cas où on aurait échoué à récupérer ne serait-ce qu'un de ces fichiers,
|
||||
# on affiche le message d'erreur correspondant et on abandonne.
|
||||
for (success, message) in files:
|
||||
if not success:
|
||||
print(message.encode("utf-8"))
|
||||
return
|
||||
# On rechiffre
|
||||
to_put = [{'filename' : f['filename'],
|
||||
'roles' : f['roles'],
|
||||
'contents' : encrypt(f['roles'], decrypt(f['contents']))}
|
||||
for f in files]
|
||||
if to_put:
|
||||
print((u"Rechiffrement de %s" % (", ".join([f['filename'] for f in to_put]))).encode("utf-8"))
|
||||
results = put_files(to_put)
|
||||
# On affiche les messages de retour
|
||||
for i in range(len(results)):
|
||||
print (u"%s : %s" % (to_put[i]['filename'], results[i][1]))
|
||||
else:
|
||||
print(u"Aucun fichier n'a besoin d'être rechiffré".encode("utf-8"))
|
||||
|
||||
def parse_roles(strroles):
|
||||
"""Interprête une liste de rôles fournie par l'utilisateur"""
|
||||
"""Interprête une liste de rôles fournie par l'utilisateur.
|
||||
Renvoie ``False`` si au moins un de ces rôles pose problème."""
|
||||
if strroles == None: return None
|
||||
roles = all_roles()
|
||||
my_roles = filter(lambda r: SERVER['user'] in roles[r],roles.keys())
|
||||
my_roles = filter(lambda r: SERVER['user'] in roles[r], roles.keys())
|
||||
my_roles_w = [ r[:-2] for r in my_roles if r.endswith('-w') ]
|
||||
ret = set()
|
||||
writable = False
|
||||
|
@ -555,10 +580,14 @@ if __name__ == "__main__":
|
|||
help="Lister les serveurs")
|
||||
action_grp.add_argument('--recrypt-files', action='store_const', dest='action',
|
||||
default=show_file, const=recrypt_files,
|
||||
help="Rechiffrer les mots de passe")
|
||||
help="Rechiffrer les mots de passe. (Avec les mêmes rôles qu'avant, sert à rajouter un lecteur)")
|
||||
|
||||
parser.add_argument('--roles', nargs='?', default=None,
|
||||
help="liste des roles à affecter au fichier")
|
||||
help="""Liste de roles (séparés par des virgules).
|
||||
Avec --edit, le fichier sera chiffré pour exactement ces roles
|
||||
(par défaut, tous vos rôles en écriture seront utilisés).
|
||||
Avec --recrypt-files, tous les fichiers ayant au moins un de ces roles (et pour lesquels vous avez le droit d'écriture) seront rechiffrés
|
||||
(par défaut, tous les fichiers pour lesquels vous avez les droits en écriture sont rechiffrés).""")
|
||||
parser.add_argument('fname', nargs='?', default=None,
|
||||
help="Nom du fichier à afficher")
|
||||
|
||||
|
@ -569,9 +598,9 @@ if __name__ == "__main__":
|
|||
if parsed.clipboard != None:
|
||||
CLIPBOARD = parsed.clipboard
|
||||
FORCED = parsed.force
|
||||
NROLES = parse_roles(parsed.roles)
|
||||
NEWROLES = parse_roles(parsed.roles)
|
||||
|
||||
if NROLES != False:
|
||||
if NEWROLES != False:
|
||||
if parsed.action.func_code.co_argcount == 0:
|
||||
parsed.action()
|
||||
elif parsed.fname == None:
|
||||
|
|
53
server.py
53
server.py
|
@ -73,22 +73,20 @@ def getfile(filename):
|
|||
obj = json.loads(open(filepath).read())
|
||||
if not validate(obj['roles']):
|
||||
return [False, u"Vous n'avez pas les droits de lecture sur le fichier %s." % filename]
|
||||
obj["filename"] = filename
|
||||
return [True, obj]
|
||||
except IOError:
|
||||
return [False, u"Le fichier %s n'existe pas." % filename]
|
||||
|
||||
|
||||
def putfile(filename):
|
||||
"""Écrit le fichier ``filename`` avec les données reçues sur stdin."""
|
||||
filepath = getpath(filename)
|
||||
def getfiles():
|
||||
"""Récupère plusieurs fichiers, lit la liste des filenames demandés sur stdin"""
|
||||
stdin = sys.stdin.read()
|
||||
parsed_stdin = json.loads(stdin)
|
||||
try:
|
||||
roles = parsed_stdin['roles']
|
||||
contents = parsed_stdin['contents']
|
||||
except KeyError:
|
||||
return [False, u"Entrée invalide"]
|
||||
|
||||
filenames = json.loads(stdin)
|
||||
return [getfile(f) for f in filenames]
|
||||
|
||||
def _putfile(filename, roles, contents):
|
||||
"""Écrit ``contents`` avec les roles ``roles`` dans le fichier ``filename``"""
|
||||
gotit, old = getfile(filename)
|
||||
if not gotit:
|
||||
old = u"[Création du fichier]"
|
||||
|
@ -102,9 +100,38 @@ def putfile(filename):
|
|||
backup(corps, filename, old)
|
||||
notification(u"Modification de %s" % filename, corps, filename, old)
|
||||
|
||||
filepath = getpath(filename)
|
||||
writefile(filepath, json.dumps({'roles': roles, 'contents': contents}))
|
||||
return [True, u"Modification effectuée."]
|
||||
|
||||
def putfile(filename):
|
||||
"""Écrit le fichier ``filename`` avec les données reçues sur stdin."""
|
||||
stdin = sys.stdin.read()
|
||||
parsed_stdin = json.loads(stdin)
|
||||
try:
|
||||
roles = parsed_stdin['roles']
|
||||
contents = parsed_stdin['contents']
|
||||
except KeyError:
|
||||
return [False, u"Entrée invalide"]
|
||||
return _putfile(filename, roles, contents)
|
||||
|
||||
def putfiles():
|
||||
"""Écrit plusieurs fichiers. Lit les filenames sur l'entrée standard avec le reste."""
|
||||
stdin = sys.stdin.read()
|
||||
parsed_stdin = json.loads(stdin)
|
||||
results = []
|
||||
for fichier in parsed_stdin:
|
||||
try:
|
||||
filename = fichier['filename']
|
||||
roles = fichier['roles']
|
||||
contents = fichier['contents']
|
||||
except KeyError:
|
||||
results.append([False, u"Entrée invalide"])
|
||||
else:
|
||||
results.append(_putfile(filename, roles, contents))
|
||||
return results
|
||||
|
||||
|
||||
def rmfile(filename):
|
||||
"""Supprime le fichier filename après avoir vérifié les droits sur le fichier"""
|
||||
gotit, old = getfile(filename)
|
||||
|
@ -120,6 +147,7 @@ def rmfile(filename):
|
|||
return u"Vous n'avez pas les droits d'écriture sur le fichier %s." % filename
|
||||
return u"Suppression effectuée"
|
||||
|
||||
|
||||
def backup(corps, fname, old):
|
||||
"""Backupe l'ancienne version du fichier"""
|
||||
back = open(getpath(fname, backup=True), 'a')
|
||||
|
@ -168,8 +196,13 @@ if __name__ == "__main__":
|
|||
answer = listkeys()
|
||||
elif command == "listfiles":
|
||||
answer = listfiles()
|
||||
elif command == "getfiles":
|
||||
answer = getfiles()
|
||||
elif command == "putfiles":
|
||||
answer = putfiles()
|
||||
else:
|
||||
if not filename:
|
||||
print("filename nécessaire pour cette opération", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if command == "getfile":
|
||||
answer = getfile(filename)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue