~launchpad-pqm/launchpad/devel

10637.3.1 by Guilherme Salgado
Use the default python version instead of a hard-coded version
1
#!/usr/bin/python -S
8452.3.3 by Karl Fogel
* utilities/: Add copyright header block to source files that were
2
#
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
3
# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
8687.15.3 by Karl Fogel
Shorten the copyright header block to two lines.
4
# GNU Affero General Public License version 3 (see the file LICENSE).
8452.3.3 by Karl Fogel
* utilities/: Add copyright header block to source files that were
5
6488.1.3 by Jonathan Lange
Docstrings ahoy!
6
"""Create a user for testing the local Launchpad.
7
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
8
Usage: make-lp-user <username> [<team1> <team2> ...] [-e email]
6488.1.3 by Jonathan Lange
Docstrings ahoy!
9
10
This script will create a usable Launchpad user in the development database to
11
help you test a locally running copy of Launchpad.
12
13
You can add this user to teams by specifying them on the command-line. For
14
example:
15
16
  make-lp-user fred vcs-imports bazaar-experts
17
18
will create a user 'fred' and add them to the 'vcs-imports' and
19
'bazaar-experts' teams.
20
21
In addition, this script will look in your ~/.ssh directory for public keys
22
and register them for the created user.
23
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
24
If you pass an email address, the new user will have this email address
25
as well as any GPG keys you have for it.
26
6488.1.3 by Jonathan Lange
Docstrings ahoy!
27
The login details will be printed to stdout.
28
29
Please note that this script is for testing purposes only. Do NOT use it in
30
production environments.
31
"""
32
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
33
import _pythonpath
34
35
import os
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
36
from optparse import OptionParser
37
import re
38
import subprocess
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
39
import sys
40
import transaction
41
10427.4.6 by Jeroen Vermeulen
Final polish. I hope.
42
from storm.store import Store
43
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
44
from zope.component import getUtility
45
46
from canonical.launchpad.interfaces import (
47
    IPersonSet,
48
    ISSHKeySet,
49
    TeamMembershipStatus,
50
    )
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
51
from canonical.launchpad.interfaces.gpghandler import IGPGHandler
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
52
from canonical.launchpad.scripts import execute_zcml_for_scripts
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
53
from lp.registry.interfaces.gpg import GPGKeyAlgorithm, IGPGKeySet
8440.1.1 by Curtis Hovey
Updated all imports or LaunchpadObjectFactory to come from lp.test.factory.
54
from lp.testing.factory import LaunchpadObjectFactory
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
55
56
# Shut up, pyflakes.
57
_pythonpath = _pythonpath
58
59
60
DEFAULT_PASSWORD = 'test'
61
factory = LaunchpadObjectFactory()
62
63
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
64
def make_person(username, email):
6488.1.3 by Jonathan Lange
Docstrings ahoy!
65
    """Create and return a person with the given username.
66
67
    The email address for the user will be <username>@example.com. The
68
    password will be the value of `DEFAULT_PASSWORD`.
69
70
    These details will be printed to stdout.
71
    """
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
72
    person = factory.makePerson(
73
        name=username, password=DEFAULT_PASSWORD, email=email)
74
    print "username: %s" % (username,)
75
    print "email:    %s" % (email,)
76
    print "password: %s" % (DEFAULT_PASSWORD,)
77
    return person
78
79
80
def add_person_to_teams(person, team_names):
6488.1.3 by Jonathan Lange
Docstrings ahoy!
81
    """Add `person` to the teams named in `team_names`.
82
83
    `person` is provided as its own review, team membership status is always
84
    `APPROVED`. This function will add users even to restricted teams.
85
86
    A list of teams joined will be printed to stdout.
87
    """
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
88
    person_set = getUtility(IPersonSet)
6488.1.4 by Jonathan Lange
Slightly more error handling.
89
    teams_joined = []
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
90
    for team_name in team_names:
91
        team = person_set.getByName(team_name)
6488.1.4 by Jonathan Lange
Slightly more error handling.
92
        if team is None:
93
            print "ERROR: %s not found." % (team_name,)
94
            continue
95
        if not team.is_team:
96
            print "ERROR: %s is not a team." % (team_name,)
97
            continue
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
98
        team.addMember(
99
            person, person, status=TeamMembershipStatus.APPROVED)
6488.1.4 by Jonathan Lange
Slightly more error handling.
100
        teams_joined.append(team_name)
101
    print "teams:    %s" % ' '.join(teams_joined)
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
102
103
104
def add_ssh_public_keys(person):
6488.1.3 by Jonathan Lange
Docstrings ahoy!
105
    """Look for public keys and register them for `person`.
106
107
    This function looks in ~/.ssh/id_rsa.pub and ~/.ssh/id_dsa.pub for SSH
108
    public keys and registers them as SSH keys for `person`.
109
    """
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
110
    ssh_dir = os.path.expanduser('~/.ssh')
111
    key_set = getUtility(ISSHKeySet)
112
    key_guesses = [
11182.3.1 by Jelmer Vernooij
Cope with ISSHKeySet.new() only taking three arguments.
113
        ("ssh-rsa", 'id_rsa.pub'),
114
        ("ssh-dsa", 'id_dsa.pub'),
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
115
        ]
