~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to scripts/usrmgt-server

  • Committer: wagrant
  • Date: 2008-07-16 00:24:07 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:879
diffservice, fileservice_lib: Factor out conversion of strings to
      revision specifications, to common.svn.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
import os
 
4
import pysvn
 
5
import subprocess
 
6
import sys
 
7
import urllib
 
8
from functools import partial
 
9
import traceback
 
10
import logging
 
11
import errno
 
12
 
 
13
import conf
 
14
import common.db
 
15
import common.chat
 
16
import common.makeuser
 
17
import common.studpath
 
18
 
 
19
# usage:
 
20
#   usrmgt-server <port> <magic>
 
21
 
 
22
# User management operations:
 
23
#   - Create local user
 
24
#   - [Re]Create jail for a user
 
25
#       - Create a svn repository for a user
 
26
#           - create repository
 
27
#           - svn config
 
28
#           - svn auth
 
29
#       - Checkout repository as home directory
 
30
#       - /etc/passwd entry
 
31
#   - Disable a user's account
 
32
#   - Enable a user's account
 
33
#   - Remove a user
 
34
#   - Rebuild svn config
 
35
#   - Rebuild svn auth file
 
36
#   - Rebuild passwd + push to nodes.
 
37
 
 
38
def create_user(props):
 
39
    """Create the database record for the given user.
 
40
       Expected properties:
 
41
        login       - used as a unix login name and svn repository name.
 
42
                      STRING REQUIRED 
 
43
        unixid      - the unix uid under which execution will take place
 
44
                      on the behalf of the user. Don't use 0! If not specified
 
45
                      or None, one will be allocated from the configured
 
46
                      numeric range.
 
47
                      INT OPTIONAL
 
48
        password    - the clear-text password for the user. If this property is
 
49
                      absent or None, this is an indication that external
 
50
                      authentication should be used (i.e. LDAP).
 
51
                      STRING OPTIONAL
 
52
        email       - the user's email address.
 
53
                      STRING OPTIONAL
 
54
        nick        - the display name to use.
 
55
                      STRING REQUIRED
 
56
        fullname    - The name of the user for results and/or other official
 
57
                      purposes.
 
58
                      STRING REQUIRED
 
59
        rolenm      - The user's role. Must be one of "anyone", "student",
 
60
                      "tutor", "lecturer", "admin".
 
61
                      STRING/ENUM REQUIRED
 
62
        studentid   - If supplied and not None, the student id of the user for
 
63
                      results and/or other official purposes.
 
64
                      STRING OPTIONAL
 
65
       Return Value: the uid associated with the user. INT
 
66
    """
 
67
 
 
68
    if 'unixid' not in props or props['unixid'] is None:
 
69
        # For the time being, just choose a random unixid.
 
70
        # This may result in duplicates, but the unixid is essentially
 
71
        # a monitoring/logging convenience only. Oh, and dups could kill
 
72
        # each other. <sigh>
 
73
        props['unixid'] = random.randrange(5000,10000)
 
74
        # raise NotImplementedError, "No algorithm for creating uids yet!"
 
75
        # unixid = invent-uid
 
76
        # props['unixid'] = unixid
 
77
 
 
78
    common.makeuser.make_user_db(**props)
 
79
 
 
80
    return int(props['unixid'])
 
81
 
 
82
def update_user(props):
 
83
    """Create the database record for the given user.
 
84
       Expected properties:
 
85
        login       - user who is making the change (not necessarily the one
 
86
                      being updated).
 
87
        update      - dict of fields to be updated. The fields are all those
 
88
                      in the login table of the db.
 
89
                      'login' required.
 
90
                      Omitted fields will not be set.
 
91
    """
 
92
    update = props['update']
 
93
    # Note: "login" is special - it is not part of the kwargs to
 
94
    # db.update_user, but the first arg - used to find the user to update.
 
95
    # However it doesn't need any special treatment here.
 
96
 
 
97
    db = common.db.DB()
 
98
    db.update_user(**update)
 
99
    db.close()
 
