~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/auth.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mfrom: (0.9.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080622211713-fpo2zrq3s5dfecxg
Tags: 1.7.0-3
Simplify /etc/moin/wikilist format: "USER URL" (drop unneeded middle
CONFIG_DIR that was wrongly advertised as DATA_DIR).  Make
moin-mass-migrate handle both formats and warn about deprecation of
the old one.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: iso-8859-1 -*-
2
 
"""
3
 
    MoinMoin - modular authentication code
4
 
 
5
 
    Here are some methods moin can use in cfg.auth authentication method list.
6
 
    The methods from that list get called (from request.py) in that sequence.
7
 
    They get request as first argument and also some more kw arguments:
8
 
       name: the value we did get from a POST of the UserPreferences page
9
 
             in the "name" form field (or None)
10
 
       password: the value of the password form field (or None)
11
 
       login: True if user has clicked on Login button
12
 
       logout: True if user has clicked on Logout button
13
 
       user_obj: the user_obj we have until now (user_obj returned from
14
 
                 previous auth method or None for first auth method)
15
 
       (we maybe add some more here)
16
 
 
17
 
    Use code like this to get them:
18
 
        name = kw.get('name') or ''
19
 
        password = kw.get('password') or ''
20
 
        login = kw.get('login')
21
 
        logout = kw.get('logout')
22
 
        request.log("got name=%s len(password)=%d login=%r logout=%r" % (name, len(password), login, logout))
23
 
    
24
 
    The called auth method then must return a tuple (user_obj, continue_flag).
25
 
    user_obj can be one of:
26
 
    * a (newly created) User object
27
 
    * None if we want to inhibit log in from previous auth methods
28
 
    * what we got as kw argument user_obj (meaning: no change).
29
 
    continue_flag is a boolean indication whether the auth loop shall continue
30
 
    trying other auth methods (or not).
31
 
 
32
 
    The methods give a kw arg "auth_attribs" to User.__init__ that tells
33
 
    which user attribute names are DETERMINED and set by this auth method and
34
 
    must not get changed by the user using the UserPreferences form.
35
 
    It also gives a kw arg "auth_method" that tells the name of the auth
36
 
    method that authentified the user.
37
 
    
38
 
    @copyright: 2005-2006 Bastian Blank, Florian Festi, Thomas Waldmann
39
 
    @copyright: 2005-2006 MoinMoin:AlexanderSchremmer
40
 
    @license: GNU GPL, see COPYING for details.
41
 
"""
42
 
 
43
 
import time, Cookie
44
 
from MoinMoin import config, user
45
 
 
46
 
def log(request, **kw):
47
 
    """ just log the call, do nothing else """
48
 
    username = kw.get('name')
49
 
    password = kw.get('password')
50
 
    login = kw.get('login')
51
 
    logout = kw.get('logout')
52
 
    user_obj = kw.get('user_obj')
53
 
    request.log("auth.log: name=%s login=%r logout=%r user_obj=%r" % (username, login, logout, user_obj))
54
 
    return user_obj, True
55
 
 
56
 
# some cookie functions used by moin_cookie auth
57
 
def makeCookie(request, moin_id, maxage, expires):
58
 
    """ calculate a MOIN_ID cookie """
59
 
    c = Cookie.SimpleCookie()
60
 
    cfg = request.cfg
61
 
    c['MOIN_ID'] = moin_id
62
 
    c['MOIN_ID']['max-age'] = maxage
63
 
    if cfg.cookie_domain:
64
 
        c['MOIN_ID']['domain'] = cfg.cookie_domain
65
 
    if cfg.cookie_path:
66
 
        c['MOIN_ID']['path'] = cfg.cookie_path
67
 
    else:
68
 
        path = request.getScriptname()
69
 
        if not path:
70
 
            path = '/'
71
 
        c['MOIN_ID']['path'] = path
72
 
    # Set expires for older clients
73
 
    c['MOIN_ID']['expires'] = request.httpDate(when=expires, rfc='850')        
74
 
    return c.output()
75
 
 
76
 
def setCookie(request, u):
77
 
    """ Set cookie for the user obj u
78
 
    
79
 
    cfg.cookie_lifetime and the user 'remember_me' setting set the
80
 
    lifetime of the cookie. lifetime in int hours, see table:
81
 
    
82
 
    value   cookie lifetime
83
 
    ----------------------------------------------------------------
84
 
     = 0    forever, ignoring user 'remember_me' setting
85
 
     > 0    n hours, or forever if user checked 'remember_me'
86
 
     < 0    -n hours, ignoring user 'remember_me' setting
87
 
    """
88
 
    # Calculate cookie maxage and expires
89
 
    lifetime = int(request.cfg.cookie_lifetime) * 3600 
90
 
    forever = 10*365*24*3600 # 10 years
91
 
    now = time.time()
92
 
    if not lifetime:
93
 
        maxage = forever
94
 
    elif lifetime > 0:
95
 
        if u.remember_me:
96
 
            maxage = forever
97
 
        else:
98
 
            maxage = lifetime
99
 
    elif lifetime < 0:
100
 
        maxage = (-lifetime)
101
 
    expires = now + maxage
102
 
    
103
 
    cookie = makeCookie(request, u.id, maxage, expires)
104
 
    # Set cookie
105
 
    request.setHttpHeader(cookie)
106
 
    # IMPORTANT: Prevent caching of current page and cookie
107
 
    request.disableHttpCaching()
108
 
 
109
 
def deleteCookie(request):
110
 
    """ Delete the user cookie by sending expired cookie with null value
111
 
 
112
 
    According to http://www.cse.ohio-state.edu/cgi-bin/rfc/rfc2109.html#sec-4.2.2
113
 
    Deleted cookie should have Max-Age=0. We also have expires
114
 
    attribute, which is probably needed for older browsers.
115
 
 
116
 
    Finally, delete the saved cookie and create a new user based on the new settings.
117
 
    """
118
 
    moin_id = ''
119
 
    maxage = 0
120
 
    # Set expires to one year ago for older clients
121
 
    expires = time.time() - (3600 * 24 * 365) # 1 year ago
122
 
    cookie = makeCookie(request, moin_id, maxage, expires) 
123
 
    # Set cookie
124
 
    request.setHttpHeader(cookie)
125
 
    # IMPORTANT: Prevent caching of current page and cookie        
126
 
    request.disableHttpCaching()
127
 
 
128
 
def moin_cookie(request, **kw):
129
 
    """ authenticate via the MOIN_ID cookie """
130
 
    username = kw.get('name')
131
 
    password = kw.get('password')
132
 
    login = kw.get('login')
133
 
    logout = kw.get('logout')
134
 
    user_obj = kw.get('user_obj')
135
 
    #request.log("auth.moin_cookie: name=%s login=%r logout=%r user_obj=%r" % (username, login, logout, user_obj))
136
 
    if login:
137
 
        u = user.User(request, name=username, password=password,
138
 
                      auth_method='login_userpassword')
139
 
        if u.valid:
140
 
            setCookie(request, u)
141
 
            return u, True # we make continuing possible, e.g. for smbmount
142
 
        return user_obj, True
143
 
 
144
 
    try:
145
 
        cookie = Cookie.SimpleCookie(request.saved_cookie)
146
 
    except Cookie.CookieError:
147
 
        # ignore invalid cookies, else user can't relogin
148
 
        cookie = None
149
 
    if cookie and cookie.has_key('MOIN_ID'):
150
 
        u = user.User(request, id=cookie['MOIN_ID'].value,
151
 
                      auth_method='moin_cookie', auth_attribs=())
152
 
 
153
 
        if logout:
154
 
            u.valid = 0 # just make user invalid, but remember him
155
 
 
156
 
        if u.valid:
157
 
            setCookie(request, u) # refreshes cookie lifetime
158
 
            return u, True # use True to get other methods called, too
159
 
        else: # logout or invalid user
160
 
            deleteCookie(request)
161
 
            return u, True # we return a invalidated user object, so that
162
 
                           # following auth methods can get the name of
163
 
                           # the user who logged out
164
 
    return user_obj, True
165
 
 
166
 
 
167
 
def http(request, **kw):
168
 
    """ authenticate via http basic/digest/ntlm auth """
169
 
    from MoinMoin.request import RequestTwisted, RequestCLI
170
 
    user_obj = kw.get('user_obj')
171
 
    u = None
172
 
    # check if we are running Twisted
173
 
    if isinstance(request, RequestTwisted):
174
 
        username = request.twistd.getUser().decode(config.charset)
175
 
        password = request.twistd.getPassword().decode(config.charset)
176
 
        # when using Twisted http auth, we use username and password from
177
 
        # the moin user profile, so both can be changed by user.
178
 
        u = user.User(request, auth_username=username, password=password,
179
 
                      auth_method='http', auth_attribs=())
180
 
 
181
 
    elif not isinstance(request, RequestCLI):
182
 
        env = request.env
183
 
        auth_type = env.get('AUTH_TYPE','')
184
 
        if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate',]:
185
 
            username = env.get('REMOTE_USER', '').decode(config.charset)
186
 
            if auth_type in ('NTLM', 'Negotiate',):
187
 
                # converting to standard case so the user can even enter wrong case
188
 
                # (added since windows does not distinguish between e.g.
189
 
                #  "Mike" and "mike")
190
 
                username = username.split('\\')[-1] # split off domain e.g.
191
 
                                                    # from DOMAIN\user
192
 
                # this "normalizes" the login name from {meier, Meier, MEIER} to Meier
193
 
                # put a comment sign in front of next line if you don't want that:
194
 
                username = username.title()
195
 
            # when using http auth, we have external user name and password,
196
 
            # we don't use the moin user profile for those attributes.
197
 
            u = user.User(request, auth_username=username,
198
 
                          auth_method='http', auth_attribs=('name', 'password'))
199
 
 
200
 
    if u:
201
 
        u.create_or_update()
202
 
    if u and u.valid:
203
 
        return u, True # True to get other methods called, too
204
 
    else:
205
 
        return user_obj, True
206
 
 
207
 
def sslclientcert(request, **kw):
208
 
    """ authenticate via SSL client certificate """
209
 
    from MoinMoin.request import RequestTwisted
210
 
    user_obj = kw.get('user_obj')
211
 
    u = None
212
 
    changed = False
213
 
    # check if we are running Twisted
214
 
    if isinstance(request, RequestTwisted):
215
 
        return user_obj, True # not supported if we run twisted
216
 
        # Addendum: this seems to need quite some twisted insight and coding.
217
 
        # A pointer i got on #twisted: divmod's vertex.sslverify
218
 
        # If you really need this, feel free to implement and test it and
219
 
        # submit a patch if it works.
220
 
    else:
221
 
        env = request.env
222
 
        if env.get('SSL_CLIENT_VERIFY', 'FAILURE') == 'SUCCESS':
223
 
            # if we only want to accept some specific CA, do a check like:
224
 
            # if env.get('SSL_CLIENT_I_DN_OU') == "http://www.cacert.org"
225
 
            email = env.get('SSL_CLIENT_S_DN_Email', '').decode(config.charset)
226
 
            email_lower = email.lower()
227
 
            commonname = env.get('SSL_CLIENT_S_DN_CN', '').decode(config.charset)
228
 
            commonname_lower = commonname.lower()
229
 
            if email_lower or commonname_lower:
230
 
                for uid in user.getUserList(request):
231
 
                    u = user.User(request, uid,
232
 
                                  auth_method='sslclientcert', auth_attribs=())
233
 
                    if email_lower and u.email.lower() == email_lower:
234
 
                        u.auth_attribs = ('email', 'password')
235
 
                        #this is only useful if same name should be used, as
236
 
                        #commonname is likely no CamelCase WikiName
237
 
                        #if commonname_lower != u.name.lower():
238
 
                        #    u.name = commonname
239
 
                        #    changed = True
240
 
                        #u.auth_attribs = ('email', 'name', 'password')
241
 
                        break
242
 
                    if commonname_lower and u.name.lower() == commonname_lower:
243
 
                        u.auth_attribs = ('name', 'password')
244
 
                        #this is only useful if same email should be used as
245
 
                        #specified in certificate.
246
 
                        #if email_lower != u.email.lower():
247
 
                        #    u.email = email
248
 
                        #    changed = True
249
 
                        #u.auth_attribs = ('name', 'email', 'password')
250
 
                        break
251
 
                else:
252
 
                    u = None
253
 
                if u is None:
254
 
                    # user wasn't found, so let's create a new user object
255
 
                    u = user.User(request, name=commonname_lower, auth_username=commonname_lower)
256
 
 
257
 
    if u:
258
 
        u.create_or_update(changed)
259
 
    if u and u.valid:
260
 
        return u, True
261
 
    else:
262
 
        return user_obj, True
263
 
 
264
 
 
265
 
def smb_mount(request, **kw):
266
 
    """ (u)mount a SMB server's share for username (using username/password for
267
 
        authentication at the SMB server). This can be used if you need access
268
 
        to files on some share via the wiki, but needs more code to be useful.
269
 
        If you don't need it, don't use it.
270
 
    """
271
 
    username = kw.get('name')
272
 
    password = kw.get('password')
273
 
    login = kw.get('login')
274
 
    logout = kw.get('logout')
275
 
    user_obj = kw.get('user_obj')
276
 
    cfg = request.cfg
277
 
    verbose = cfg.smb_verbose
278
 
    if verbose: request.log("got name=%s login=%r logout=%r" % (username, login, logout))
279
 
    
280
 
    # we just intercept login to mount and logout to umount the smb share
281
 
    if login or logout:
282
 
        import os, pwd, subprocess
283
 
        web_username = cfg.smb_dir_user
284
 
        web_uid = pwd.getpwnam(web_username)[2] # XXX better just use current uid?
285
 
        if logout and user_obj: # logout -> we don't have username in form
286
 
            username = user_obj.name # so we take it from previous auth method (moin_cookie e.g.)
287
 
        mountpoint = cfg.smb_mountpoint % {
288
 
            'username': username,
289
 
        }
290
 
        if login:
291
 
            cmd = u"sudo mount -t cifs -o user=%(user)s,domain=%(domain)s,uid=%(uid)d,dir_mode=%(dir_mode)s,file_mode=%(file_mode)s,iocharset=%(iocharset)s //%(server)s/%(share)s %(mountpoint)s >>%(log)s 2>&1"
292
 
        elif logout:
293
 
            cmd = u"sudo umount %(mountpoint)s >>%(log)s 2>&1"
294
 
            
295
 
        cmd = cmd % {
296
 
            'user': username,
297
 
            'uid': web_uid,
298
 
            'domain': cfg.smb_domain,
299
 
            'server': cfg.smb_server,
300
 
            'share': cfg.smb_share,
301
 
            'mountpoint': mountpoint,
302
 
            'dir_mode': cfg.smb_dir_mode,
303
 
            'file_mode': cfg.smb_file_mode,
304
 
            'iocharset': cfg.smb_iocharset,
305
 
            'log': cfg.smb_log,
306
 
        }
307
 
        env = os.environ.copy()
308
 
        if login:
309
 
            try:
310
 
                os.makedirs(mountpoint) # the dir containing the mountpoint must be writeable for us!
311
 
            except OSError, err:
312
 
                pass
313
 
            env['PASSWD'] = password.encode(cfg.smb_coding)
314
 
        subprocess.call(cmd.encode(cfg.smb_coding), env=env, shell=True)
315
 
    return user_obj, True
316
 
 
317
 
 
318
 
def ldap_login(request, **kw):
319
 
    """ get authentication data from form, authenticate against LDAP (or Active Directory),
320
 
        fetch some user infos from LDAP and create a user profile for that user that must
321
 
        be used by subsequent auth plugins (like moin_cookie) as we never return a user
322
 
        object from ldap_login.
323
 
        python-ldap needs to be at least 2.0.0pre06 (available since mid 2002) for ldaps support -
324
 
        some older debian installations (woody and older?) require libldap2-tls and python2.x-ldap-tls,
325
 
        otherwise you get ldap.SERVER_DOWN: "Can't contact LDAP server" -
326
 
        more recent debian installations have tls support in libldap2 (see dependency on gnutls)
327
 
        and also in python-ldap.
328
 
        use ldaps://server:636 url for ldaps, ldap://server for ldap with tls (plus some tls code, todo)
329
 
    """
330
 
    username = kw.get('name')
331
 
    password = kw.get('password')
332
 
    login = kw.get('login')
333
 
    logout = kw.get('logout')
334
 
    user_obj = kw.get('user_obj')
335
 
 
336
 
    cfg = request.cfg
337
 
    verbose = cfg.ldap_verbose
338
 
 
339
 
    if verbose: request.log("got name=%s login=%r logout=%r" % (username, login, logout))
340
 
 
341
 
    # we just intercept login for ldap, other requests have to be
342
 
    # handled by another auth handler
343
 
    if not login:
344
 
        return user_obj, True
345
 
 
346
 
    # we require non-empty password as ldap bind does a anon (not password
347
 
    # protected) bind if the password is empty and SUCCEEDS!
348
 
    if not password:
349
 
        return None, False
350
 
 
351
 
    import sys, re
352
 
    import ldap
353
 
 
354
 
    try:
355
 
        try:
356
 
            u = None
357
 
            dn = None
358
 
            coding = cfg.ldap_coding
359
 
            if verbose: request.log("LDAP: Setting misc. options...")
360
 
            ldap.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) # ldap v2 is outdated
