Switch to unified view

a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
...
...
26
from allura.lib import exceptions as forge_exc
26
from allura.lib import exceptions as forge_exc
27
27
28
log = logging.getLogger(__name__)
28
log = logging.getLogger(__name__)
29
29
30
class AuthenticationProvider(object):
30
class AuthenticationProvider(object):
31
    '''
32
    An interface to provide authentication services for Allura.
33
34
    To use a new provider, expose an entry point in setup.py:
35
36
        [allura.auth]
37
        myprovider = foo.bar.MyAuthProvider
38
39
    Then in your .ini file, set auth.method=myprovider
40
    '''
31
41
32
    def __init__(self, request):
42
    def __init__(self, request):
33
        self.request = request
43
        self.request = request
34
44
35
    @classmethod
45
    @classmethod
36
    def get(cls, request):
46
    def get(cls, request):
47
        '''returns the AuthenticationProvider instance for this request'''
37
        try:
48
        try:
38
            result = cls._loaded_ep
49
            result = cls._loaded_ep
39
        except AttributeError:
50
        except AttributeError:
40
            method = config.get('auth.method', 'local')
51
            method = config.get('auth.method', 'local')
41
            for ep in pkg_resources.iter_entry_points(
52
            for ep in pkg_resources.iter_entry_points(
...
...
56
        if user is None:
67
        if user is None:
57
            return M.User.anonymous()
68
            return M.User.anonymous()
58
        return user
69
        return user
59
70
60
    def register_user(self, user_doc):
71
    def register_user(self, user_doc):
72
        '''
73
        Register a user.
74
75
        :param user_doc: a dict with 'username' and 'display_name'.  Optionally 'password' and others
76
        :rtype: :class:`User <allura.model.auth.User>`
77
        '''
61
        raise NotImplementedError, 'register_user'
78
        raise NotImplementedError, 'register_user'
62
79
63
    def _login(self):
80
    def _login(self):
81
        '''
82
        Authorize a user, usually using self.request.params['username'] and ['password']
83
84
        :rtype: :class:`User <allura.model.auth.User>`
85
        :raises: HTTPUnauthorized if user not found, or credentials are not valid
86
        '''
64
        raise NotImplementedError, '_login'
87
        raise NotImplementedError, '_login'
65
88
66
    def login(self, user=None):
89
    def login(self, user=None):
67
        try:
90
        try:
68
            if user is None: user = self._login()
91
            if user is None: user = self._login()
...
...
76
    def logout(self):
99
    def logout(self):
77
        self.session['userid'] = None
100
        self.session['userid'] = None
78
        self.session.save()
101
        self.session.save()
79
102
80
    def by_username(self, username):
103
    def by_username(self, username):
104
        '''
105
        Find a user by username.
106
107
        :rtype: :class:`User <allura.model.auth.User>` or None
108
        '''
81
        raise NotImplementedError, 'by_username'
109
        raise NotImplementedError, 'by_username'
82
110
83
    def set_password(self, user, old_password, new_password):
111
    def set_password(self, user, old_password, new_password):
112
        '''
113
        Set a user's password.
114
115
        :param user: a :class:`User <allura.model.auth.User>`
116
        :rtype: None
117
        :raises: HTTPUnauthorized if old_password is not valid
118
        '''
84
        raise NotImplementedError, 'set_password'
119
        raise NotImplementedError, 'set_password'
85
120
86
    def upload_sshkey(self, username, pubkey):
121
    def upload_sshkey(self, username, pubkey):
122
        '''
123
        Upload an SSH Key.  Providers do not necessarily need to implement this.
124
125
        :rtype: None
126
        :raises: AssertionError with user message, upon any error
127
        '''
87
        raise NotImplemented, 'upload_sshkey'
128
        raise NotImplemented, 'upload_sshkey'
88
129
89
class LocalAuthenticationProvider(AuthenticationProvider):
130
class LocalAuthenticationProvider(AuthenticationProvider):
131
    '''
132
    Stores user passwords on the User model, in mongo.  Uses per-user salt and
133
    SHA-256 encryption.
134
    '''
90
135
91
    def register_user(self, user_doc):
136
    def register_user(self, user_doc):
92
        from allura import model as M
137
        from allura import model as M
93
        u = M.User(**user_doc)
138
        u = M.User(**user_doc)
94
        if 'password' in user_doc:
139
        if 'password' in user_doc:
...
...
207
        except ldap.INVALID_CREDENTIALS:
252
        except ldap.INVALID_CREDENTIALS:
208
            raise exc.HTTPUnauthorized()
253
            raise exc.HTTPUnauthorized()
209
        return user
254
        return user
210
255
211
class ProjectRegistrationProvider(object):
256
class ProjectRegistrationProvider(object):
257
    '''
258
    Project registration services for Allura.  This is a full implementation
259
    and the default.  Extend this class with your own if you need to add more
260
    functionality.
261
262
    To use a new provider, expose an entry point in setup.py:
263
264
        [allura.project_registration]
265
        myprovider = foo.bar.MyAuthProvider
266
267
    Then in your .ini file, set registration.method=myprovider
268
    '''
212
269
213
    @classmethod
270
    @classmethod
214
    def get(cls):
271
    def get(cls):
215
        method = config.get('registration.method', 'local')
272
        method = config.get('registration.method', 'local')
216
        for ep in pkg_resources.iter_entry_points('allura.project_registration', method):
273
        for ep in pkg_resources.iter_entry_points('allura.project_registration', method):
...
...
336
        '''This is the url needed to render a download button.
393
        '''This is the url needed to render a download button.
337
           It should be overridden for your specific envirnoment'''
394
           It should be overridden for your specific envirnoment'''
338
        return None
395
        return None
339
396
340
class ThemeProvider(object):
397
class ThemeProvider(object):
398
    '''
399
    Theme information for Allura.  This is a full implementation
400
    and the default.  Extend this class with your own if you need to add more
401
    functionality.
402
403
    To use a new provider, expose an entry point in setup.py:
404
405
        [allura.theme]
406
        myprovider = foo.bar.MyThemeProvider
407
408
    Then in your .ini file, set theme=mytheme
409
410
    The variables referencing jinja template files can be changed to point at your
411
    own jinja templates.  Use the standard templates as a reference, you should
412
    provide matching macros and block names.
413
414
    :var base_css: tuple of (css-resource, theme-name), or just a string css-resource
415
    :var icons: a dictionary of sized icons for each tool
416
    '''
417
341
    master_template = 'allura:templates/jinja_master/master.html'
418
    master_template = 'allura:templates/jinja_master/master.html'
342
    jinja_macros = 'allura:templates/jinja_master/theme_macros.html'
419
    jinja_macros = 'allura:templates/jinja_master/theme_macros.html'
343
    nav_menu = 'allura:templates/jinja_master/nav_menu.html'
420
    nav_menu = 'allura:templates/jinja_master/nav_menu.html'
344
    top_nav = 'allura:templates/jinja_master/top_nav.html'
421
    top_nav = 'allura:templates/jinja_master/top_nav.html'
345
    sidebar_menu = 'allura:templates/jinja_master/sidebar_menu.html'
422
    sidebar_menu = 'allura:templates/jinja_master/sidebar_menu.html'
...
...
353
        }
430
        }
