Commit 57ff2413 authored by Vincent Le gallic's avatar Vincent Le gallic

Merge branch 'master' of ssh://git.crans.org/git/cranspasswords

parents aade16b2 0c127bdf
clientconfig.py
serverconfig.py
*.pyc
# Dossier contenant les mots de passe
db
......@@ -3,9 +3,10 @@ Ce dépôt git contient à la fois le programme client (à utiliser sur votre
ordinateur) et le serveur.
== Installation et configuration du client ==
* Copier le dépôt git sur votre ordinateur:
* Copier le dépôt git sur votre ordinateur :
$ git clone git://git.crans.org/git/cranspasswords.git
* Copier clientconfig.example.py en clientconfig.py et adapter
à vos besoins
* Si ce n'est déjà fait, indiquer votre clé publique sur gest_crans
* ???
......@@ -9,6 +9,12 @@ servers = {
'/root/cranspasswords/server'],
'user' : os.getenv('USER') # À définir à la main pour les personnes
# n'ayant pas le même login sur leur pc
},
'ovh': {
'server_cmd': ['/usr/bin/ssh', 'ovh.crans.org',\
'/root/cranspasswords/server'],
'user' : os.getenv('USER') # À définir à la main pour les personnes
# n'ayant pas le même login sur leur pc
}
}
......@@ -10,7 +10,14 @@ import os
import atexit
import argparse
import re
import clientconfig as config
import random
import string
import datetime
try:
import clientconfig as config
except ImportError:
print "Read the README"
sys.exit(1)
## Password pattern in files:
PASS = re.compile('[\t ]*pass(?:word)?[\t ]*:[\t ]*(.*)\r?\n?$', \
......@@ -29,7 +36,8 @@ GPG_ARGS = {
DEBUG = False
VERB = False
CLIPBOARD = bool(os.getenv('DISPLAY')) # Par défaut, place-t-on le mdp dans le presse-papier ?
# Par défaut, place-t-on le mdp dans le presse-papier ?
CLIPBOARD = bool(os.getenv('DISPLAY')) and os.path.exists('/usr/bin/xclip')
FORCED = False #Mode interactif qui demande confirmation
NROLES = None # Droits à définir sur le fichier en édition
SERVER = None
......@@ -56,6 +64,18 @@ def gpg(command, args = None):
proc.stderr.close()
return proc.stdin, proc.stdout
class simple_memoize(object):
""" Memoization/Lazy """
def __init__(self, f):
self.f = f
self.val = None
def __call__(self):
if self.val==None:
self.val = self.f()
return self.val
######
## Remote commands
......@@ -84,14 +104,17 @@ def remote_command(command, arg = None, stdin_contents = None):
sshin.close()
return json.loads(sshout.read())
@simple_memoize
def all_keys():
"""Récupère les clés du serveur distant"""
return remote_command("listkeys")
@simple_memoize
def all_roles():
"""Récupère les roles du serveur distant"""
return remote_command("listroles")
@simple_memoize
def all_files():
"""Récupère les fichiers du serveur distant"""
return remote_command("listfiles")
......@@ -108,11 +131,19 @@ def rm_file(filename):
"""Supprime le fichier sur le serveur distant"""
return remote_command("rmfile", filename)
@simple_memoize
def get_my_roles():
"""Retoure la liste des rôles perso"""
allr = all_roles()
return filter(lambda role: SERVER['user'] in allr[role],allr.keys())
def gen_password():
"""Generate random password"""
random.seed(datetime.datetime.now().microsecond)
chars = string.letters + string.digits + '/=+*'
length = 15
return ''.join([random.choice(chars) for _ in xrange(length)])
######
## Local commands
......@@ -140,17 +171,38 @@ def check_keys():
return True
return False
def get_recipients_of_roles(roles):
"""Renvoie les destinataires d'un rôle"""
recipients = set()
allroles = all_roles()
for role in roles:
for recipient in allroles[role]:
recipients.add(recipient)
return recipients
def get_dest_of_roles(roles):
""" Summarize recipients of a role """
allkeys = all_keys()
def additionnal_info(rec):
""" Gives additionnal information for a given recipient """
if len(allkeys[rec]) == 0:
return ""
out = allkeys[rec][0]
if len(allkeys[rec]) > 1:
out += " -> " + allkeys[rec][1]
return "(%s)" % out
return ["%s %s" % (rec, additionnal_info(rec)) for rec in \
get_recipients_of_roles(roles) ]
def encrypt(roles, contents):
"""Chiffre le contenu pour les roles donnés"""
recipients = set()
allroles = all_roles()
allkeys = all_keys()
recipients = get_recipients_of_roles(roles)
email_recipients = []
for role in roles:
for recipient in allroles[role]:
recipients.add(recipient)
for recipient in recipients:
email, key = allkeys[recipient]
if key:
......@@ -194,25 +246,39 @@ def get_password(name):
## Interface
def editor(texte):
""" Lance $EDITOR sur texte"""
f = tempfile.NamedTemporaryFile()
def editor(texte, annotations=""):
""" Lance $EDITOR sur texte.
Renvoie le nouveau texte si des modifications ont été apportées, ou None
"""
# Avoid syntax hilight with ".txt". Would be nice to have some colorscheme
# for annotations ...
f = tempfile.NamedTemporaryFile(suffix='.txt')
atexit.register(f.close)
f.write(texte)
for l in annotations.split('\n'):
f.write("# %s\n" % l.encode('utf-8'))
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
ntexte = '\n'.join(filter(lambda l: not l.startswith('#'), ntexte.split('\n')))
if texte != ntexte:
return ntexte
return None
def show_files():
proc = subprocess.Popen("cat",stdin=subprocess.PIPE,shell=True)
out = proc.stdin
out.write("""Liste des fichiers disponibles\n""" )
my_roles = get_my_roles()
for (fname,froles) in all_files().iteritems():
files = all_files()
keys = files.keys()
keys.sort()
for fname in keys:
froles = files[fname]
access = set(my_roles).intersection(froles) != set([])
out.write(" %s %s (%s)\n" % ((access and '+' or '-'),fname,", ".join(froles)))
out.write("""--Mes roles: %s\n""" % \
......@@ -227,6 +293,11 @@ def show_roles():
if role.endswith('-w'): continue
print " * " + role
def show_servers():
print """Liste des serveurs disponibles"""
for server in config.servers.keys():
print " * " + server
old_clipboard = None
def saveclipboard(restore=False):
global old_clipboard
......@@ -284,12 +355,15 @@ def show_file(fname):
def edit_file(fname):
value = get_file(fname)
nfile = False
annotations = u""
if value == False:
nfile = True
print "Fichier introuvable"
if not confirm("Créer fichier ?"):
return
texte = ""
annotations += u"""Ceci est un fichier initial contenant un mot de passe
aléatoire, pensez à rajouter une ligne "login: ${login}" """
texte = "pass: %s\n" % gen_password()
roles = get_my_roles()
# Par défaut les roles d'un fichier sont ceux en écriture de son
# créateur
......@@ -303,7 +377,16 @@ def edit_file(fname):
sin.write(value['contents'])
sin.close()
texte = sout.read()
ntexte = editor(texte)
value['roles'] = NROLES 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" % (
', '.join(value['roles']),
'\n'.join(' %s' % rec for rec in get_dest_of_roles(value['roles']))
)
ntexte = editor(texte, annotations)
if ntexte == None and not nfile and NROLES == None:
print "Pas de modifications effectuées"
else:
......@@ -388,7 +471,7 @@ if __name__ == "__main__":
help="Mode verbeux")
parser.add_argument('-c','--clipboard',action='store_true',default=None,
help="Stocker le mot de passe dans le presse papier")
parser.add_argument('--noclipboard',action='store_false',default=None,
parser.add_argument('--no-clip', '--noclip', '--noclipboard',action='store_false',default=None,
dest='clipboard',
help="Ne PAS stocker le mot de passe dans le presse papier")
parser.add_argument('-f','--force',action='store_true',default=False,
......@@ -417,6 +500,9 @@ if __name__ == "__main__":
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('--list-servers',action='store_const',dest='action',
default=show_file,const=show_servers,
help="Lister les rôles serveurs")
action_grp.add_argument('--recrypt-role',action='store_const',dest='action',
default=show_file,const=update_role,
help="Met à jour (reencode les roles)")
......
# Fonction de notre auto completion
contain(){
local i
for i in $2; do
if [[ "$i" = "$1" ]]; then
return 0
fi
done
return 1
}
if [[ "$EDITOR" = "" ]]; then
export EDITOR="nano";
fi
_cranspasswords(){
# declaration des variables locales
local argc first last prev cur cur_first_char opts_short opts role_dir pass_dir server server_list role_list pass_list timeout
role_dir="/tmp/cranspasswords-$USER-role/"
pass_dir="/tmp/cranspasswords-$USER-passwords/"
# Combien de temps on garde les réponses du serveur en cache (en minutes)
timeout=5
#COMPREPLY désigne la réponse à renvoyer pour la complétion actuelle
COMPREPLY=()
# argc : vaut le nombre d'argument actuel sur la ligne de commande
argc=${COMP_CWORD};
# cur : désigne la chaine de caractère actuelle pour le dernier mot de la ligne de commande
first="${COMP_WORDS[1]}"
last="${COMP_WORDS[$(($argc - 1 ))]}"
prev="${COMP_WORDS[$(($argc - 2 ))]}"
cur="${COMP_WORDS[argc]}"
cur_first_char=${cur:0:1}
opts_short="-h -v -c -f -l"
opts="--help --server --verbose --clipboard --noclipboard --force --edit --view --remove --list --check-keys --update-keys --list-roles --recrypt-roles --roles --list-servers"
mkdir -p -m 700 "$role_dir"
mkdir -p -m 700 "$pass_dir"
find "$role_dir" -type f -mmin +$timeout -exec rm -f {} \;
find "$pass_dir" -type f -mmin +$timeout -exec rm -f {} \;
# On détermine si on utilsie un serveur alternatif
if contain "--server" "${COMP_WORDS[*]}"; then
if [[ "$prev" = "--server" ]]; then
_cranspasswords_server=$last;
fi
else
_cranspasswords_server="default";
fi
server=$_cranspasswords_server
# les options possibles pour notre auto-complétion
if [[ $cur_first_char = "-" ]]; then
COMPREPLY=( $(compgen -W "$opts" -- $cur ) )
return 0
fi
if [[ "$last" = "--server" ]]; then
server_list="`cranspasswords --list-servers | grep -- "*" | awk '{print $2}'`"
COMPREPLY=( $(compgen -W "$server_list" -- $cur ) )
return 0
fi
if [[ "$last" = "--roles" ]]; then
if [ ! -f "${role_dir}$server" ]; then
echo "`cranspasswords --server $server --list-roles | grep -- "*" | awk '{print $2}'`" > "${role_dir}$server"
fi
role_list="`cat "${role_dir}$server"`"
COMPREPLY=( $(compgen -W "$role_list" -- $cur ) )
return 0
fi
if [[ "$last" = "--edit" ]]; then
if [ ! -f "${pass_dir}${server}-w" ]; then
echo "`cranspasswords --server $server -l | grep "+" | awk '{print $2}'`" > "${pass_dir}${server}-w"
fi
pass_list="`cat "${pass_dir}${server}-w"`"
COMPREPLY=( $(compgen -W "$pass_list" -- $cur ) )
return 0
fi
if true; then
if [ ! -f "${pass_dir}$server" ]; then
echo "`cranspasswords --server $server -l | grep "\( +\| -\)" | awk '{print $2}'`" > "${pass_dir}$server"
fi
pass_list="`cat "${pass_dir}$server"`"
COMPREPLY=( $(compgen -W "$pass_list" -- $cur ) )
return 0
fi
}
# On active l'auto-completion
complete -F _cranspasswords cranspasswords
#!/bin/bash
sudo /root/cranspasswords/server.py $*
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