361
 
            ldap.set_option(ldap.OPT_REFERRALS, 0) # needed for Active Directory:
362
 
            ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, cfg.ldap_timeout)
363
 
 
364
 
            starttls = cfg.ldap_start_tls
365
 
            if ldap.TLS_AVAIL:
366
 
                for option, value in (
367
 
                    (ldap.OPT_X_TLS_CACERTDIR, cfg.ldap_tls_cacertdir),
368
 
                    (ldap.OPT_X_TLS_CACERTFILE, cfg.ldap_tls_cacertfile),
369
 
                    (ldap.OPT_X_TLS_CERTFILE, cfg.ldap_tls_certfile),
370
 
                    (ldap.OPT_X_TLS_KEYFILE, cfg.ldap_tls_keyfile),
371
 
                    (ldap.OPT_X_TLS_REQUIRE_CERT, cfg.ldap_tls_require_cert), # ldap.OPT_X_TLS_NEVER - this is needed for self-signed ssl certs
372
 
                    (ldap.OPT_X_TLS, starttls),
373
 
                    #(ldap.OPT_X_TLS_ALLOW, 1),
374
 
                ):
375
 
                    if value:
376
 
                        ldap.set_option(option, value)
377
 
 
378
 
            server = cfg.ldap_uri
379
 
            if verbose: request.log("LDAP: Trying to initialize %s." % server)
