root/accountmanagerplugin/0.9/acct_mgr/web_ui.py

Revision 1712, 8.5 kB (checked in by coderanger, 2 years ago)

AccountManagerPlugin:

Fix backport of ignore_auth_case protection.

Line 
1 # -*- coding: iso8859-1 -*-
2 #
3 # Copyright (C) 2005 Matthew Good <trac@matt-good.net>
4 #
5 # "THE BEER-WARE LICENSE" (Revision 42):
6 # <trac@matt-good.net> wrote this file.  As long as you retain this notice you
7 # can do whatever you want with this stuff. If we meet some day, and you think
8 # this stuff is worth it, you can buy me a beer in return.   Matthew Good
9 #
10 # Author: Matthew Good <trac@matt-good.net>
11
12 from __future__ import generators
13
14 from trac import perm, util
15 from trac.core import *
16 from trac.web import auth
17 from trac.web.api import IAuthenticator
18 from trac.web.main import IRequestHandler
19 from trac.web.chrome import INavigationContributor, ITemplateProvider
20 from trac.util import Markup
21
22 from api import AccountManager
23
24 class AccountModule(Component):
25     """Allows users to change their password or delete their account.
26     The settings for the AccountManager module must be set in trac.ini
27     in order to use this.
28     """
29
30     implements(INavigationContributor, IRequestHandler, ITemplateProvider)
31
32     #INavigationContributor methods
33     def get_active_navigation_item(self, req):
34         return 'account'
35
36     def get_navigation_items(self, req):
37         if req.authname != 'anonymous':
38             yield 'metanav', 'account', Markup('<a href="%s">My Account</a>',
39                                                (self.env.href.account()))
40
41     # IRequestHandler methods
42     def match_request(self, req):
43         return req.path_info == '/account'
44
45     def process_request(self, req):
46         if req.authname == 'anonymous':
47             req.redirect(self.env.href.wiki())
48         action = req.args.get('action')
49         if req.method == 'POST':
50             if action == 'change_password':
51                 self._do_change_password(req)
52             elif action == 'delete':
53                 self._do_delete(req)
54         return 'account.cs', None
55
56     def _do_change_password(self, req):
57         user = req.authname
58         password = req.args.get('password')
59         if not password:
60             req.hdf['account.error'] = 'Password cannot be empty.'
61             return
62
63         if password != req.args.get('password_confirm'):
64             req.hdf['account.error'] = 'The passwords must match.'
65             return
66
67         AccountManager(self.env).set_password(user, password)
68         req.hdf['account.message'] = 'Password successfully updated.'
69
70     def _do_delete(self, req):
71         user = req.authname
72         AccountManager(self.env).delete_user(user)
73         req.redirect(self.env.href.logout())
74
75     # ITemplateProvider
76    
77     def get_htdocs_dirs(self):
78         """Return the absolute path of a directory containing additional
79         static resources (such as images, style sheets, etc).
80         """
81         return []
82
83     def get_templates_dirs(self):
84         """Return the absolute path of the directory containing the provided
85         ClearSilver templates.
86         """
87         from pkg_resources import resource_filename
88         return [resource_filename(__name__, 'templates')]
89
90 class RegistrationModule(Component):
91     """Provides users the ability to register a new account.
92     Requires configuration of the AccountManager module in trac.ini.
93     """
94
95     implements(INavigationContributor, IRequestHandler, ITemplateProvider)
96
97     def __init__(self):
98         self._enable_check(log=True)
99
100     def _enable_check(self, log=False):
101         ignore_case = self.config.getbool('trac', 'ignore_auth_case')
102         if log and ignore_case:
103             self.log.warn('RegistrationModule is disabled because '
104                           'ignore_auth_case is enabled in trac.ini.  '
105                           'This setting needs disabled to support '
106                           'registration.')
107         return not ignore_case
108
109     #INavigationContributor methods
110
111     def get_active_navigation_item(self, req):
112         return 'register'
113
114     def get_navigation_items(self, req):
115         if not self._enable_check():
116             return
117         if req.authname == 'anonymous':
118             yield 'metanav', 'register', Markup('<a href="%s">Register</a>',
119                                                 (self.env.href.register()))
120
121     # IRequestHandler methods
122
123     def match_request(self, req):
124         return req.path_info == '/register' and self._enable_check(log=True)
125
126     def process_request(self, req):
127         if req.authname != 'anonymous':
128             req.redirect(self.env.href.account())
129         action = req.args.get('action')
130         if req.method == 'POST' and action == 'create':
131             self._do_create(req)
132         return 'register.cs', None
133
134     def _do_create(self, req):
135         mgr = AccountManager(self.env)
136
137         user = req.args.get('user')
138         if not user:
139             req.hdf['registration.error'] = 'Username cannot be empty.'
140             return
141
142         if mgr.has_user(user):
143             req.hdf['registration.error'] = \
144                 'Another account with that name already exists.'
145             return
146
147         # disallow registration of accounts which have existing permissions
148         permission_system = perm.PermissionSystem(self.env)
149         if permission_system.get_user_permissions(user) != \
150            permission_system.get_user_permissions('authenticated'):
151             req.hdf['registration.error'] = \
152                 'Another account with that name already exists.'
153             return
154
155         password = req.args.get('password')
156         if not password:
157             req.hdf['registration.error'] = 'Password cannot be empty.'
158             return
159
160         if password != req.args.get('password_confirm'):
161             req.hdf['registration.error'] = 'The passwords must match.'
162             return
163
164         mgr.set_password(user, password)
165         req.redirect(self.env.href.login())
166
167     # ITemplateProvider
168    
169     def get_htdocs_dirs(self):
170         """Return the absolute path of a directory containing additional
171         static resources (such as images, style sheets, etc).
172         """
173         return []
174
175     def get_templates_dirs(self):
176         """Return the absolute path of the directory containing the provided
177         ClearSilver templates.
178         """
179         from pkg_resources import resource_filename
180         return [resource_filename(__name__, 'templates')]
181
182 def if_enabled(func):
183     def wrap(self, *args, **kwds):
184         if not self.enabled:
185             return None
186         return func(self, *args, **kwds)
187     return wrap
188
189 class LoginModule(auth.LoginModule):
190
191     implements(ITemplateProvider)
192
193     def authenticate(self, req):
194         if req.method == 'POST' and req.path_info.startswith('/login'):
195             req.remote_user = self._remote_user(req)
196         return auth.LoginModule.authenticate(self, req)
197     authenticate = if_enabled(authenticate)
198
199     match_request = if_enabled(auth.LoginModule.match_request)
200
201     def process_request(self, req):
202         if req.path_info.startswith('/login') and req.authname == 'anonymous':
203             req.hdf['referer'] = self._referer(req)
204             if req.method == 'POST':
205                 req.hdf['login.error'] = 'Invalid username or password'
206             return 'login.cs', None
207         return auth.LoginModule.process_request(self, req)
208
209     def _do_login(self, req):
210         if not req.remote_user:
211             req.redirect(self.env.abs_href())
212         return auth.LoginModule._do_login(self, req)
213
214     def _remote_user(self, req):
215         user = req.args.get('user')
216         password = req.args.get('password')
217         if not user or not password:
218             return None
219         if AccountManager(self.env).check_password(user, password):
220             return user
221         return None
222
223     def _redirect_back(self, req):
224         """Redirect the user back to the URL she came from."""
225         referer = self._referer(req)
226         if referer and not referer.startswith(req.base_url):
227             # don't redirect to external sites
228             referer = None
229         req.redirect(referer or self.env.abs_href())
230
231     def _referer(self, req):
232         return req.args.get('referer') or req.get_header('Referer')
233
234     def enabled(self):
235         # Users should disable the built-in authentication to use this one
236         return not self.env.is_component_enabled(auth.LoginModule)
237     enabled = property(enabled)
238
239     # ITemplateProvider
240    
241     def get_htdocs_dirs(self):
242         """Return the absolute path of a directory containing additional
243         static resources (such as images, style sheets, etc).
244         """
245         return []
246
247     def get_templates_dirs(self):
248         """Return the absolute path of the directory containing the provided
249         ClearSilver templates.
250         """
251         from pkg_resources import resource_filename
252         return [resource_filename(__name__, 'templates')]
Note: See TracBrowser for help on using the browser.