116
    for key_type, guessed_filename in key_guesses:
117
        guessed_filename = os.path.join(ssh_dir, guessed_filename)
118
        try:
119
            public_key_file = open(guessed_filename, 'r')
120
            try:
121
                public_key = public_key_file.read()
122
            finally:
123
                public_key_file.close()
124
        except (OSError, IOError):
125
            continue
126
        public_key = public_key.split()[1]
11182.3.1 by Jelmer Vernooij
Cope with ISSHKeySet.new() only taking three arguments.
127
        key_set.new(person,
128
            "%s %s %s" % (key_type, public_key, 'Added by utility script.'))
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
129
        print 'Registered SSH key: %s' % (guessed_filename,)
130
131
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
132
def parse_fingerprints(gpg_output):
133
    """Find key fingerprints in "gpg --fingerprint <email>" output."""
134
    line_prefix = re.compile('\s*Key fingerprint\s*=\s*')
135
    return [
136
        ''.join(re.sub(line_prefix, '', line).split())
137
        for line in gpg_output.splitlines()
138
        if line_prefix.match(line)
139
        ]
140
141
142
def run_native_gpg(arguments):
143
    """Run GPG using the user's real keyring."""
144
    # Need to override GNUPGHOME or we'll get a dummy GPG in a temp
145
    # directory, which won't find any keys.
146
    env = os.environ.copy()
147
    if 'GNUPGHOME' in env:
148
        del env['GNUPGHOME']
10427.4.8 by Jeroen Vermeulen
Beware of translated gpg, thanks adeuring for spotting that.
149
150
    # Prevent translated gpg output from messing up our parsing.
151
    env['LC_ALL'] = 'C'
152
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
153
    command_line = ['gpg'] + arguments
154
    pipe = subprocess.Popen(
155
        command_line, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
156
    stdout, stderr = pipe.communicate()
157
    if stderr != '':
158
        print stderr
159
    if pipe.returncode != 0:
160
        raise Exception('GPG error during "%s"' % ' '.join(command_line))
161
162
    return stdout
163
164
165
def add_gpg_key(person, fingerprint):
166
    """Add the GPG key with the given fingerprint to `person`."""
167
    run_native_gpg([
168
        '--keyserver', 'keyserver.launchpad.dev',
169
        '--send-key', fingerprint
170
        ])
171
172
    gpghandler = getUtility(IGPGHandler)
173
    key = gpghandler.retrieveKey(fingerprint)
174
175
    gpgkeyset = getUtility(IGPGKeySet)
176
    if gpgkeyset.getByFingerprint(fingerprint) is not None:
177
        # We already have this key.
178
        return
179
180
    algorithm = GPGKeyAlgorithm.items[key.algorithm]
181
    can_encrypt = True
182
    lpkey = gpgkeyset.new(
183
        person.id, key.keyid, fingerprint, key.keysize, algorithm,
184
        active=True, can_encrypt=can_encrypt)
185
    Store.of(person).add(lpkey)
186
187
188
def attach_gpg_keys(email, person):
189
    """Attach the GPG key(s) for `email` to `person`."""
190
    output = run_native_gpg(['--fingerprint', email])
191
192
    fingerprints = parse_fingerprints(output)
193
    if len(fingerprints) == 0:
194
        print "No GPG key fingerprints found!"
195
    for fingerprint in fingerprints:
196
        add_gpg_key(person, fingerprint)
197
198
199
def parse_args(arguments):
200
    """Parse command-line arguments.
201
202
    :return: options object.  Among the options are username (a
203
        string) and optionally teams (a list).
204
    """
205
    parser = OptionParser(description="Create a local Launchpad user.")
206
    parser.add_option(
207
        '-e', '--email', action='store', dest='email', default=None,
208
        help="Email address; set to use real GPG key for this address.")
10427.4.4 by Jeroen Vermeulen
Better options processing.
209
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
210
    options, args = parser.parse_args(arguments)
211
    if len(args) == 0:
212
        print __doc__
10427.4.4 by Jeroen Vermeulen
Better options processing.
213
        sys.exit(2)
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
214
215
    options.username = args[0]
216
    options.teams = args[1:]
217
218
    return options
219
220
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
221
def main(arguments):
6488.1.3 by Jonathan Lange
Docstrings ahoy!
222
    """Run the script."""
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
223
    options = parse_args(arguments)
224
    if options.email is None:
225
    	email = '%s@example.com' % options.username
226
    else:
227
        email = options.email
228
10427.4.4 by Jeroen Vermeulen
Better options processing.
229
    execute_zcml_for_scripts()
230
    transaction.begin()
231
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
232
    person = make_person(options.username, email)
233
    add_person_to_teams(person, options.teams)
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
234
    add_ssh_public_keys(person)
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
235
236
    if options.email is not None:
237
        attach_gpg_keys(options.email, person)
238
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
239
    transaction.commit()
10427.4.2 by Jeroen Vermeulen
Moved user/gpg creation into make-lp-user.
240
6488.1.1 by Jonathan Lange
Initial commit of utility to make a user on development environment.
241
    return 0
242
243
244
if __name__ == '__main__':
245
    sys.exit(main(sys.argv[1:]))