380
 
            l = ldap.initialize(server)
381
 
            if verbose: request.log("LDAP: Connected to LDAP server %s." % server)
382
 
 
383
 
            if starttls and server.startswith('ldap:'):
384
 
                if verbose: request.log("LDAP: Trying to start TLS to %s." % server)
385
 
                try:
386
 
                    l.start_tls_s()
387
 
                    if verbose: request.log("LDAP: Using TLS to %s." % server)
388
 
                except (ldap.SERVER_DOWN, ldap.CONNECT_ERROR), err:
389
 
                    if verbose: request.log("LDAP: Couldn't establish TLS to %s (err: %s)." % (server, str(err)))
390
 
                    raise
391
 
 
392
 
            # you can use %(username)s and %(password)s here to get the stuff entered in the form:
393
 
            ldap_binddn = cfg.ldap_binddn % locals()
394
 
            ldap_bindpw = cfg.ldap_bindpw % locals()
395
 
            l.simple_bind_s(ldap_binddn.encode(coding), ldap_bindpw.encode(coding))
396
 
            if verbose: request.log("LDAP: Bound with binddn %s" % ldap_binddn)
397
 
 
398
 
            # you can use %(username)s here to get the stuff entered in the form:
399
 
            filterstr = cfg.ldap_filter % locals()
