diff --git a/clientconfig.example.py b/clientconfig.example.py index 3d8ea7fccce134b4fb04529f6ab49558a221f819..12f7739143780f58398d9d9574b2c57f17675897 100755 --- a/clientconfig.example.py +++ b/clientconfig.example.py @@ -1,8 +1,12 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- +"""Configuration du client cranspasswords""" + import os +#: Serveurs distants utilisables, +#: avec la commande distante à exécuter et l'username sur le serveur servers = { 'default': { 'server_cmd': ['/usr/bin/ssh', 'vert.adm.crans.org',\ @@ -17,4 +21,3 @@ servers = { # n'ayant pas le même login sur leur pc } } - diff --git a/server b/server index ac12868ad1863fc17d0a5502880a2c6524bc5916..10f645703069152047b8d3d1c6b830d97602d66b 100755 --- a/server +++ b/server @@ -1,2 +1,3 @@ #!/bin/bash +# sudo-wrapper pour exécuter cranspasswords côté serveur sudo /root/cranspasswords/server.py $* diff --git a/server.py b/server.py index 8599921c8fe7c334b76e18d3cbc89dfbb2f50d56..a32ee5c3836486d5549f20743adb25e608cbad4e 100755 --- a/server.py +++ b/server.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -"""cranspasswords-server.py: Serveur pour cranspasswords""" + +"""Serveur pour cranspasswords""" import glob import os @@ -18,22 +19,23 @@ MYUID = pwd.getpwuid(os.getuid())[0] if MYUID == 'root': MYUID = os.environ['SUDO_USER'] -def validate(roles,mode='r'): - """Valide que l'appelant appartient bien aux roles précisés +def validate(roles, mode='r'): + """Vérifie 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 mode == 'w': role+='-w' + if mode == 'w': + role += '-w' if ROLES.has_key(role) and MYUID in ROLES[role]: return True return False -def getpath(filename,backup=False): - """Récupère le chemin du fichier `filename'""" - return os.path.join(STORE, '%s.%s' % (filename,'bak' if backup else 'json')) +def getpath(filename, backup=False): + """Récupère le chemin du fichier ``filename``""" + return os.path.join(STORE, '%s.%s' % (filename, 'bak' if backup else 'json')) def writefile(filename, contents): - """Écrit le fichier de manière sécure""" + """Écrit le fichier avec les bons droits UNIX""" os.umask(0077) f = open(filename, 'w') f.write(contents) @@ -44,26 +46,22 @@ def listroles(): return ROLES def listkeys(): - """Liste les uid et les clés correspondantes""" + """Liste les usernames et les (mail, fingerprint) correspondants""" return KEYS def listfiles(): """Liste les fichiers dans l'espace de stockage, et les roles qui peuvent y accéder""" os.chdir(STORE) - + filenames = glob.glob('*.json') - files = {} - for filename in filenames: file_dict = json.loads(open(filename).read()) files[filename[:-5]] = file_dict["roles"] - return files def getfile(filename): - """Récupère le fichier `filename'""" - + """Récupère le fichier ``filename``""" filepath = getpath(filename) try: obj = json.loads(open(filepath).read()) @@ -75,19 +73,16 @@ def getfile(filename): def putfile(filename): - """Écrit le fichier `filename' avec les données reçues sur stdin.""" - + """Écrit le fichier ``filename`` avec les données reçues sur stdin.""" filepath = getpath(filename) - stdin = sys.stdin.read() parsed_stdin = json.loads(stdin) - try: roles = parsed_stdin['roles'] contents = parsed_stdin['contents'] except KeyError: return False - + try: old = getfile(filename) oldroles = old['roles'] @@ -98,9 +93,9 @@ def putfile(filename): if not validate(oldroles,'w'): return False - notification("Modification de %s" % filename,\ - "Le fichier %s a été modifié par %s." %\ - (filename,MYUID),filename,old) + notification("Modification de %s" % filename, + "Le fichier %s a été modifié par %s." % (filename, MYUID), + filename, old) writefile(filepath, json.dumps({'roles': roles, 'contents': contents})) @@ -123,22 +118,22 @@ def rmfile(filename): return False return True -def notification(subject,corps,fname,old): - back = open(getpath(fname,True),'a') +def backup(fname, old): + """Backupe l'ancienne version du fichier""" + back = open(getpath(fname, backup=True), 'a') back.write(json.dumps(old)) back.write('\n') back.write('* %s: %s\n' % (str(datetime.datetime.now()),corps)) back.close() - - # Puis envoi du message + +def notification(subject, corps, fname, old): + """Envoie par mail une notification de changement de fichier""" conn = smtplib.SMTP('localhost') frommail = CRANSP_MAIL tomail = DEST_MAIL msg = MIMEMultipart(_charset="utf-8") msg['Subject'] = subject msg['X-Mailer'] = "cranspasswords" - # me == the sender's email address - # family = the list of all recipients' email addresses msg['From'] = CRANSP_MAIL msg['To'] = DEST_MAIL msg.preamble = "cranspasswords report" @@ -167,7 +162,7 @@ if __name__ == "__main__": filename = argv[1] except IndexError: pass - + if command == "listroles": print json.dumps(listroles()) elif command == "listkeys": @@ -185,4 +180,3 @@ if __name__ == "__main__": print json.dumps(rmfile(filename)) else: sys.exit(1) - diff --git a/serverconfig.example.py b/serverconfig.example.py index 414ce6a49edec41bc7b82518225f107fa351ca38..10d68d60d7a953cf06438c6cb8a762670e2fb583 100755 --- a/serverconfig.example.py +++ b/serverconfig.example.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- + """ Configuration Serveur de cranspasswords. Sont définis ici les utilisateurs et les rôles associés. Ce fichier est donné à titre d'exemple, mais n'est PAS @@ -7,19 +8,19 @@ utilisé lorsque fonctionnement en mode client. Dans le futur, sera remplacé par une connexion ldap. """ +#: Répertoire de stockage des mots de passe STORE = '/root/cranspasswords/db/' -""" Répertoire de stockage """ +#: Ce serveur est-il read-only (on ne peut pas y modifier les mots de passe) READONLY = False -""" Ce serveur est-il read-only (on ne peut pas y modifier les mots de passe) """ +#: Expéditeur du mail de notification CRANSP_MAIL = "cranspasswords <root@crans.org>" -""" Expéditeur du mail de notification """ +#: Destinataire du mail de notification DEST_MAIL = "root@crans.org" -""" Destinataire du mail de notification """ - +#: Mapping des utilisateurs et de leurs (mail, fingerprint GPG) KEYS = { 'aza-vallina': ('Damien.Aza-Vallina@crans.org', None), 'becue': ('becue@crans.org', '9AE04D986400E3B67528F4930D442664194974E2'), @@ -62,11 +63,12 @@ KEYS = { 'kviard': ('kviard@crans.org', None) } -# Les variables suivantes sont utilisées pour définir le dictionnaire des -# rôles. +#: Les variables suivantes sont utilisées pour définir le dictionnaire des +#: rôles. RTC=[ "iffrig" ] +#: Liste des usernames des nounous NOUNOUS=RTC+[ "blockelet", "becue", @@ -86,12 +88,14 @@ NOUNOUS=RTC+[ ] # Autogen: +#: Liste des usernames des apprentis APPRENTIS=['grande', 'bonaque', 'moisy-mabille', 'baste', 'duplouy', 'besson', 'pvincent', 'quelennec', 'pommeret', 'guiraud', 'serrano', 'kherouf', 'randazzo', 'tilquin', 'lasseri', 'epalle', 'soret', 'gstalter', 'kviard'] +#: Liste des usernames des membres du CA CA=[ ] -## Les vrais rôles ! +#: Les roles utilisés pour savoir qui a le droit le lire/écrire quoi ROLES = { "ca": CA, "ca-w": CA,