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

« back to all changes in this revision

Viewing changes to scripts/usrmgt-server

  • Committer: mattgiuca
  • Date: 2008-07-21 05:25:48 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:920
userdb: Added to table "offering" fields max_students_per_group and
    max_groups_per_student.

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
    common.makeuser.make_user_db(**props)
 
69
    user = common.db.get_user(props["login"])
 
70
    return user["unixid"]
 
71
 
 
72
def update_user(props):
 
73
    """Create the database record for the given user.
 
74
       Expected properties:
 
75
        login       - user who is making the change (not necessarily the one
 
76
                      being updated).
 
77
        update      - dict of fields to be updated. The fields are all those
 
78
                      in the login table of the db.
 
79
                      'login' required.
 
80
                      Omitted fields will not be set.
 
81
    """
 
82
    update = props['update']
 
83
    # Note: "login" is special - it is not part of the kwargs to
 
84
    # db.update_user, but the first arg - used to find the user to update.
 
85
    # However it doesn't need any special treatment here.
 
86
 
 
87
    db = common.db.DB()
 
88
    db.update_user(**update)
 
89
    db.close()
 
90
 
 
91
def get_login(login, passwd, realm, existing_login, _may_save):
 
92
    """Callback function used by pysvn for authentication.
 
93
    login, passwd: Credentials of the user attempting to log in.
 
94
        passwd in clear (this should be the user's svn_pass, a
 
95
        randomly-generated permanent password for this user, not their main
 
96
        IVLE password).
 
97
    realm, existing_login, _may_save: The 3 arguments passed by pysvn to
 
98
        callback_get_login.
 
99
        The following has been determined empirically, not from docs:
 
100
        existing_login will be the name of the user who owns the process on
 
101
        the first attempt, "" on subsequent attempts. We use this fact.
 
102
    """
 
103
    logging.debug("Getting password for %s (realm %s)" % (login, realm))
 
104
    # Only provide credentials on the _first_ attempt.
 
105
    # If we're being asked again, then it means the credentials failed for
 
106
    # some reason and we should just fail. (This is not desirable, but it's
 
107
    # better than being asked an infinite number of times).
 
108
    if existing_login == "":
 
109
        logging.warning("Could not authenticate SVN for %s" % login)
 
110
        return (False, login, passwd, False)
 
111
    else:
 
112
        return (True, login, passwd, False)
 
113
 
 
114
def activate_user(props):
 
115
    """Create the on-disk stuff for the given user.
 
116
       Sets the state of the user in the db from pending to enabled.
 
117
       Expected properties:
 
118
        login       - the user name for the jail
 
119
                      STRING REQUIRED
 
120
       Return Value: None
 
121
    """
 
122
 
 
123
    os.umask(0022) # Bad, but start_server sets it worse.
 
124
 
 
125
    login = props['login']
 
126
 
 
127
    db = common.db.DB()
 
128
 
 
129
    try:
 
130
 
 
131
        # FIXME: check we're pending
 
132
 
 
133
        details = db.get_user(login)
 
134
 
 
135
        # make svn config/auth
 
136
 
 
137
        logging.debug("Creating repo")
 
138
        common.makeuser.make_svn_repo(login, throw_on_error=False)
 
139
        logging.debug("Creating svn config")
 
140
        common.makeuser.make_svn_config(login, throw_on_error=False)
 
141
        logging.debug("Creating svn auth")
 
142
        passwd = common.makeuser.make_svn_auth(login, throw_on_error=False)
 
143
        logging.debug("passwd: %s" % passwd)
 
144
 
 
145
        svn = pysvn.Client()
 
146
        svn.callback_get_login = partial(get_login, login, passwd)
 
147
 
 
148
        if conf.svn_addr[-1] != '/':
 
149
            conf.svn_addr = conf.svn_addr + "/"
 
150
    
 
151
        # FIXME: This should be a loop over enrolements.
 
152
        #        We're not going to fix this now because it requires
 
153
        #        a large amount of admin console work to manage subject
 
154
        #        offerings.
 
155
        #        Instead, we're just going to use a single offering with
 
156
        #        a "short" name of "info1".
 
157
        logging.debug("Creating /info1")
 
158
        try:
 
159
            svn.mkdir(conf.svn_addr + login + "/info1",
 
160
                      "Initial creation of work directory for Informatics 1")
 
161
        except Exception, exc:
 
162
            logging.warning("While mkdiring info1: %s" % str(exc))
 
163
            pass
 
164
        logging.debug("Creating /stuff")
 
165
        try:
 
166
            svn.mkdir(conf.svn_addr + login + "/stuff",
 
167
                      "Initial creation of directory for miscellania")
 