400
 
            if verbose: request.log("LDAP: Searching %s" % filterstr)
401
 
            lusers = l.search_st(cfg.ldap_base, cfg.ldap_scope, filterstr.encode(coding),
402
 
                                 attrlist=[cfg.ldap_email_attribute,
403
 
                                           cfg.ldap_aliasname_attribute,
404
 
                                           cfg.ldap_surname_attribute,
405
 
                                           cfg.ldap_givenname_attribute,
406
 
                                 ], timeout=cfg.ldap_timeout)
407
 
            # we remove entries with dn == None to get the real result list:
408
 
            lusers = [(dn, ldap_dict) for dn, ldap_dict in lusers if dn is not None]
409
 
            if verbose:
410
 
                for dn, ldap_dict in lusers:
411
 
                    request.log("LDAP: dn:%s" % dn)
412
 
                    for key, val in ldap_dict.items():
413
 
                        request.log("    %s: %s" % (key, val))
414
 
 
415
 
            result_length = len(lusers)
416
 
            if result_length != 1:
417
 
                if result_length > 1:
418
 
                    request.log("LDAP: Search found more than one (%d) matches for %s." % (result_length, filterstr))
419
 
                if result_length == 0:
420
 
                    if verbose: request.log("LDAP: Search found no matches for %s." % (filterstr, ))
