2 # Example Python module for prepaid usage using MySQL
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 # Copyright 2002 Miguel A.L. Paraz <mparaz@mparaz.com>
17 # Copyright 2002 Imperium Technology, Inc.
23 configDb = 'python' # Database name
24 configHost = 'localhost' # Database host
25 configUser = 'python' # Database user and password
26 configPasswd = 'python'
35 radiusd.radlog(level, 'prepaid.py: ' + s)
38 """Module Instantiation. 0 for success, -1 for failure.
39 p is a dummy variable here."""
45 dbHandle = MySQLdb.connect(db=configDb, host=configHost,
46 user=configUser, passwd=configPasswd)
48 except MySQLdb.OperationalError, e:
49 # Report the error and return -1 for failure.
50 # xxx A more advanced module would retry the database.
51 log(radiusd.L_ERR, str(e))
54 log(radiusd.L_INFO, 'db connection: ' + str(dbHandle))
59 def authorize(authData):
60 """Authorization and authentication are done in one step."""
62 # Extract the data we need.
67 if t[0] == 'User-Name':
69 elif t[0] == 'Password':
72 # Build and log the SQL statement
73 # radiusd puts double quotes (") around the string representation of
75 sql = 'select passwd, maxseconds from users where username = ' + userName
77 log(radiusd.L_DBG, sql)
80 # xxx Or should this be one cursor all throughout?
82 dbCursor = dbHandle.cursor()
83 except MySQLdb.OperationalError, e:
84 log(radiusd.L_ERR, str(e))
85 return radiusd.RLM_MODULE_FAIL
87 # Execute the SQL statement
90 except MySQLdb.OperationalError, e:
91 log(radiusd.L_ERR, str(e))
93 return radiusd.RLM_MODULE_FAIL
95 # Get the result. (passwd, maxseconds)
96 result = dbCursor.fetchone()
99 log(radiusd.L_INFO, 'user not found: ' + userName)
101 return radiusd.RLM_MODULE_NOTFOUND
106 # Ignore the quotes around userPasswd.
107 if result[0] != userPasswd[1:-1]:
108 log(radiusd.L_DBG, 'user password mismatch: ' + userName)
109 return radiusd.RLM_MODULE_REJECT
111 maxSeconds = result[1]
113 # Compute their session limit
115 # Build and log the SQL statement
116 sql = 'select sum(seconds) from sessions where username = ' + userName
118 log(radiusd.L_DBG, sql)
120 # Execute the SQL statement
122 dbCursor.execute(sql)
123 except MySQLdb.OperationalError, e:
124 log(radiusd.L_ERR, str(e))
126 return radiusd.RLM_MODULE_FAIL
128 # Get the result. (sum,)
129 result = dbCursor.fetchone()
130 if (not result) or (not result[0]):
134 secondsUsed = result[0]
139 # Note that MySQL returns the result of SUM() as a float.
140 sessionTimeout = maxSeconds - int(secondsUsed)
142 if sessionTimeout <= 0:
143 # No more time, reject outright
144 log(radiusd.L_INFO, 'user out of time: ' + userName)
145 return radiusd.RLM_MODULE_REJECT
148 log(radiusd.L_DBG, 'user accepted: %s, %d seconds' %
149 (userName, sessionTimeout))
151 # We are adding to the RADIUS packet
152 # Note that the session timeout integer must be converted to string.
153 # We need to set an Auth-Type.
155 return (radiusd.RLM_MODULE_UPDATED,
156 (('Session-Timeout', str(sessionTimeout)),),
157 (('Auth-Type', 'python'),))
163 return radiusd.RLM_MODULE_OK
168 return radiusd.RLM_MODULE_OK
171 def accounting(acctData):
173 # Extract the data we need.
176 acctSessionTime = None
177 acctStatusType = None
179 # xxx A dict would make this nice.
181 if t[0] == 'User-Name':
183 elif t[0] == 'Acct-Session-Time':
184 acctSessionTime = t[1]
185 elif t[0] == 'Acct-Status-Type':
186 acctStatusType = t[1]
189 # We will not deal with Start for now.
190 # We may later, for simultaneous checks and the like.
191 if acctStatusType == 'Start':
192 return radiusd.RLM_MODULE_OK
194 # Build and log the SQL statement
195 # radiusd puts double quotes (") around the string representation of
198 # xxx This is simplistic as it does not record the time, etc.
200 sql = 'insert into sessions (username, seconds) values (%s, %d)' % \
201 (userName, int(acctSessionTime))
203 log(radiusd.L_DBG, sql)
206 # xxx Or should this be one cursor all throughout?
208 dbCursor = dbHandle.cursor()
209 except MySQLdb.OperationalError, e:
210 log(radiusd.L_ERR, str(e))
211 return radiusd.RLM_MODULE_FAIL
213 # Execute the SQL statement
215 dbCursor.execute(sql)
216 except MySQLdb.OperationalError, e:
217 log(radiusd.L_ERR, str(e))
219 return radiusd.RLM_MODULE_FAIL
222 return radiusd.RLM_MODULE_OK
226 """Detach and clean up."""
227 # Shut down the database connection.
229 log(radiusd.L_DBG, 'closing database handle: ' + str(dbHandle))
232 return radiusd.RLM_MODULE_OK
237 if __name__ == '__main__':
239 print authorize((('User-Name', '"map"'), ('User-Password', '"abc"')))