Statistics
| Branch: | Tag: | Revision:

root / vncauthproxy / passwd.py @ c7da5329

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
    random.shuffle(chars)
57

    
58
    return "".join(random.choice(chars) for x in range(16))
59

    
60

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

    
66

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

    
72

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

    
80
    return None
81

    
82

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

    
93

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

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

    
104
    (idx, line) = user_line
105
    lines.remove(line)
106
    return lines
107

    
108

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

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

    
118
    newline = gen_hash(user, password)
119

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

    
130
    return lines
131

    
132

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

    
138
        user = args.user[0]
139
        passwdfile = args.passwdfile[0]
140

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

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

    
150
        write_wrapper(passwdfile, lines, args.dry_run)
151
    except KeyboardInterrupt:
152
        pass
153

    
154
    sys.exit(0)