421
 
                return None, False # if ldap returns unusable results, we veto the user and don't let him in
422
 
 
423
 
            dn, ldap_dict = lusers[0]
424
 
            if verbose: request.log("LDAP: DN found is %s, trying to bind with pw" % dn)
425
 
            l.simple_bind_s(dn, password.encode(coding))
426
 
            if verbose: request.log("LDAP: Bound with dn %s (username: %s)" % (dn, username))
427
 
 
428
 
            email = ldap_dict.get(cfg.ldap_email_attribute, [''])[0]
429
 
            email = email.decode(coding)
430
 
 
431
 
            aliasname = ''
432
 
            try:
433
 
                aliasname = ldap_dict[cfg.ldap_aliasname_attribute][0]
434
 
            except (KeyError, IndexError):
435
 
                pass
436
 
            if not aliasname:
437
 
                sn = ldap_dict.get(cfg.ldap_surname_attribute, [''])[0]
438
 
                gn = ldap_dict.get(cfg.ldap_givenname_attribute, [''])[0]
439
 
                if sn and gn:
440
 
                    aliasname = "%s, %s" % (sn, gn)
441
 
                elif sn:
442
 
                    aliasname = sn
443
 
            aliasname = aliasname.decode(coding)
444
 
 
445
 
            u = user.User(request, auth_username=username, password=password, auth_method='ldap', auth_attribs=('name', 'password', 'email', 'mailto_author',))