168
        except Exception, exc:
 
169
            logging.warning("While mkdiring stuff: %s" % str(exc))
 
170
            pass
 
171
 
 
172
        logging.debug("Creating jail")
 
173
        common.makeuser.make_jail(login, details.unixid, svn_pass=passwd)
 
174
 
 
175
        common.makeuser.mount_jail(login)
 
176
 
 
177
        do_checkout(props, svn_pass=passwd)
 
178
 
 
179
        # FIXME: should this be nicer?
 
180
        os.system("chown -R %d:%d %s" \
 
181
                % (details.unixid, details.unixid,
 
182
                   common.studpath.url_to_local(login)[1]))
 
183
 
 
184
        logging.info("Enabling user")
 
185
        db.update_user(login, state='enabled')
 
186
 
 
187
        return {"response": "okay"}
 
188
 
 
189
    finally:
 
190
        db.close()
 
191
 
 
192
def do_checkout(props, svn_pass=None):
 
193
    """Create the default contents of a user's jail by checking out from the
 
194
    repositories.
 
195
    If any directory already exists, just fail silently (so this is not a
 
196
    "wipe-and-checkout", merely a checkout if it failed or something).
 
197
       Expected properties:
 
198
        login       - the user name for the jail
 
199
                      STRING REQUIRED
 
200
       Return Value: None
 
201
    """
 
202
    login = props['login']
 
203
 
 
204
    db = common.db.DB()
 
205
    user = db.get_user(login)
 
206
    # If svn_pass isn't supplied, grab it from the DB
 
207
    if svn_pass is None:
 
208
        svn_pass = user.svn_pass
 
209
    db.close()
 
210
 
 
211
    svn = pysvn.Client()
 
212
    svn.callback_get_login = partial(get_login, login, svn_pass)
 
213
 
 
214
    if conf.svn_addr[-1] != os.sep:
 
215
        conf.svn_addr += os.sep
 
216
 
 
217
    # FIXME: <hack>
 
218
 
 
219
    logging.debug("Checking out directories in the jail")
 
220
    try:
 
221
        svn.checkout(conf.svn_addr + login + "/stuff",
 
222
                     common.studpath.url_to_local(login + "/stuff")[1])
 
223
        os.system("chown -R %d:%d %s" \
 
224
                % (user.unixid, user.unixid,
 
225
                   common.studpath.url_to_local(login + "/stuff")[1]))
 
226
    except Exception, exc:
 
227
        logging.warning("While mkdiring stuff: %s" % str(exc))
 
228
        pass
 
229
    try:
 
230
        svn.checkout(conf.svn_addr + login + "/info1",
 
231
                     common.studpath.url_to_local(login + "/info1")[1])
 
232
        os.system("chown -R %d:%d %s" \
 
233
                % (user.unixid, user.unixid,
 
234
                   common.studpath.url_to_local(login + "/info1")[1]))
 
235
    except Exception, exc:
 
236
        logging.warning("While mkdiring info1: %s" % str(exc))
 
237
        pass
 
238
 
 
239
    # FIXME: </hack>
 
240
 
 
241
    return {"response": "okay"}
 
242
 
 
243
actions = {
 
244
        'create_user':create_user,
 
245
        'update_user':update_user,
 
246
        'activate_user':activate_user,
 
247
        'do_checkout':do_checkout,
 
248
    }
 
249
 
 
250
def initializer():
 
251
    try:
 
252
        pidfile = open('/var/run/usrmgt-server.pid', 'w')
 
253
        pidfile.write('%d\n' % os.getpid())
 
254
        pidfile.close()
 
255
    except IOError, (errno, strerror):
 
256
        print "Couldn't write PID file. IO error(%s): %s" % (errno, strerror)
 
257
        sys.exit(1)
 
258
 
 
259
def dispatch(props):
 
260
    logging.debug(repr(props))
 
261
    action = props.keys()[0]
 
262
    return actions[action](props[action])
 
263
 
 
264
if __name__ == "__main__":
 
265
    if len(sys.argv) <3:
 
266
        print >>sys.stderr, "Usage: usrmgt-server <port> <magic>"
 
267
        sys.exit(1)
 
268
    port = int(sys.argv[1])
 
269
    magic = sys.argv[2]
 
270
 
 
271
    pid = os.getpid()
 
272
 
 
273
    logging.basicConfig(filename="/var/log/usrmgt.log", level=logging.INFO)
 
274
    logging.info("Starting usrmgt server on port %d (pid = %d)" % (port, pid))
 
275
 
 
276
    common.chat.start_server(port, magic, True, dispatch, initializer)