1 # -*- coding: iso-8859-1 -*-
3 MoinMoin - auth plugin doing a check against MySQL db
5 @copyright: 2008 Matthijs Kooijman
6 @license: GNU GPL, see COPYING for details.
11 from MoinMoin import user
12 from MoinMoin.auth import BaseAuth, ContinueLogin
13 from MoinMoin.datastruct.backends import LazyGroupsBackend, LazyGroup
14 from MoinMoin import log
15 logging = log.getLogger(__name__)
18 class PhpbbGroupsBackend(LazyGroupsBackend):
19 class PhpbbGroup(LazyGroup):
25 def __init__(self, request, auth):
26 super(LazyGroupsBackend, self).__init__(request)
29 self.request = request
33 Return a list of group names.
35 return self.list_query("SELECT group_name \
37 WHERE group_single_user = 0"
38 % self.auth.phpbb_prefix)
40 def __contains__(self, group_name):
42 Does a group with the given name exist?
44 return self.single_query("SELECT EXISTS ( \
47 WHERE group_single_user = 0 \
48 AND group_name=%%s)" % self.auth.phpbb_prefix,
51 def __getitem__(self, group_name):
53 Get the group with the given name.
55 return self.PhpbbGroup(self.request, group_name, self)
57 def _iter_group_members(self, group_name):
59 Get all member names for the given group. This is called by
62 return self.list_query ("SELECT username \
63 FROM `%susers` as u, `%suser_group` as ug, `%sgroups` as g \
64 WHERE u.user_id = ug.user_id AND ug.group_id = g.group_id \
65 AND ug.user_pending = 0 AND g.group_single_user = 0 \
66 AND g.group_name = %%s"
67 % (self.auth.phpbb_prefix, self.auth.phpbb_prefix, self.auth.phpbb_prefix),
70 def _group_has_member(self, group_name, member):
72 Does the group with the given name have a member with the given name?
73 This is called by LazyGroup.__contains__.
75 return self.single_query ("SELECT EXISTS( \
77 FROM `%susers` as u, `%suser_group` as ug, `%sgroups` as g \
78 WHERE u.user_id = ug.user_id AND ug.group_id = g.group_id \
79 AND ug.user_pending = 0 AND g.group_single_user = 0 \
80 AND g.group_name = %%s AND u.username = %%s)"
81 % (self.auth.phpbb_prefix, self.auth.phpbb_prefix, self.auth.phpbb_prefix),
84 def groups_with_member(self, member):
86 Return the group names for all groups that have a member with the
89 return self.list_query ("SELECT g.group_name \
90 FROM `%susers` as u, `%suser_group` as ug, `%sgroups` as g \
91 WHERE u.user_id = ug.user_id AND ug.group_id = g.group_id \
92 AND ug.user_pending = 0 AND g.group_single_user = 0 \
94 % (self.auth.phpbb_prefix, self.auth.phpbb_prefix, self.auth.phpbb_prefix),
97 def single_query(self, *args):
99 Runs an SQL query, that returns single row with a single column.
100 Returns just that single result.
105 conn = self.auth.connect(self.request)
106 cursor = conn.cursor()
107 cursor.execute(*args)
109 return cursor.fetchone()[0]
116 def list_query(self, *args):
118 Runs an SQL query, that returns any number of single-column rows.
119 Returns the results as a list of single values
124 conn = self.auth.connect(self.request)
125 cursor = conn.cursor()
126 cursor.execute(*args)
136 class PhpbbAuth(BaseAuth):
137 logout_possible = True
138 login_inputs = ['username', 'password']
140 def __init__(self, name='phpbb', dbhost=None, dbuser=None, dbpass=None, dbname=None, dbport=None, phpbb_prefix='', hint=None):
142 Authenticate using credentials from a phpbb database
144 The name parameter should be unique among all authentication methods.
146 The hint parameter is a snippet of HTML that is displayed below the login form.
153 self.phpbb_prefix = phpbb_prefix
157 # Create a "constructor" to create a phpbb_groups instance, while
158 # passing ourselves to it.
159 self.groups_backend = lambda config, request: PhpbbGroupsBackend(request, self)
161 def check_login(self, request, username, password):
162 """ Checks the given username password combination. Returns the
163 corresponding emailaddress, or False if authentication failed.
165 conn = self.connect(request)
170 # Get some data. Note that we interpolate the prefix ourselves, since
171 # letting the mysql library do it only works with values (it adds ''
172 # automatically). Note also that this allows possible SQL injection
173 # through the phpbb_prefix variable, but that should be a trusted
175 cursor = conn.cursor ()
176 cursor.execute ("SELECT user_password,user_email FROM `%susers` WHERE username=%%s" % self.phpbb_prefix, username)
179 if (cursor.rowcount == 0):
184 row = cursor.fetchone()
187 if (md5.new(password).hexdigest() == row[0]):
192 def connect(self, request):
193 # This code was shamelessly stolen from
194 # django.db.backends.mysql.base.cursor
197 'use_unicode': False,
200 kwargs['user'] = self.dbuser
202 kwargs['db'] = self.dbname
204 kwargs['passwd'] = self.dbpass
205 if self.dbhost.startswith('/'):
206 kwargs['unix_socket'] = self.dbhost
208 kwargs['host'] = self.dbhost
210 kwargs['port'] = int(self.dbport)
215 conn = MySQLdb.connect (**kwargs)
219 info = sys.exc_info()
220 logging.error("phpbb_login: authentication failed due to exception connecting to DB, traceback follows...")
221 logging.error(''.join(traceback.format_exception(*info)))
226 def login(self, request, user_obj, **kw):
228 Handle a login. Called by moinmoin.
231 username = kw.get('username')
232 password = kw.get('password')
234 logging.debug("phpbb_login: Trying to log in, username=%r " % (username))
236 # simply continue if something else already logged in
238 if user_obj and user_obj.valid:
239 return ContinueLogin(user_obj)
241 # Deny empty username or passwords
242 if not username or not password:
243 return ContinueLogin(user_obj)
245 email = self.check_login(request, username, password)
249 logging.debug("phpbb_login: authentication failed for %s" % (username))
250 return ContinueLogin(user_obj)
252 logging.debug("phpbb_login: authenticated %s (email %s)" % (username, email))
254 u = user.User(request, auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'email'))
256 #u.remember_me = 0 # 0 enforces cookie_lifetime config param
257 u.create_or_update(True)
259 return ContinueLogin(u)
263 info = sys.exc_info()
264 logging.error("phpbb_login: authentication failed due to unexpected exception, traceback follows...")
265 logging.error(''.join(traceback.format_exception(*info)))
266 return ContinueLogin(user_obj)
268 def login_hint(self, request):
269 """ Return a snippet of HTML that is displayed with the login form. """
272 # vim: set sw=4 expandtab sts=4:vim