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

« back to all changes in this revision

Viewing changes to scripts/usrmgt-server

ivle-buildjail now only copies the system's IVLE files if jail/devmode
is True. It defaults to False.

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)