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 def connect(dbhost=None, dbport=None, dbname=None, dbuser=None, dbpass=None, **kwargs):
19 # This code was shamelessly stolen from
20 # django.db.backends.mysql.base.cursor
26 kwargs['user'] = dbuser
30 kwargs['passwd'] = dbpass
31 if dbhost.startswith('/'):
32 kwargs['unix_socket'] = dbhost
34 kwargs['host'] = dbhost
36 kwargs['port'] = int(dbport)
41 conn = MySQLdb.connect (**kwargs)
46 logging.error("phpbb: authentication failed due to exception connecting to DB, traceback follows...")
47 logging.error(''.join(traceback.format_exception(*info)))
53 class PhpbbGroupsBackend(LazyGroupsBackend):
54 class PhpbbGroup(LazyGroup):
60 def __init__(self, request, **kwargs):
61 super(LazyGroupsBackend, self).__init__(request)
63 self.request = request
64 self.dbconfig = kwargs
68 Return a list of group names.
70 return self.list_query("SELECT group_name \
72 WHERE group_single_user = 0"
73 % self.dbconfig['phpbb_prefix'])
75 def __contains__(self, group_name):
77 Does a group with the given name exist?
79 return self.single_query("SELECT EXISTS ( \
82 WHERE group_single_user = 0 \
83 AND group_name=%%s)" % self.dbconfig['phpbb_prefix'],
86 def __getitem__(self, group_name):
88 Get the group with the given name.
90 return self.PhpbbGroup(self.request, group_name, self)
92 def _iter_group_members(self, group_name):
94 Get all member names for the given group. This is called by
97 return self.list_query ("SELECT username \
98 FROM `%susers` as u, `%suser_group` as ug, `%sgroups` as g \
99 WHERE u.user_id = ug.user_id AND ug.group_id = g.group_id \
100 AND ug.user_pending = 0 AND g.group_single_user = 0 \
101 AND g.group_name = %%s"
102 % (self.dbconfig['phpbb_prefix'], self.dbconfig['phpbb_prefix'], self.dbconfig['phpbb_prefix']),
105 def _group_has_member(self, group_name, member):
107 Does the group with the given name have a member with the given name?
108 This is called by LazyGroup.__contains__.
110 return self.single_query ("SELECT EXISTS( \
112 FROM `%susers` as u, `%suser_group` as ug, `%sgroups` as g \
113 WHERE u.user_id = ug.user_id AND ug.group_id = g.group_id \
114 AND ug.user_pending = 0 AND g.group_single_user = 0 \
115 AND g.group_name = %%s AND u.username = %%s)"
116 % (self.dbconfig['phpbb_prefix'], self.dbconfig['phpbb_prefix'], self.dbconfig['phpbb_prefix']),
117 (group_name, member))
119 def groups_with_member(self, member):
121 Return the group names for all groups that have a member with the
124 return self.list_query ("SELECT g.group_name \
125 FROM `%susers` as u, `%suser_group` as ug, `%sgroups` as g \
126 WHERE u.user_id = ug.user_id AND ug.group_id = g.group_id \
127 AND ug.user_pending = 0 AND g.group_single_user = 0 \
128 AND u.username = %%s"
129 % (self.dbconfig['phpbb_prefix'], self.dbconfig['phpbb_prefix'], self.dbconfig['phpbb_prefix']),
132 def single_query(self, *args):
134 Runs an SQL query, that returns single row with a single column.
135 Returns just that single result.
140 conn = connect(**self.dbconfig)
141 cursor = conn.cursor()
142 cursor.execute(*args)
144 return cursor.fetchone()[0]
151 def list_query(self, *args):
153 Runs an SQL query, that returns any number of single-column rows.
154 Returns the results as a list of single values
159 conn = connect(**self.dbconfig)
160 cursor = conn.cursor()
161 cursor.execute(*args)
171 class PhpbbAuth(BaseAuth):
172 logout_possible = True
173 login_inputs = ['username', 'password']
175 def __init__(self, name='phpbb', hint=None, **kwargs):
177 Authenticate using credentials from a phpbb database
179 The name parameter should be unique among all authentication methods.
181 The hint parameter is a snippet of HTML that is displayed below the login form.
183 self.dbconfig = kwargs
187 def check_login(self, request, username, password):
188 """ Checks the given username password combination. Returns the
189 corresponding emailaddress, or False if authentication failed.
191 conn = connect(**self.dbconfig)
196 # Get some data. Note that we interpolate the prefix ourselves, since
197 # letting the mysql library do it only works with values (it adds ''
198 # automatically). Note also that this allows possible SQL injection
199 # through the phpbb_prefix variable, but that should be a trusted
201 cursor = conn.cursor ()
202 cursor.execute ("SELECT user_password,user_email FROM `%susers` WHERE username=%%s" % self.dbconfig['phpbb_prefix'], username)
205 if (cursor.rowcount == 0):
210 row = cursor.fetchone()
213 if (md5.new(password).hexdigest() == row[0]):
218 def login(self, request, user_obj, **kw):
220 Handle a login. Called by moinmoin.
223 username = kw.get('username')
224 password = kw.get('password')
226 logging.debug("phpbb_login: Trying to log in, username=%r " % (username))
228 # simply continue if something else already logged in
230 if user_obj and user_obj.valid:
231 return ContinueLogin(user_obj)
233 # Deny empty username or passwords
234 if not username or not password:
235 return ContinueLogin(user_obj)
237 email = self.check_login(request, username, password)
241 logging.debug("phpbb_login: authentication failed for %s" % (username))
242 return ContinueLogin(user_obj)
244 logging.debug("phpbb_login: authenticated %s (email %s)" % (username, email))
246 u = user.User(request, auth_username=username, auth_method=self.name, auth_attribs=('name', 'password', 'email'))
248 #u.remember_me = 0 # 0 enforces cookie_lifetime config param
249 u.create_or_update(True)
251 return ContinueLogin(u)
255 info = sys.exc_info()
256 logging.error("phpbb_login: authentication failed due to unexpected exception, traceback follows...")
257 logging.error(''.join(traceback.format_exception(*info)))
258 return ContinueLogin(user_obj)
260 def login_hint(self, request):
261 """ Return a snippet of HTML that is displayed with the login form. """
266 Setup the phpbb backend. Takes the following keyword arguments:
267 dbhost -- The database server host
268 dbport -- The database server portname
269 dbname -- The database name
270 dbuser -- The username to log in
271 dbpass -- The password to log in
272 phpbb_prefix -- The table name prefix used for this phpbb installation
273 name -- The name to use for the auth backend
274 hint -- A hint to show in the login interface (HTML string)
276 This function can be called multiple times to create backends for
277 different phpbb installations
279 Returns a tuple (auth, groups) containing an (instantiated) auth backend
280 and a groups backend (constructor). These can be put directly into the
281 "auth" (as part of the list) and "groups" (directly) config directives.
286 (phpbb_auth, phpbb_groups) = phpbb.setup(...)
288 groups = phpbb_groups
290 (actual configuration parameters to setup() are omitted in this example)
293 # Create a "constructor" to create a phpbb_groups instance, while
294 # passing ourselves to it.
295 groups = lambda config, request: PhpbbGroupsBackend(request, **kwargs)
296 # Create an instantiated auth backend.
297 auth = PhpbbAuth(**kwargs)
299 return (auth, groups)
301 # vim: set sw=4 expandtab sts=4:vim