354
    }
431
    }
355
432
356
    @LazyProperty
433
    @LazyProperty
357
    def password_change_form(self):
434
    def password_change_form(self):
435
        '''
436
        :return: None, or an easywidgets Form to render on the user preferences page
437
        '''
358
        from allura.lib.widgets.forms import PasswordChangeForm
438
        from allura.lib.widgets.forms import PasswordChangeForm
359
        return PasswordChangeForm(action='/auth/prefs/change_password')
439
        return PasswordChangeForm(action='/auth/prefs/change_password')
360
440
361
    @LazyProperty
441
    @LazyProperty
362
    def upload_key_form(self):
442
    def upload_key_form(self):
443
        '''
444
        :return: None, or an easywidgets Form to render on the user preferences page
445
        '''
363
        from allura.lib.widgets.forms import UploadKeyForm
446
        from allura.lib.widgets.forms import UploadKeyForm
364
        return UploadKeyForm(action='/auth/prefs/upload_sshkey')
447
        return UploadKeyForm(action='/auth/prefs/upload_sshkey')
365
448
366
    @property
449
    @property
367
    def master(self):
450
    def master(self):
...
...
392
475
393
class LocalProjectRegistrationProvider(ProjectRegistrationProvider):
476
class LocalProjectRegistrationProvider(ProjectRegistrationProvider):
394
    pass