446
 
            u.name = username
447
 
            u.aliasname = aliasname
448
 
            u.email = email
449
 
            u.remember_me = 0 # 0 enforces cookie_lifetime config param
450
 
            if verbose: request.log("LDAP: creating userprefs with name %s email %s alias %s" % (username, email, aliasname))
451
 
 
452
 
        except ldap.INVALID_CREDENTIALS, err:
453
 
            request.log("LDAP: invalid credentials (wrong password?) for dn %s (username: %s)" % (dn, username))
454
 
            return None, False # if ldap says no, we veto the user and don't let him in
455
 
 
456
 
        if u:
457
 
            u.create_or_update(True)
458
 
        return user_obj, True # == nop, moin_cookie has to set the cookie and return the user obj
459
 
 
460
 
    except:
461
 
        import traceback
462
 
        info = sys.exc_info()
463
 
        request.log("LDAP: caught an exception, traceback follows...")
464
 
        request.log(''.join(traceback.format_exception(*info)))
465
 
        return None, False # something went completely wrong, in doubt we veto the login
466
 
 
467
 
 
468
 
def interwiki(request, **kw):
469
 
    # TODO use auth_method and auth_attribs for User object
470
 
    username = kw.get('name')
471
 
    password = kw.get('password')
472
 
    login = kw.get('login')
473
 
    logout = kw.get('logout')
474
 
    user_obj = kw.get('user_obj')
475
 
 
476
 
    if login:
477
 
        wikitag, wikiurl, wikitail, err = wikiutil.resolve_wiki(username)
478
 
 
479
 
        if err or wikitag not in request.cfg.trusted_wikis:
480
 
            return user_obj, True
481
 
        
482
 
        if password:
