~sidnei/zope3/reduce-deps

« back to all changes in this revision

Viewing changes to src/zope/password/zpasswd.py

  • Committer: Sidnei da Silva
  • Date: 2010-07-05 20:15:44 UTC
  • Revision ID: sidnei.da.silva@canonical.com-20100705201544-nl51oynbbclpyz2o
- Downgrade zope.password

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
##############################################################################
2
 
#
3
 
# Copyright (c) 2004 Zope Foundation and Contributors.
4
 
# All Rights Reserved.
5
 
#
6
 
# This software is subject to the provisions of the Zope Public License,
7
 
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
 
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
 
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
 
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
 
# FOR A PARTICULAR PURPOSE.
12
 
#
13
 
##############################################################################
14
 
"""Implementation of the zpasswd script.
15
 
 
16
 
$Id: zpasswd.py 112149 2010-05-07 15:40:42Z ulif $
17
 
"""
18
 
import optparse
19
 
import os
20
 
import pkg_resources
21
 
import sys
22
 
from xml.sax.saxutils import quoteattr
23
 
 
24
 
VERSION = pkg_resources.get_distribution('zope.password').version
25
 
 
26
 
def main(argv=None):
27
 
    """Top-level script function to create a new principals."""
28
 
    if argv is None:
29
 
        argv = sys.argv
30
 
    try:
31
 
        options = parse_args(argv)
32
 
    except SystemExit, e:
33
 
        if e.code:
34
 
            return 2
35
 
        else:
36
 
            return 0
37
 
    app = Application(options)
38
 
    try:
39
 
        return app.process()
40
 
    except KeyboardInterrupt:
41
 
        return 1
42
 
    except SystemExit, e:
43
 
        return e.code
44
 
 
45
 
class Principal(object):
46
 
    """Principal.
47
 
 
48
 
    >>> principal = Principal("id", "title", "login", "password")
49
 
    >>> print principal
50
 
      <principal
51
 
        id="id"
52
 
        title="title"
53
 
        login="login"
54
 
        password="password"
55
 
        />
56
 
 
57
 
    >>> principal = Principal("id", "title", "login", "password",
58
 
    ...     "description", "SHA1")
59
 
    >>> print principal
60
 
      <principal
61
 
        id="id"
62
 
        title="title"
63
 
        login="login"
64
 
        password="password"
65
 
        description="description"
66
 
        password_manager="SHA1"
67
 
        />
68
 
    """
69
 
 
70
 
    def __init__(self, id, title, login, password,
71
 
            description="", password_manager_name="Plain Text"):
72
 
        self.id = id
73
 
        self.login = login
74
 
        self.password = password
75
 
        self.title = title
76
 
        self.description = description
77
 
        self.password_manager_name = password_manager_name
78
 
 
79
 
    def getLines(self):
80
 
        lines = [
81
 
            '  <principal',
82
 
            '    id=%s' % quoteattr(self.id),
83
 
            '    title=%s' % quoteattr(self.title),
84
 
            '    login=%s' % quoteattr(self.login),
85
 
            '    password=%s' % quoteattr(self.password)
86
 
            ]
87
 
        if self.description:
88
 
            lines.append('    description=%s' % quoteattr(self.description))
89
 
        if self.password_manager_name != "Plain Text":
90
 
            lines.append('    password_manager=%s'
91
 
                % quoteattr(self.password_manager_name))
92
 
        lines.append('    />')
93
 
        return lines
94
 
 
95
 
    def __str__(self):
96
 
        return "\n".join(self.getLines())
97
 
 
98
 
TITLE = """
99
 
============================================
100
 
Principal information for inclusion in ZCML:
101
 
"""
102
 
 
103
 
ID_TITLE = """
104
 
Please choose an id for the principal.
105
 
"""
106
 
 
107
 
TITLE_TITLE = """
108
 
Please choose a title for the principal.
109
 
"""
110
 
 
111
 
LOGIN_TITLE = """
112
 
Please choose a login for the principal.
113
 
"""
114
 
 
115
 
PASSWORD_TITLE = """
116
 
Please provide a password for the principal.
117
 
"""
118
 
 
119
 
DESCRIPTION_TITLE = """
120
 
Please provide an optional description for the principal.
121
 
"""
122
 
 
123
 
class Application(object):
124
 
 
125
 
    title = TITLE
126
 
    id_title = ID_TITLE
127
 
    title_title = TITLE_TITLE
128
 
    login_title = LOGIN_TITLE
129
 
    password_title = PASSWORD_TITLE
130
 
    description_title = DESCRIPTION_TITLE
131
 
 
132
 
    def __init__(self, options):
133
 
        self.options = options
134
 
        self.need_blank_line = False
135
 
 
136
 
    def read_input_line(self, prompt):
137
 
        # The tests replace this to make sure the right things happen.
138
 
        return raw_input(prompt)
139
 
 
140
 
    def read_password(self, prompt):
141
 
        # The tests replace this to make sure the right things happen.
142
 
        import getpass
143
 
        try:
144
 
            return getpass.getpass(prompt)
145
 
        except KeyboardInterrupt:
