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'),))
161 # If you want to use different operators
163 # return (radiusd.RLM_MODULE_UPDATED,
165 # ('Session-Timeout', ':=', str(sessionTimeout)),
166 # ('Some-other-option', '-=', Value'),
169 # ('Auth-Type', ':=', 'python'),
175 return radiusd.RLM_MODULE_OK
180 return radiusd.RLM_MODULE_OK
183 def accounting(acctData):
185 # Extract the data we need.
188 acctSessionTime = None
189 acctStatusType = None
191 # xxx A dict would make this nice.
193 if t[0] == 'User-Name':
195 elif t[0] == 'Acct-Session-Time':
196 acctSessionTime = t[1]
197 elif t[0] == 'Acct-Status-Type':
198 acctStatusType = t[1]
201 # We will not deal with Start for now.
202 # We may later, for simultaneous checks and the like.
203 if acctStatusType == 'Start':
204 return radiusd.RLM_MODULE_OK
206 # Build and log the SQL statement
207 # radiusd puts double quotes (") around the string representation of
210 # xxx This is simplistic as it does not record the time, etc.
212 sql = 'insert into sessions (username, seconds) values (%s, %d)' % \
213 (userName, int(acctSessionTime))
215 log(radiusd.L_DBG, sql)
218 # xxx Or should this be one cursor all throughout?
220 dbCursor = dbHandle.cursor()
221 except MySQLdb.OperationalError, e:
222 log(radiusd.L_ERR, str(e))
223 return radiusd.RLM_MODULE_FAIL
225 # Execute the SQL statement
227 dbCursor.execute(sql)
228 except MySQLdb.OperationalError, e:
229 log(radiusd.L_ERR, str(e))
231 return radiusd.RLM_MODULE_FAIL
234 return radiusd.RLM_MODULE_OK
238 """Detach and clean up."""
239 # Shut down the database connection.
241 log(radiusd.L_DBG, 'closing database handle: ' + str(dbHandle))
244 return radiusd.RLM_MODULE_OK
249 if __name__ == '__main__':
251 print authorize((('User-Name', '"map"'), ('User-Password', '"abc"')))