|
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
|