146
 
            # The cursor was left on the same line as the prompt,
147
 
            # which we don't like.  Print a blank line.
148
 
            print
149
 
            raise
150
 
 
151
 
    def process(self):
152
 
        options = self.options
153
 
 
154
 
        if not options.destination:
155
 
            destination = sys.stdout
156
 
        else:
157
 
            destination = open(options.destination, "wb")
158
 
 
159
 
        principal = self.get_principal()
160
 
 
161
 
        if destination is sys.stdout:
162
 
            print self.title
163
 
        print >>destination, principal
164
 
        print
165
 
 
166
 
        return 0
167
 
 
168
 
    def get_principal(self):
169
 
        id = self.get_value(self.id_title, "Id: ", "Id may not be empty")
170
 
        title = self.get_value(self.title_title, "Title: ",
171
 
            "Title may not be empty")
172
 
        login = self.get_value(self.login_title, "Login: ",
173
 
            "Login may not be empty")
174
 
        password_manager_name, password_manager = self.get_password_manager()
175
 
        password = self.get_password()
176
 
        description = self.get_value(self.description_title, "Description: ",)
177
 
 
178
 
        password = password_manager.encodePassword(password)
179
 
        return Principal(id, title, login, password, description,
180
 
            password_manager_name)
181
 
 
182
 
    def get_value(self, title, prompt, error=""):
183
 
        self.print_message(title)
184
 
        self.need_blank_line = True
185
 
        while True:
186
 
            value = self.read_input_line(prompt).strip()
187
 
            if not value and error:
188
 
                print >>sys.stderr, error
189
 
                continue
190
 
            return value
191
 
 
192
 
    def get_password_manager(self):
193
 
        default = 0
194
 
        self.print_message("Password manager:")
195
 
        print
196
 
        managers = self.options.managers
197
 
 
198
 
        for i, (name, manager) in enumerate(managers):
199
 
            print "% i. %s" % (i + 1, name)
200
 
            if name == 'SSHA':
201
 
                default = i
202
 
        print
203
 
        self.need_blank_line = True
204
 
        while True:
205
 
            password_manager = self.read_input_line(
206
 
                "Password Manager Number [%s]: " % (default + 1))
207
 
            if not password_manager:
208
 
                index = default
209
 
                break
210
 
            elif password_manager.isdigit():
211
 
                index = int(password_manager)
212
 
                if index > 0 and index <= len(managers):
213
 
                    index -= 1
214
 
                    break
215
 
            print >>sys.stderr, "You must select a password manager"
216
 
        print "%s password manager selected" % managers[index][0]
217
 
        return managers[index]
218
 
 
219
 
    def get_password(self):
220
 
        self.print_message(self.password_title)
221
 
        while True:
222
 
            password = self.read_password("Password: ")
223
 
            if not password:
224
 
                print >>sys.stderr, "Password may not be empty"
225
 
                continue
226
 
            if password != password.strip() or password.split() != [password]:
227
 
                print >>sys.stderr, "Password may not contain spaces"
228
 
                continue
229
 
            break
230
 
        again = self.read_password("Verify password: ")
231
 
        if again != password:
232
 
            print >>sys.stderr, "Password not verified!"
233
 
            sys.exit(1)
234
 
        return password
235
 
 
236
 
    def print_message(self, message):
237
 
        if self.need_blank_line:
238
 
            print
239
 
            self.need_blank_line = False
240
 
        print message
241
 
 
242
 
def get_password_managers(config_path=None):
243
 
    if not config_path:
244
 
        from zope.password.password import managers
245
 
    else:
246
 
        from zope.configuration import xmlconfig
247
 
        from zope.component import getUtilitiesFor
248
 
        from zope.password.interfaces import IPasswordManager
249
 
 
250
 
        print "Loading configuration..."
251
 
        config = xmlconfig.file(config_path)
252
 
        managers = []
253
 
        for name, manager in getUtilitiesFor(IPasswordManager):
254
 
            if name == "Plain Text":
255
 
                managers.insert(0, (name, manager))
256
 
            else:
257
 
                managers.append((name, manager))
258
 
        if not managers:
259
 
            from zope.password.password import managers
260
 
    return managers
261
 
 
262
 
def parse_args(argv):
263
 
    """Parse the command line, returning an object representing the input."""
264
 
    path, prog = os.path.split(os.path.realpath(argv[0]))
265
 
    p = optparse.OptionParser(prog=prog,
266
 
                              usage="%prog [options]",
267
 
                              version=VERSION)
268
 
    p.add_option("-c", "--config", dest="config", metavar="FILE",
269
 
        help=("path to the site.zcml configuration file"
270
 
        " (more accurate but slow password managers registry creation)"))
271
 
    p.add_option("-o", "--output", dest="destination", metavar="FILE",
272
 
        help=("the file in which the output will be saved"
273
 
        " (STDOUT by default)"))
274
 
    options, args = p.parse_args(argv[1:])
275
 
    options.managers = get_password_managers(options.config)
276
 
    options.program = prog
277
 
    options.version = VERSION
278
 
    if args:
279
 
        p.error("too many arguments")
280
 
    return options