100
 
 
101
def get_login(login, passwd, realm, existing_login, _may_save):
 
102
    """Callback function used by pysvn for authentication.
 
103
    login, passwd: Credentials of the user attempting to log in.
 
104
        passwd in clear (this should be the user's svn_pass, a
 
105
        randomly-generated permanent password for this user, not their main
 
106
        IVLE password).
 
107
    realm, existing_login, _may_save: The 3 arguments passed by pysvn to
 
108
        callback_get_login.
 
109
        The following has been determined empirically, not from docs:
 
110
        existing_login will be the name of the user who owns the process on
 
111
        the first attempt, "" on subsequent attempts. We use this fact.
 
112
    """
 
113
    logging.debug("Getting password for %s (realm %s)" % (login, realm))
 
114
    # Only provide credentials on the _first_ attempt.
 
115
    # If we're being asked again, then it means the credentials failed for
 
116
    # some reason and we should just fail. (This is not desirable, but it's
 
117
    # better than being asked an infinite number of times).
 
118
    if existing_login == "":
 
119
        logging.warning("Could not authenticate SVN for %s" % login)
 
120
        return (False, login, passwd, False)
 
121
    else:
 
122
        return (True, login, passwd, False)
 
123
 
 
124
def activate_user(props):
 
125
    """Create the on-disk stuff for the given user.
 
126
       Sets the state of the user in the db from pending to enabled.
 
127
       Expected properties:
 
128
        login       - the user name for the jail
 
129
                      STRING REQUIRED
 
130
       Return Value: None
 
131
    """
 
132
 
 
133
    os.umask(0022) # Bad, but start_server sets it worse.
 
134
 
 
135
    login = props['login']
 
136
 
 
137
    db = common.db.DB()
 
138
 
 
139
    try:
 
140
 
 
141
        # FIXME: check we're pending
 
142
 
 
143
        details = db.get_user(login)
 
144
 
 
145
        # make svn config/auth
 
146
 
 
147
        logging.debug("Creating repo")
 
148
        common.makeuser.make_svn_repo(login, throw_on_error=False)
 
149
        logging.debug("Creating svn config")
 
150
        common.makeuser.make_svn_config(login, throw_on_error=False)
 
151
        logging.debug("Creating svn auth")
 
152
        passwd = common.makeuser.make_svn_auth(login, throw_on_error=False)
 
153
        logging.debug("passwd: %s" % passwd)
 
154
 
 
155
        svn = pysvn.Client()
 
156
        svn.callback_get_login = partial(get_login, login, passwd)
 
157
 
 
158
        if conf.svn_addr[-1] != '/':
 
159
            conf.svn_addr = conf.svn_addr + "/"
 
160
    
 
161
        # FIXME: This should be a loop over enrolements.
 
162
        #        We're not going to fix this now because it requires
 
163
        #        a large amount of admin console work to manage subject
 
164
        #        offerings.
 
165
        #        Instead, we're just going to use a single offering with
 
166
        #        a "short" name of "info1".
 
167
        logging.debug("Creating /info1")
 
168
        try:
 
169
            svn.mkdir(conf.svn_addr + login + "/info1",
 
170
                      "Initial creation of work directory for Informatics 1")
 
171
        except Exception, exc:
 
172
            logging.warning("While mkdiring info1: %s" % str(exc))
 
173
            pass
 
174
        logging.debug("Creating /stuff")
 
175
        try:
 
176
            svn.mkdir(conf.svn_addr + login + "/stuff",
 
177
                      "Initial creation of directory for miscellania")
 
178
        except Exception, exc:
 
179
            logging.warning("While mkdiring stuff: %s" % str(exc))
 
180
            pass
 
181
 
 
182
        logging.debug("Creating jail")
 
183
        common.makeuser.make_jail(login, details.unixid, svn_pass=passwd)
 
184
 
 
185
        common.makeuser.mount_jail(login)
 
186
 
 
187
        do_checkout(props, svn_pass=passwd)
 
188
 
 
189
        # FIXME: should this be nicer?
 
