Commit 1c001ba4 authored by Daniel STAN's avatar Daniel STAN

roles modif + argparse

parent 22a59154
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
"""cranspasswords-server.py: Serveur pour cranspasswords""" """cranspasswords-server.py: Serveur pour cranspasswords"""
MYDIR = '/home/dstan/crans/cranspasswords/' MYDIR = '/home/dstan/cranspasswords/'
STORE = '/home/dstan/crans/passwords/v2/' STORE = MYDIR+'test/'
import glob import glob
import os import os
...@@ -31,7 +31,7 @@ KEYS = { ...@@ -31,7 +31,7 @@ KEYS = {
"becue": ("becue@crans.org", "194974E2"), "becue": ("becue@crans.org", "194974E2"),
"dstan": ("daniel.stan@crans.org", "6E1C820B"), "dstan": ("daniel.stan@crans.org", "6E1C820B"),
"cauderlier": ("cauderlier@crans.org",None), #Méchant pas beau "cauderlier": ("cauderlier@crans.org",None), #Méchant pas beau
"maioli": ("maioli@crans.org","9E5026E8") "maioli": ("maioli@crans.org",None) #Bis (maybe 9E5026E8)
} }
RTC=[ RTC=[
...@@ -54,15 +54,8 @@ NOUNOUS=RTC+[ ...@@ -54,15 +54,8 @@ NOUNOUS=RTC+[
] ]
ROLES = { ROLES = {
"bureau": [ "bureau": [],
"aza-vallina", "ca": [],
],
"ca": [
"aza-vallina",
"blockelet",
"durand-gasselin",
"lagorce",
],
"rtc": RTC, "rtc": RTC,
"nounous": NOUNOUS, "nounous": NOUNOUS,
"nounous-w": NOUNOUS #Or maybe RTC ? "nounous-w": NOUNOUS #Or maybe RTC ?
......
...@@ -8,6 +8,7 @@ import json ...@@ -8,6 +8,7 @@ import json
import tempfile import tempfile
import os import os
import atexit import atexit
import argparse
###### ######
## GPG Definitions ## GPG Definitions
...@@ -20,8 +21,11 @@ GPG_ARGS = { ...@@ -20,8 +21,11 @@ GPG_ARGS = {
'receive-keys': ['--recv-keys'], 'receive-keys': ['--recv-keys'],
} }
DEBUG=False DEBUG = False
CLIPBOARD=False # Par défaut, place-t-on le mdp dans le presse-papier ? VERB = False
CLIPBOARD = False # Par défaut, place-t-on le mdp dans le presse-papier ?
FORCED = False #Mode interactif qui demande confirmation
NROLES = None # Droits à définir sur le fichier en édition
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
...@@ -30,7 +34,7 @@ def gpg(command, args = None): ...@@ -30,7 +34,7 @@ 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: if VERB:
stderr=sys.stderr stderr=sys.stderr
else: else:
stderr=subprocess.PIPE stderr=subprocess.PIPE
...@@ -41,22 +45,23 @@ def gpg(command, args = None): ...@@ -41,22 +45,23 @@ def gpg(command, args = None):
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = stderr, stderr = stderr,
close_fds = True) close_fds = True)
if not DEBUG: if not VERB:
proc.stderr.close() proc.stderr.close()
return proc.stdin, proc.stdout return proc.stdin, proc.stdout
###### ######
## Remote commands ## Remote commands
SSH = '/usr/bin/ssh' SERVER_CMD_DEBUG = ['/usr/bin/ssh', 'localhost', \
SSH_HOST = 'localhost' '/home/dstan/crans/cranspasswords/cranspasswords-server.py']
REMOTE_COMMAND = ['/home/dstan/crans/cranspasswords/cranspasswords-server.py'] SERVER_CMD = ['/usr/bin/ssh', 'vo',\
'/home/dstan/cranspasswords/cranspasswords-server']
USER = 'dstan'
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
standard et sa sortie standard.""" standard et sa sortie standard."""
full_command = [SSH, SSH_HOST] full_command = list(SERVER_CMD)
full_command.extend(REMOTE_COMMAND)
full_command.append(command) full_command.append(command)
if arg: if arg:
full_command.append(arg) full_command.append(arg)
...@@ -101,6 +106,11 @@ def rm_file(filename): ...@@ -101,6 +106,11 @@ def rm_file(filename):
"""Supprime le fichier sur le serveur distant""" """Supprime le fichier sur le serveur distant"""
return remote_command("rmfile", filename) return remote_command("rmfile", filename)
def get_my_roles():
"""Retoure la liste des rôles perso"""
allr = all_roles()
return filter(lambda role: USER in allr[role],allr.keys())
###### ######
## Local commands ## Local commands
...@@ -120,13 +130,12 @@ def check_keys(): ...@@ -120,13 +130,12 @@ 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 DEBUG: print "Checking %s" % mail if VERB: print "Checking %s" % mail
if str("<%s>" % mail.lower()) not in stdout.read().lower(): if str("<%s>" % mail.lower()) not in stdout.read().lower():
if DEBUG: print "-->Fail on %s" % mail if VERB: print "-->Fail on %s" % mail
break break
else: else:
return True return True
return False return False
def encrypt(roles, contents): def encrypt(roles, contents):
...@@ -151,7 +160,7 @@ def encrypt(roles, contents): ...@@ -151,7 +160,7 @@ def encrypt(roles, contents):
stdin.close() stdin.close()
out = stdout.read() out = stdout.read()
if out == '': if out == '':
if DEBUG: print "Échec de chiffrement" if VERB: print "Échec de chiffrement"
return None return None
else: else:
return out return out
...@@ -167,6 +176,8 @@ def put_password(name, roles, contents): ...@@ -167,6 +176,8 @@ 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)
if NROLES != None:
roles = NROLES
if enc_pwd <> None: if enc_pwd <> None:
return put_file(name, roles, enc_pwd) return put_file(name, roles, enc_pwd)
else: else:
...@@ -178,26 +189,6 @@ def get_password(name): ...@@ -178,26 +189,6 @@ def get_password(name):
return decrypt(remotefile['contents']) return decrypt(remotefile['contents'])
## Interface ## 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): def editor(texte):
""" Lance $EDITOR sur texte""" """ Lance $EDITOR sur texte"""
...@@ -214,8 +205,12 @@ def editor(texte): ...@@ -214,8 +205,12 @@ def editor(texte):
def show_files(): def show_files():
print """Liste des fichiers disponibles""" print """Liste des fichiers disponibles"""
for fname in all_files(): my_roles = get_my_roles()
print " * " + fname for (fname,froles) in all_files().iteritems():
access = set(my_roles).intersection(froles) != set([])
print " %s %s (%s)" % ((access and '+' or '-'),fname,", ".join(froles))
print """--Mes roles: %s""" % \
", ".join(my_roles)
def show_roles(): def show_roles():
print """Liste des roles disponibles""" print """Liste des roles disponibles"""
...@@ -254,7 +249,6 @@ def show_file(fname): ...@@ -254,7 +249,6 @@ def show_file(fname):
print texte print texte
print "-----" print "-----"
print "Visible par: %s" % ','.join(value['roles']) print "Visible par: %s" % ','.join(value['roles'])
# Todo: some clipboard facility
def edit_file(fname): def edit_file(fname):
value = get_file(fname) value = get_file(fname)
...@@ -272,31 +266,129 @@ def edit_file(fname): ...@@ -272,31 +266,129 @@ def edit_file(fname):
print "Modifications enregistrées" print "Modifications enregistrées"
else: else:
print "Erreur lors de l'enregistrement (avez-vous les droits suffisants ?)" print "Erreur lors de l'enregistrement (avez-vous les droits suffisants ?)"
def confirm(text):
if FORCED: return True
while True:
out = raw_input(text + ' (O/N)').lower()
if out == 'o':
return True
elif out == 'n':
return False
def remove_file(fname):
if not confirm('Êtes-vous sûr de vouloir supprimer %s ?' % fname):
return
if rm_file(fname):
print "Suppression achevée"
else:
print "Erreur de suppression (avez-vous les droits ?)"
def my_check_keys():
check_keys() and "Base de clés ok" or "Erreurs dans la base"
def my_update_keys():
print update_keys()
def update_role(roles=None):
""" Reencode les fichiers, si roles est fourni,
contient une liste de rôles"""
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 filter(lambda r: r.endswith('-w'),my_roles)]
if type(roles) != list:
roles = [roles]
for (fname,froles) in all_files().iteritems():
if set(roles).intersection(froles) == set([]):
continue
#if VERB:
print "Reencodage de %s" % fname
put_password(fname,froles,get_password(fname))
def parse_roles(strroles):
if strroles == None: return None
roles = all_roles()
my_roles = filter(lambda r: USER in roles[r],roles.keys())
my_roles_w = [ r[:-2] for r in filter(lambda r: r.endswith('-w'),my_roles) ]
ret = set()
writable = False
for role in strroles.split(','):
if role not in roles.keys():
print("Le rôle %s n'existe pas !" % role)
return False
if role.endswith('-w'):
print("Le rôle %s ne devrait pas être utilisé ! (utilisez %s)"
% (role,role[:-2]))
return False
writable = writable or role in my_roles_w
ret.add(role)
if not FORCED and not writable:
if not confirm("Vous vous apprêtez à perdre vos droits d'écritures (role ne contient pas %s) sur ce fichier, continuer ?" % ", ".join(my_roles_w)):
return False
return list(ret)
if __name__ == "__main__": if __name__ == "__main__":
argv = sys.argv[1:] parser = argparse.ArgumentParser(description="trousseau crans")
if '-c' in argv or '--clipboard' in argv: parser.add_argument('--test',action='store_true',default=False,
CLIPBOARD=True help='Utilisation du serveur de test')
action = show_file parser.add_argument('-v','--verbose',action='store_true',default=False,
if '--edit' in argv: help="Mode verbeux")
action = edit_file parser.add_argument('-c','--clipboard',action='store_true',default=False,
if '-v' in argv: #Verbose ! help="Stocker le mot de passe dans le presse papier")
DEBUG = True parser.add_argument('-f','--force',action='store_true',default=False,
for arg in argv: help="Forcer l'action")
if arg in ['--list','-l']:
show_files() # Actions possibles
elif not arg.startswith('-'): action_grp = parser.add_mutually_exclusive_group(required=False)
action(arg) action_grp.add_argument('--edit',action='store_const',dest='action',
elif arg == '--check-keys': default=show_file,const=edit_file,
print check_keys() and "Base de clés ok" or "Erreurs dans la base" help="Editer")
elif arg == '--update-keys': action_grp.add_argument('--view',action='store_const',dest='action',
print update_keys() default=show_file,const=show_file,
elif arg == '--list-roles': help="Voir")
show_roles() action_grp.add_argument('--remove',action='store_const',dest='action',
elif arg in ['-c','--clipboard','--view','--edit','-v']: default=show_file,const=remove_file,
pass help="Effacer")
action_grp.add_argument('-l','--list',action='store_const',dest='action',
default=show_file,const=show_files,
help="Lister les fichiers")
action_grp.add_argument('--check-keys',action='store_const',dest='action',
default=show_file,const=my_check_keys,
help="Vérifier les clés")
action_grp.add_argument('--update-keys',action='store_const',dest='action',
default=show_file,const=my_update_keys,
help="Mettre à jour les clés")
action_grp.add_argument('--list-roles',action='store_const',dest='action',
default=show_file,const=show_roles,
help="Lister les rôles des gens")
action_grp.add_argument('--recrypt-role',action='store_const',dest='action',
default=show_file,const=update_role,
help="Met à jour (reencode les roles)")
parser.add_argument('--roles',nargs='?',default=None,
help="liste des roles à affecter au fichier")
parser.add_argument('fname',nargs='?',default=None,
help="Nom du fichier à afficher")
parsed = parser.parse_args(sys.argv[1:])
DEBUG = parsed.test
if DEBUG:
SERVER_CMD = SERVER_CMD_DEBUG
VERB = parsed.verbose
CLIPBOARD = parsed.clipboard
FORCED = parsed.force
NROLES = parse_roles(parsed.roles)
if NROLES != False:
if parsed.action.func_code.co_argcount == 0:
parsed.action()
elif parsed.fname == None:
print("Vous devez fournir un nom de fichier avec cette commande")
parser.print_help()
else: else:
usage() parsed.action(parsed.fname)
break
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment