list2form : gestion des erreurs et des petits fichiers (merci cgi.py…)
[progfou.git] / ejabberd / auth-mysql.py
CommitLineData
42b8c4c3
P
1#!/usr/bin/env python
2# -*- coding: UTF-8 -*-
3# Depends: python-mysqldb
4a082535
P
4# install -o root -g ejabberd -m 0750 -c auth-mysql.py /etc/ejabberd/
5# install -o ejabberd -g adm -m 0640 -c /dev/null /var/log/ejabberd/auth-mysql.log
6# Test: printf '\000\031isuser:moussa.nombre:test' | ./auth-mysql.py | hd
7
42b8c4c3
P
8import sys
9import traceback
10import struct
11import time
12import MySQLdb
13import crypt
14
15# TODO: transformer toute la partie gestion de BdD en une classe
16# TODO: connexion persistente et gérer les coupures de service MySQL
17
18_host = 'nss'
19_user = 'jabber'
20_passwd = 'password'
4a082535
P
21_db = 'mail'
22_timeout = 2
23_query = "SELECT * FROM auforg_virtual WHERE source=%s AND LENGTH(password)>1"
f30ec2ce
P
24_log_filename = '/var/log/ejabberd/auth-mysql.log'
25_cache_positive_ttl = (15*60)
26_cache_negative_ttl = (1*60)
27
28# ce qui suit est à usage interne, ne pas toucher...
29_find_user_cache = {}
30_authenticate_user_cache = {}
42b8c4c3 31
4a082535 32def find_user(user, host):
f30ec2ce
P
33 """
34 Returns (found, in_cache)
35 where 'found' means the (user,host) was found in the database
36 and 'in_cache' means it was found from the local cache
37 """
38 now = time.time()
39
40 global _find_user_cache
41 c = _find_user_cache.get((user,host))
42 if c != None:
43 if c['found'] and now < (c['time'] + _cache_positive_ttl):
44 return (True, True)
45 if not c['found'] and now < (c['time'] + _cache_negative_ttl):
46 return (False, True)
47
42b8c4c3
P
48 global _host, _user, _passwd, _db
49 db = MySQLdb.connect(host=_host, user=_user, passwd=_passwd,
4a082535 50 db=_db, connect_timeout=_timeout)
42b8c4c3 51 cur = db.cursor(MySQLdb.cursors.DictCursor)
4a082535 52 nrows = cur.execute(_query, ('%s@%s' % (user,host), ))
42b8c4c3 53 del cur, db
f30ec2ce
P
54 found = (nrows > 0)
55 _find_user_cache[(user,host)] = {'found': found, 'time': now}
56 return (found, False)
42b8c4c3 57
4a082535 58def authenticate_user(user, host, password):
f30ec2ce
P
59 """
60 Returns (valid, in_cache)
61 where 'valid' means the (user,host,password) was valid in the database
62 and 'in_cache' means it was validated against the local cache
63 """
64 now = time.time()
65
66 global _authenticate_user_cache
67 c = _authenticate_user_cache.get((user,host,password))
68 if c != None:
69 if c['valid'] and now < (c['time'] + _cache_positive_ttl):
70 return (True, True)
71 if not c['valid'] and now < (c['time'] + _cache_negative_ttl):
72 return (False, True)
73
42b8c4c3
P
74 global _host, _user, _passwd, _db
75 db = MySQLdb.connect(host=_host, user=_user, passwd=_passwd,
4a082535 76 db=_db, connect_timeout=_timeout)
42b8c4c3 77 cur = db.cursor(MySQLdb.cursors.DictCursor)
4a082535 78 nrows = cur.execute(_query, ('%s@%s' % (user,host), ))
42b8c4c3
P
79 users = cur.fetchall()
80 del cur, db
f30ec2ce
P
81 valid = False
82 if nrows > 0:
83 for u in users:
84 if crypt.crypt(password, u['password']) == u['password']:
85 valid = True
86 break
87 _authenticate_user_cache[(user,host,password)] = {'valid': valid, 'time': now}
88 return (valid, False)
42b8c4c3
P
89
90def main():
f30ec2ce 91 log_file = open(_log_filename, 'a')
42b8c4c3
P
92 while True:
93 try:
94 nread = sys.stdin.read(2)
95 if len(nread) == 0:
96 time.sleep(0.25)
97 continue
98 now = time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())
99 if len(nread) < 2:
100 log_file.write('%s bytes_read=%d\n' % (now, len(nread)))
101 log_file.flush()
102 continue
103 size = struct.unpack('>h', nread)[0]
104 data = sys.stdin.read(size)
105 (operation, data) = data.split(':', 1)
106 if operation == 'auth':
107 (user, host, password) = data.split(':', 2)
108 log_file.write('%s operation=%s user=%s host=%s\n'
109 % (now, operation, user, host))
110 log_file.flush()
f30ec2ce 111 (result, in_cache) = authenticate_user(user, host, password)
42b8c4c3
P
112 elif operation == 'isuser':
113 (user, host) = data.split(':', 1)
114 log_file.write('%s operation=%s user=%s host=%s\n'
115 % (now, operation, user, host))
116 log_file.flush()
f30ec2ce 117 (result, in_cache) = find_user(user, host)
42b8c4c3
P
118 elif operation == 'setpass':
119 (user, host, password) = data.split(':', 2)
120 log_file.write('%s operation=%s user=%s host=%s\n'
121 % (now, operation, user, host))
122 log_file.flush()
4a082535 123 #result = set_user_password(user, host, password)
42b8c4c3 124 result = False
f30ec2ce 125 in_cache = False
42b8c4c3
P
126 else:
127 result = False
f30ec2ce
P
128 in_cache = False
129 if in_cache:
130 in_cache = ' (from cache)'
131 else:
132 in_cache = ''
133 log_file.write('%s => result=%s%s\n' % (now, result, in_cache))
42b8c4c3
P
134 log_file.flush()
135 sys.stdout.write(struct.pack('>hh', 2, result and 1 or 0))
136 sys.stdout.flush()
137 except:
138 traceback.print_exc(file=log_file)
139 #sys.exit()
140
141if __name__ == '__main__':
142 main()