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

« back to all changes in this revision

Viewing changes to scripts/usrmgt-server

  • Committer: David Coles
  • Date: 2009-11-27 05:34:33 UTC
  • mto: This revision was merged to the branch mainline in revision 1322.
  • Revision ID: coles.david@gmail.com-20091127053433-8ki9nm6xrkogxq67
Added diagram of system architecture

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)