3 # Example Python module for prepaid usage using MySQL
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 # Copyright 2002 Miguel A.L. Paraz <mparaz@mparaz.com>
18 # Copyright 2002 Imperium Technology, Inc.
26 configDb = 'python' # Database name
27 configHost = 'localhost' # Database host
28 configUser = 'python' # Database user and password
29 configPasswd = 'python'
38 radiusd.radlog(level, 'prepaid.py: ' + s)
41 """Module Instantiation. 0 for success, -1 for failure.
42 p is a dummy variable here."""
48 dbHandle = MySQLdb.connect(db=configDb, host=configHost,
49 user=configUser, passwd=configPasswd)
51 except MySQLdb.OperationalError, e:
52 # Report the error and return -1 for failure.
53 # xxx A more advanced module would retry the database.
54 log(radiusd.L_ERR, str(e))
57 log(radiusd.L_INFO, 'db connection: ' + str(dbHandle))
62 def authorize(authData):
63 """Authorization and authentication are done in one step."""
65 # Extract the data we need.
70 if t[0] == 'User-Name':
72 elif t[0] == 'Password':
75 # Build and log the SQL statement
76 # radiusd puts double quotes (") around the string representation of
78 sql = 'select passwd, maxseconds from users where username = ' + userName
80 log(radiusd.L_DBG, sql)
83 # xxx Or should this be one cursor all throughout?
85 dbCursor = dbHandle.cursor()
86 except MySQLdb.OperationalError, e:
87 log(radiusd.L_ERR, str(e))
88 return radiusd.RLM_MODULE_FAIL
90 # Execute the SQL statement
93 except MySQLdb.OperationalError, e:
94 log(radiusd.L_ERR, str(e))
96 return radiusd.RLM_MODULE_FAIL
98 # Get the result. (passwd, maxseconds)
99 result = dbCursor.fetchone()
102 log(radiusd.L_INFO, 'user not found: ' + userName)
104 return radiusd.RLM_MODULE_NOTFOUND
109 # Ignore the quotes around userPasswd.
110 if result[0] != userPasswd[1:-1]:
111 log(radiusd.L_DBG, 'user password mismatch: ' + userName)
112 return radiusd.RLM_MODULE_REJECT
114 maxSeconds = result[1]
116 # Compute their session limit
118 # Build and log the SQL statement
119 sql = 'select sum(seconds) from sessions where username = ' + userName
121 log(radiusd.L_DBG, sql)
123 # Execute the SQL statement
125 dbCursor.execute(sql)
126 except MySQLdb.OperationalError, e:
127 log(radiusd.L_ERR, str(e))
129 return radiusd.RLM_MODULE_FAIL
131 # Get the result. (sum,)
132 result = dbCursor.fetchone()
133 if (not result) or (not result[0]):
137 secondsUsed = result[0]
142 # Note that MySQL returns the result of SUM() as a float.
143 sessionTimeout = maxSeconds - int(secondsUsed)
145 if sessionTimeout <= 0:
146 # No more time, reject outright
147 log(radiusd.L_INFO, 'user out of time: ' + userName)
148 return radiusd.RLM_MODULE_REJECT
151 log(radiusd.L_DBG, 'user accepted: %s, %d seconds' %
152 (userName, sessionTimeout))
154 # We are adding to the RADIUS packet
155 # Note that the session timeout integer must be converted to string.
156 # We need to set an Auth-Type.
158 return (radiusd.RLM_MODULE_UPDATED,
159 (('Session-Timeout', str(sessionTimeout)),),
160 (('Auth-Type', 'python'),))
166 return radiusd.RLM_MODULE_OK
171 return radiusd.RLM_MODULE_OK
174 def accounting(acctData):
176 # Extract the data we need.
179 acctSessionTime = None
180 acctStatusType = None
182 # xxx A dict would make this nice.
184 if t[0] == 'User-Name':
186 elif t[0] == 'Acct-Session-Time':
187 acctSessionTime = t[1]
188 elif t[0] == 'Acct-Status-Type':
189 acctStatusType = t[1]
192 # We will not deal with Start for now.
193 # We may later, for simultaneous checks and the like.
194 if acctStatusType == 'Start':
195 return radiusd.RLM_MODULE_OK
197 # Build and log the SQL statement
198 # radiusd puts double quotes (") around the string representation of
201 # xxx This is simplistic as it does not record the time, etc.
203 sql = 'insert into sessions (username, seconds) values (%s, %d)' % \
204 (userName, int(acctSessionTime))
206 log(radiusd.L_DBG, sql)
209 # xxx Or should this be one cursor all throughout?
211 dbCursor = dbHandle.cursor()
212 except MySQLdb.OperationalError, e:
213 log(radiusd.L_ERR, str(e))
214 return radiusd.RLM_MODULE_FAIL
216 # Execute the SQL statement
218 dbCursor.execute(sql)
219 except MySQLdb.OperationalError, e:
220 log(radiusd.L_ERR, str(e))
222 return radiusd.RLM_MODULE_FAIL
225 return radiusd.RLM_MODULE_OK
229 """Detach and clean up."""
230 # Shut down the database connection.
232 log(radiusd.L_DBG, 'closing database handle: ' + str(dbHandle))
235 return radiusd.RLM_MODULE_OK
240 if __name__ == '__main__':
242 print authorize((('User-Name', '"map"'), ('User-Password', '"abc"')))