190
        os.system("chown -R %d:%d %s" \
 
191
                % (details.unixid, details.unixid,
 
192
                   common.studpath.url_to_local(login)[1]))
 
193
 
 
194
        logging.info("Enabling user")
 
195
        db.update_user(login, state='enabled')
 
196
 
 
197
        return {"response": "okay"}
 
198
 
 
199
    finally:
 
200
        db.close()
 
201
 
 
202
def do_checkout(props, svn_pass=None):
 
203
    """Create the default contents of a user's jail by checking out from the
 
204
    repositories.
 
205
    If any directory already exists, just fail silently (so this is not a
 
206
    "wipe-and-checkout", merely a checkout if it failed or something).
 
207
       Expected properties:
 
208
        login       - the user name for the jail
 
209
                      STRING REQUIRED
 
210
       Return Value: None
 
211
    """
 
212
    login = props['login']
 
213
 
 
214
    db = common.db.DB()
 
215
    user = db.get_user(login)
 
216
    # If svn_pass isn't supplied, grab it from the DB
 
217
    if svn_pass is None:
 
218
        svn_pass = user.svn_pass
 
219
    db.close()
 
220
 
 
221
    svn = pysvn.Client()
 
222
    svn.callback_get_login = partial(get_login, login, svn_pass)
 
223
 
 
224
    if conf.svn_addr[-1] != os.sep:
 
225
        conf.svn_addr += os.sep
 
226
 
 
227
    # FIXME: <hack>
 
228
 
 
229
    logging.debug("Checking out directories in the jail")
 
230
    try:
 
231
        svn.checkout(conf.svn_addr + login + "/stuff",
 
232
                     common.studpath.url_to_local(login + "/stuff")[1])
 
233
        os.system("chown -R %d:%d %s" \
 
234
                % (user.unixid, user.unixid,
 
235
                   common.studpath.url_to_local(login + "/stuff")[1]))
 
236
    except Exception, exc:
 
237
        logging.warning("While mkdiring stuff: %s" % str(exc))
 
238
        pass
 
239
    try:
 
240
        svn.checkout(conf.svn_addr + login + "/info1",
 
241
                     common.studpath.url_to_local(login + "/info1")[1])
 
242
        os.system("chown -R %d:%d %s" \
 
243
                % (user.unixid, user.unixid,
 
244
                   common.studpath.url_to_local(login + "/info1")[1]))
 
245
    except Exception, exc:
 
246
        logging.warning("While mkdiring info1: %s" % str(exc))
 
247
        pass
 
248
 
 
249
    # FIXME: </hack>
 
250
 
 
251
    return {"response": "okay"}
 
252
 
 
253
actions = {
 
254
        'create_user':create_user,
 
255
        'update_user':update_user,
 
256
        'activate_user':activate_user,
 
257
        'do_checkout':do_checkout,
 
258
    }
 
259
 
 
260
def initializer():
 
261
    try:
 
262
        pidfile = open('/var/run/usrmgt-server.pid', 'w')
 
263
        pidfile.write('%d\n' % os.getpid())
 
264
        pidfile.close()
 
265
    except IOError, (errno, strerror):
 
266
        print "Couldn't write PID file. IO error(%s): %s" % (errno, strerror)
 
267
        sys.exit(1)
 
268
 
 
269
def dispatch(props):
 
270
    logging.debug(repr(props))
 
271
    action = props.keys()[0]
 
272
    return actions[action](props[action])
 
273
 
 
274
if __name__ == "__main__":
 
275
    if len(sys.argv) <3:
 
276
        print >>sys.stderr, "Usage: usrmgt-server <port> <magic>"
 
277
        sys.exit(1)
 
278
    port = int(sys.argv[1])
 
279
    magic = sys.argv[2]
 
280
 
 
281
    pid = os.getpid()
 
282
 
 
283
    logging.basicConfig(filename="/var/log/usrmgt.log", level=logging.INFO)
 
284
    logging.info("Starting usrmgt server on port %d (pid = %d)" % (port, pid))
 
285
 
 
286
    common.chat.start_server(port, magic, True, dispatch, initializer)