483
 
            import xmlrpclib
484
 
            homewiki = xmlrpclib.Server(wikiurl + "?action=xmlrpc2")
485
 
            account_data = homewiki.getUser(wikitail, password)
486
 
            if isinstance(account_data, str):
487
 
                # show error message
488
 
                return user_obj, True
489
 
            
490
 
            u = user.User(request, name=username)
491
 
            for key, value in account_data.iteritems():
492
 
                if key not in ["may", "id", "valid", "trusted"
493
 
                               "auth_username",
494
 
                               "name", "aliasname",
495
 
                               "enc_passwd"]:
496
 
                    setattr(u, key, value)
497
 
            u.save()
498
 
            setCookie(request, u)
499
 
            return u, True
500
 
        else:
501
 
            pass
502
 
            # XXX redirect to homewiki
503
 
    
504
 
    return user_obj, True
505
 
 
506
 
 
507
 
class php_session:
508
 
    """ Authentication module for PHP based frameworks
509
 
        Authenticates via PHP session cookie. Currently supported systems:
510
 
 
511
 
        * eGroupware 1.2 ("egw")
512
 
         * You need to configure eGroupware in the "header setup" to use
513
 
           "php sessions plus restore"
514
 
 
515
 
        @copyright: 2005 by MoinMoin:AlexanderSchremmer
516
 
            - Thanks to Spreadshirt
517
 
    """
518
 
 
519
 
    def __init__(self, apps=['egw'], s_path="/tmp", s_prefix="sess_"):
520
 
        """ @param apps: A list of the enabled applications. See above for
521
 
            possible keys.
522
 
            @param s_path: The path where the PHP sessions are stored.
523
 
            @param s_prefix: The prefix of the session files.
524
 
        """
525
 
        
526
 
        self.s_path = s_path
527
 
        self.s_prefix = s_prefix
528
 
        self.apps = apps
529
 
 
530
 
    def __call__(self, request, **kw):
531
 
        def handle_egroupware(session):
532
 
            """ Extracts name, fullname and email from the session. """
533
 
            username = session['egw_session']['session_lid'].split("@", 1)[0]
534
 
            known_accounts = session['egw_info_cache']['accounts']['cache']['account_data']
535
 
            
536
 
            # if the next line breaks, then the cache was not filled with the current
537
 
            # user information
538
 
            user_info = [value for key, value in known_accounts.items()
539
 
                         if value['account_lid'] == username][0]
540
 
            name = user_info.get('fullname', '')
541
 
            email = user_info.get('email', '')
542
 
            
543
 
            dec = lambda x: x and x.decode("iso-8859-1")
544
 
            
545
 
            return dec(username), dec(email), dec(name)
546
 
        
547
 
        import Cookie, urllib
548
 
        from MoinMoin.user import User
549
 
        from MoinMoin.util import sessionParser
550
 
    
551
 
        user_obj = kw.get('user_obj')
552
 
        try:
553
 
            cookie = Cookie.SimpleCookie(request.saved_cookie)
554
 
        except Cookie.CookieError: # ignore invalid cookies
555
 
            cookie = None
556
 
        if cookie:
557
 
            for cookiename in cookie.keys():
558
 
                cookievalue = urllib.unquote(cookie[cookiename].value).decode('iso-8859-1')
559
 
                session = sessionParser.loadSession(cookievalue, path=self.s_path, prefix=self.s_prefix)
560
 
                if session:
561
 
                    if "egw" in self.apps and session.get('egw_session', None):
562
 
                        username, email, name = handle_egroupware(session)
563
 
                        break
564
 
            else:
565
 
                return user_obj, True
566
 
            
567
 
            user = User(request, name=username, auth_username=username)
568
 
            
569
 
            changed = False
570
 
            if name != user.aliasname:
571
 
                user.aliasname = name
572
 
                changed = True
573
 
            if email != user.email:
574
 
                user.email = email
575
 
                changed = True
576
 
            
577
 
            if user:
578
 
                user.create_or_update(changed)
579
 
            if user and user.valid:
580
 
                return user, True # True to get other methods called, too
581
 
        return user_obj, True # continue with next method in auth list
582