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

« back to all changes in this revision

Viewing changes to scripts/usrmgt-server

  • Committer: William Grant
  • Date: 2008-07-07 01:45:11 UTC
  • mfrom: (unknown (missing))
  • Revision ID: wgrant@ugrad.unimelb.edu.au-20080707014511-xyy43p6y8jiqrsh9
Merge killall-editarea branch. We now use CodePress instead, which is
somewhat less slothful.

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
    login = props['login']
 
134
 
 
135
    db = common.db.DB()
 
136
 
 
137
    try:
 
138
 
 
139
        # FIXME: check we're pending
 
140
 
 
141
        details = db.get_user(login)
 
142
 
 
143
        # make svn config/auth
 
144
 
 
145
        logging.debug("Creating repo")
 
146
        common.makeuser.make_svn_repo(login, throw_on_error=False)
 
147
        logging.debug("Creating svn config")
 
148
        common.makeuser.make_svn_config(login, throw_on_error=False)
 
149
        logging.debug("Creating svn auth")
 
150
        passwd = common.makeuser.make_svn_auth(login, throw_on_error=False)
 
151
        logging.debug("passwd: %s" % passwd)
 
152
 
 
153
        svn = pysvn.Client()
 
154
        svn.callback_get_login = partial(get_login, login, passwd)
 
155
 
 
156
        if conf.svn_addr[-1] != '/':
 
157
            conf.svn_addr = conf.svn_addr + "/"
 
158
    
 
159
        # FIXME: This should be a loop over enrolements.
 
160
        #        We're not going to fix this now because it requires
 
161
        #        a large amount of admin console work to manage subject
 
162
        #        offerings.
 
163
        #        Instead, we're just going to use a single offering with
 
164
        #        a "short" name of "info1".
 
165
        logging.debug("Creating /info1")
 
166
        try:
 
167
            svn.mkdir(conf.svn_addr + login + "/info1",
 
168
                      "Initial creation of work directory for Informatics 1")
 
169
        except Exception, exc:
 
170
            logging.warning("While mkdiring info1: %s" % str(exc))
 
171
            pass
 
172
        logging.debug("Creating /stuff")
 
173
        try:
 
174
            svn.mkdir(conf.svn_addr + login + "/stuff",
 
175
                      "Initial creation of directory for miscellania")
 
176
        except Exception, exc:
 
177
            logging.warning("While mkdiring stuff: %s" % str(exc))
 
178
            pass
 
179
 
 
180
        logging.debug("Creating jail")
 
181
        common.makeuser.make_jail(login, details.unixid, svn_pass=passwd)
 
182
 
 
183
        do_checkout(props, svn_pass=passwd)
 
184
 
 
185
        # FIXME: should this be nicer?
 
186
        os.system("chown -R %d:%d %s" \
 
187
                % (details.unixid, details.unixid,
 
188
                   common.studpath.url_to_local(login)[1]))
 
189
 
 
190
        logging.info("Enabling user")
 
191
        db.update_user(login, state='enabled')
 
192
 
 
193
        return {"response": "okay"}
 
194
 
 
195
    finally:
 
196
        db.close()
 
197
 
 
198
def do_checkout(props, svn_pass=None):
 
199
    """Create the default contents of a user's jail by checking out from the
 
200
    repositories.
 
201
    If any directory already exists, just fail silently (so this is not a
 
202
    "wipe-and-checkout", merely a checkout if it failed or something).
 
203
       Expected properties:
 
204
        login       - the user name for the jail
 
205
                      STRING REQUIRED
 
206
       Return Value: None
 
207
    """
 
208
    login = props['login']
 
209
 
 
210
    # If svn_pass isn't supplied, grab it from the DB
 
211
    if svn_pass is None:
 
212
        db = common.db.DB()
 
213
        user = db.get_user(login)
 
214
        svn_pass = user.svn_pass
 
215
        db.close()
 
216
 
 
217
    svn = pysvn.Client()
 
218
    svn.callback_get_login = partial(get_login, login, svn_pass)
 
219
 
 
220
    if conf.svn_addr[-1] != os.sep:
 
221
        conf.svn_addr += os.sep
 
222
 
 
223
    # FIXME: <hack>
 
224
 
 
225
    logging.debug("Checking out directories in the jail")
 
226
    try:
 
227
        svn.checkout(conf.svn_addr + login + "/stuff",
 
228
                     common.studpath.url_to_local(login + "/stuff")[1])
 
229
        os.system("chown -R %d:%d %s" \
 
230
                % (user.unixid, user.unixid,
 
231
                   common.studpath.url_to_local(login + "/stuff")[1]))
 
232
    except Exception, exc:
 
233
        logging.warning("While mkdiring stuff: %s" % str(exc))
 
234
        pass
 
235
    try:
 
236
        svn.checkout(conf.svn_addr + login + "/info1",
 
237
                     common.studpath.url_to_local(login + "/info1")[1])
 
238
        os.system("chown -R %d:%d %s" \
 
239
                % (user.unixid, user.unixid,
 
240
                   common.studpath.url_to_local(login + "/info1")[1]))
 
241
    except Exception, exc:
 
242
        logging.warning("While mkdiring info1: %s" % str(exc))
 
243
        pass
 
244
 
 
245
    # FIXME: </hack>
 
246
 
 
247
    return {"response": "okay"}
 
248
 
 
249
actions = {
 
250
        'create_user':create_user,
 
251
        'update_user':update_user,
 
252
        'activate_user':activate_user,
 
253
        'do_checkout':do_checkout,
 
254
    }
 
255
 
 
256
def initializer():
 
257
    try:
 
258
        pidfile = open('/var/run/usrmgt-server.pid', 'w')
 
259
        pidfile.write('%d\n' % os.getpid())
 
260
        pidfile.close()
 
261
    except IOError, (errno, strerror):
 
262
        print "Couldn't write PID file. IO error(%s): %s" % (errno, strerror)
 
263
        sys.exit(1)
 
264
 
 
265
def dispatch(props):
 
266
    logging.debug(repr(props))
 
267
    action = props.keys()[0]
 
268
    return actions[action](props[action])
 
269
 
 
270
if __name__ == "__main__":
 
271
    if len(sys.argv) <3:
 
272
        print >>sys.stderr, "Usage: usrmgt-server <port> <magic>"
 
273
        sys.exit(1)
 
274
    port = int(sys.argv[1])
 
275
    magic = sys.argv[2]
 
276
 
 
277
    pid = os.getpid()
 
278
 
 
279
    logging.basicConfig(filename="/var/log/usrmgt.log", level=logging.INFO)
 
280
    logging.info("Starting usrmgt server on port %d (pid = %d)" % (port, pid))
 
281
 
 
282
    common.chat.start_server(port, magic, True, dispatch, initializer)