477
    pass
395
478
396
class UserPreferencesProvider(object):
479
class UserPreferencesProvider(object):
480
    '''
481
    An interface for user preferences, like display_name and email_address
482
483
    To use a new provider, expose an entry point in setup.py:
484
485
        [allura.user_prefs]
486
        myprefs = foo.bar.MyUserPrefProvider
487
488
    Then in your .ini file, set user_prefs_storage.method=myprefs
489
    '''
397
490
398
    @classmethod
491
    @classmethod
399
    def get(cls):
492
    def get(cls):
400
        method = config.get('user_prefs_storage.method', 'local')
493
        method = config.get('user_prefs_storage.method', 'local')
401
        for ep in pkg_resources.iter_entry_points('allura.user_prefs', method):
494
        for ep in pkg_resources.iter_entry_points('allura.user_prefs', method):
402
            return ep.load()()
495
            return ep.load()()
403
496
404
    def get_pref(self, user, pref_name):
497
    def get_pref(self, user, pref_name):
498
        '''
499
        :param user: a :class:`User <allura.model.auth.User>`
500
        :param str pref_name:
501
        :return: pref_value
502
        :raises: AttributeError if pref_name not found
503
        '''
405
        raise NotImplementedError, 'get_pref'
504
        raise NotImplementedError, 'get_pref'
406
505
407
    def save_pref(self, user, pref_name, pref_value):
506
    def save_pref(self, user, pref_name, pref_value):
507
        '''
508
        :param user: a :class:`User <allura.model.auth.User>`
509
        :param str pref_name:
510
        :param pref_value:
511
        '''
408
        raise NotImplementedError, 'set_pref'
512
        raise NotImplementedError, 'set_pref'
409
513
410
    def find_by_display_name(self, name, substring=True):
514
    def find_by_display_name(self, name):
515
        '''
516
        :rtype: list of :class:`Users <allura.model.auth.User>`
517
        '''
411
        raise NotImplementedError, 'find_by_display_name'
518
        raise NotImplementedError, 'find_by_display_name'
412
519
413
class LocalUserPreferencesProvider(UserPreferencesProvider):
520
class LocalUserPreferencesProvider(UserPreferencesProvider):
521
    '''
522
    The default UserPreferencesProvider, storing preferences on the User object
523
    in mongo.
524
    '''
414
525
415
    def get_pref(self, user, pref_name):
526
    def get_pref(self, user, pref_name):
416
        if pref_name in user.preferences:
527
        if pref_name in user.preferences:
417
            return user.preferences[pref_name]
528
            return user.preferences[pref_name]
418
        else:
529
        else:
...
...
422
        if pref_name in user.preferences:
533
        if pref_name in user.preferences:
423
            user.preferences[pref_name] = pref_value
534
            user.preferences[pref_name] = pref_value
424
        else:
535
        else:
425
            setattr(user, pref_name, pref_value)
536
            setattr(user, pref_name, pref_value)
426
537
427
    def find_by_display_name(self, name, substring=True):
538
    def find_by_display_name(self, name):
428
        from allura import model as M
539
        from allura import model as M
429
        if not substring:
430
            raise NotImplementedError, 'non-substring'
431
        name_regex = re.compile('(?i)%s' % re.escape(name))
540
        name_regex = re.compile('(?i)%s' % re.escape(name))
432
        users = M.User.query.find(dict(
541
        users = M.User.query.find(dict(
433
                display_name=name_regex)).sort('username').all()
542
                display_name=name_regex)).sort('username').all()
434
        return users
543
        return users