Statistics
| Branch: | Tag: | Revision:

root / vncauthproxy / passwd.py @ 5861b3d5

History | View | Annotate | Download (4.3 kB)

1
#!/usr/bin/env python
2
"""
3
vncauthproxy-passwd - vncauthproxy passwd file mgmt tool
4
"""
5
#
6
# Copyright (c) 2010-2013 Greek Research and Technology Network S.A.
7
#
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
12
#
13
# This program is distributed in the hope that it will be useful, but
14
# WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
# General Public License for more details.
17
#
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21
# 02110-1301, USA.
22

    
23
import argparse
24
import crypt
25
import getpass
26
import os
27
import random
28
import re
29
import string
30
import sys
31
import tempfile
32

    
33

    
34
def parse_arguments():
35
    """ Parse cli args. """
36
    parser = argparse.ArgumentParser()
37

    
38
    parser.add_argument("-n", "--dry-run", action="store_true", dest="dry_run",
39
                        help="Display the results on stdout without updating "
40
                        "the passwd file")
41
    parser.add_argument("-D", "--delete", action="store_true",
42
                        dest="delete_user", help="Delete user from file")
43
    parser.add_argument("passwdfile", metavar="file", type=str, nargs=1,
44
                        help="Path to the passwd file")
45
    parser.add_argument("user", metavar="user", type=str, nargs=1,
46
                        help="User to edit")
47

    
48
    args = parser.parse_args()
49

    
50
    return args
51

    
52

    
53
def gen_salt():
54
    """ Generate 16-char salt string. """
55
    chars = list(string.ascii_letters + string.digits + "./")
56
    return "".join(random.choice(chars) for x in range(16))
57

    
58

    
59
def gen_hash(username, password):
60
    """ Generate SHA-512 hash in crypt() format. """
61
    salt = "$6$%s$" % gen_salt()
62
    return "%s:%s\n" % (username, crypt.crypt(password, salt))
63

    
64

    
65
def fail(reason):
66
    """ Print reason for failure and exit. """
67
    sys.stderr.write("%s\n" % reason)
68
    sys.exit(1)
69

    
70

    
71
def find_user(lines, user):
72
    """ Return user info from passwd file, if the users exists. """
73
    for (idx, line) in enumerate(lines):
74
        (username, _) = line.split(":", 1)
75
        if user == username:
76
            return (idx, line)
77

    
78
    return None
79

    
80

    
81
def write_wrapper(passwdfile, lines, dry_run):
82
    """ Dry-run wrapper for write. """
83
    if not dry_run:
84
        (fd, fpath) = tempfile.mkstemp(dir=os.path.dirname(passwdfile))
85
        with os.fdopen(fd, "w+") as f:
86
            f.write("".join(lines))
87
        os.rename(fpath, passwdfile)
88
    else:
89
        sys.stdout.write("".join(lines))
90

    
91

    
92
def delete_user(user, passwdfile):
93
    """ Delete user from passwdfile. """
94
    if not os.path.isfile(passwdfile):
95
        fail("Cannot delete user from non-existent file")
96

    
97
    lines = open(passwdfile).readlines()
98
    user_line = find_user(lines, user)
99
    if not user_line:
100
        fail("User not found!")
101

    
102
    (idx, line) = user_line
103
    lines.remove(line)
104
    return lines
105

    
106

    
107
def add_or_update_user(user, passwdfile):
108
    """ Add or update user from passwdfile. """
109
    password = getpass.getpass()
110
    if password == "":
111
        fail("Password cannot be empty")
112

    
113
    if password != getpass.getpass("Retype password: "):
114
        fail("Passwords don't match")
115

    
116
    newline = gen_hash(user, password)
117

    
118
    lines = [newline]
119
    if os.path.isfile(passwdfile):
120
        lines = open(passwdfile).readlines()
121
        user_line = find_user(lines, user)
122
        if not user_line:
123
            lines.append(newline)
124
        else:
125
            (idx, _) = user_line
126
            lines[idx] = newline
127

    
128
    return lines
129

    
130

    
131
def main():
132
    """ Run the tool from the command line. """
133
    try:
134
        args = parse_arguments()
135

    
136
        user = args.user[0]
137
        passwdfile = args.passwdfile[0]
138

    
139
        user_re = r'^[a-z_][a-z0-9_]{0,30}$'
140
        if re.match(user_re, user) is None:
141
            fail("Username must match the following regexp: %s" % user_re)
142

    
143
        if args.delete_user:
144
            lines = delete_user(user, passwdfile)
145
        else:
146
            lines = add_or_update_user(user, passwdfile)
147

    
148
        write_wrapper(passwdfile, lines, args.dry_run)
149
    except KeyboardInterrupt:
150
        pass
151

    
152
    sys.exit(0)