]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - webapp/backend/accounts.py
e52b1899f99ffcdf61199376758cb681486d2d41
[people/shoehn/ipfire.org.git] / webapp / backend / accounts.py
1 #!/usr/bin/python
2 # encoding: utf-8
3
4 import PIL
5 import StringIO
6 import hashlib
7 import ldap
8 import logging
9 import urllib
10
11 from misc import Object
12
13 class Accounts(Object):
14 @property
15 def ldap(self):
16 if not hasattr(self, "_ldap"):
17 # Connect to LDAP server
18 ldap_uri = self.settings.get("ldap_uri")
19 self._ldap = ldap.initialize(ldap_uri)
20
21 # Bind with username and password
22 bind_dn = self.settings.get("ldap_bind_dn")
23 if bind_dn:
24 bind_pw = self.settings.get("ldap_bind_pw", "")
25 self._ldap.simple_bind(bind_dn, bind_pw)
26
27 return self._ldap
28
29 def _search(self, query, attrlist=None, limit=0):
30 logging.debug("Performing LDAP query: %s" % query)
31
32 search_base = self.settings.get("ldap_search_base")
33 results = self.ldap.search_ext_s(search_base, ldap.SCOPE_SUBTREE,
34 query, attrlist=attrlist, sizelimit=limit)
35
36 return results
37
38 def search(self, query, limit=0):
39 results = self._search(query, limit=limit)
40
41 accounts = []
42 for dn, attrs in results:
43 account = Account(self.backend, dn, attrs)
44 accounts.append(account)
45
46 return sorted(accounts)
47
48 def search_one(self, query):
49 result = self.search(query, limit=1)
50 assert len(result) <= 1
51
52 if result:
53 return result[0]
54
55 def get_all(self):
56 # Only return developers (group with ID 500)
57 return self.search("(&(objectClass=posixAccount)(gidNumber=500))")
58
59 list = get_all
60
61 def get_by_uid(self, uid):
62 return self.search_one("(&(objectClass=posixAccount)(uid=%s))" % uid)
63
64 def get_by_mail(self, mail):
65 return self.search_one("(&(objectClass=posixAccount)(mail=%s))" % mail)
66
67 find = get_by_uid
68
69 def find_account(self, s):
70 account = self.get_by_uid(s)
71 if account:
72 return account
73
74 return self.get_by_mail(s)
75
76 def get_by_sip_id(self, sip_id):
77 return self.search_one("(|(&(objectClass=sipUser)(sipAuthenticationUser=%s)) \
78 (&(objectClass=sipRoutingObject)(sipLocalAddress=%s)))" % (sip_id, sip_id))
79
80 # Session stuff
81
82 def _cleanup_expired_sessions(self):
83 self.db.execute("DELETE FROM sessions WHERE time_expires <= NOW()")
84
85 def create_session(self, account, host):
86 self._cleanup_expired_sessions()
87
88 res = self.db.get("INSERT INTO sessions(host, uid) VALUES(%s, %s) \
89 RETURNING session_id, time_expires", host, account.uid)
90
91 # Session could not be created
92 if not res:
93 return None, None
94
95 logging.info("Created session %s for %s which expires %s" \
96 % (res.session_id, account, res.time_expires))
97 return res.session_id, res.time_expires
98
99 def destroy_session(self, session_id, host):
100 logging.info("Destroying session %s" % session_id)
101
102 self.db.execute("DELETE FROM sessions \
103 WHERE session_id = %s AND host = %s", session_id, host)
104 self._cleanup_expired_sessions()
105
106 def get_by_session(self, session_id, host):
107 logging.debug("Looking up session %s" % session_id)
108
109 res = self.db.get("SELECT uid FROM sessions WHERE session_id = %s \
110 AND host = %s AND NOW() BETWEEN time_created AND time_expires",
111 session_id, host)
112
113 # Session does not exist or has expired
114 if not res:
115 return
116
117 # Update the session expiration time
118 self.db.execute("UPDATE sessions SET time_expires = NOW() + INTERVAL '14 days' \
119 WHERE session_id = %s AND host = %s", session_id, host)
120
121 return self.get_by_uid(res.uid)
122
123
124 class Account(Object):
125 def __init__(self, backend, dn, attrs=None):
126 Object.__init__(self, backend)
127 self.dn = dn
128
129 self.__attrs = attrs or {}
130
131 def __repr__(self):
132 return "<%s %s>" % (self.__class__.__name__, self.dn)
133
134 def __cmp__(self, other):
135 return cmp(self.name, other.name)
136
137 @property
138 def ldap(self):
139 return self.accounts.ldap
140
141 @property
142 def attributes(self):
143 return self.__attrs
144
145 def _get_first_attribute(self, attr, default=None):
146 if not self.attributes.has_key(attr):
147 return default
148
149 res = self.attributes.get(attr, [])
150 if res:
151 return res[0]
152
153 def get(self, key):
154 try:
155 attribute = self.attributes[key]
156 except KeyError:
157 raise AttributeError(key)
158
159 if len(attribute) == 1:
160 return attribute[0]
161
162 return attribute
163
164 def check_password(self, password):
165 """
166 Bind to the server with given credentials and return
167 true if password is corrent and false if not.
168
169 Raises exceptions from the server on any other errors.
170 """
171
172 logging.debug("Checking credentials for %s" % self.dn)
173 try:
174 self.ldap.simple_bind_s(self.dn, password.encode("utf-8"))
175 except ldap.INVALID_CREDENTIALS:
176 logging.debug("Account credentials are invalid.")
177 return False
178
179 logging.debug("Successfully authenticated.")
180 return True
181
182 def is_admin(self):
183 return "admins" in self.groups
184
185 def is_talk_enabled(self):
186 return "sipUser" in self.classes or "sipRoutingObject" in self.classes
187
188 @property
189 def classes(self):
190 return self.attributes.get("objectClass", [])
191
192 @property
193 def uid(self):
194 return self._get_first_attribute("uid")
195
196 @property
197 def name(self):
198 return self._get_first_attribute("cn")
199
200 @property
201 def first_name(self):
202 return self._get_first_attribute("givenName")
203
204 @property
205 def groups(self):
206 if not hasattr(self, "_groups"):
207 self._groups = []
208
209 res = self.accounts._search("(&(objectClass=posixGroup) \
210 (memberUid=%s))" % self.uid, ["cn"])
211
212 for dn, attrs in res:
213 cns = attrs.get("cn")
214 if cns:
215 self._groups.append(cns[0])
216
217 return self._groups
218
219 @property
220 def address(self):
221 address = self._get_first_attribute("homePostalAddress", "")
222 address = address.replace(", ", "\n")
223
224 return address
225
226 @property
227 def email(self):
228 name = self.name.lower()
229 name = name.replace(" ", ".")
230 name = name.replace("Ä", "Ae")
231 name = name.replace("Ö", "Oe")
232 name = name.replace("Ü", "Ue")
233 name = name.replace("ä", "ae")
234 name = name.replace("ö", "oe")
235 name = name.replace("ü", "ue")
236
237 for mail in self.attributes.get("mail", []):
238 if mail.startswith("%s@ipfire.org" % name):
239 return mail
240
241 # If everything else fails, we will go with the UID
242 return "%s@ipfire.org" % self.uid
243
244 @property
245 def sip_id(self):
246 if "sipUser" in self.classes:
247 return self._get_first_attribute("sipAuthenticationUser")
248
249 if "sipRoutingObject" in self.classes:
250 return self._get_first_attribute("sipLocalAddress")
251
252 @property
253 def sip_password(self):
254 return self._get_first_attribute("sipPassword")
255
256 @property
257 def sip_url(self):
258 return "%s@ipfire.org" % self.sip_id
259
260 def uses_sip_forwarding(self):
261 if self.sip_routing_url:
262 return True
263
264 return False
265
266 @property
267 def sip_routing_url(self):
268 if "sipRoutingObject" in self.classes:
269 return self._get_first_attribute("sipRoutingAddress")
270
271 def sip_is_online(self):
272 assert self.sip_id
273
274 if not hasattr(self, "_is_online"):
275 self._is_online = self.backend.talk.user_is_online(self.sip_id)
276
277 return self._is_online
278
279 @property
280 def telephone_numbers(self):
281 return self.attributes.get("telephoneNumber")
282
283 def avatar_url(self, size=None):
284 if self.backend.debug:
285 hostname = "accounts.dev.ipfire.org"
286 else:
287 hostname = "accounts.ipfire.org"
288
289 url = "http://%s/avatar/%s.jpg" % (hostname, self.uid)
290
291 if size:
292 url += "?size=%s" % size
293
294 return url
295
296 gravatar_icon = avatar_url
297
298 def get_avatar(self, size=None):
299 avatar = self._get_first_attribute("jpegPhoto")
300 if not avatar:
301 return
302
303 if not size:
304 return avatar
305
306 return self._resize_avatar(avatar, size)
307
308 def _resize_avatar(self, image, size):
309 image = StringIO.StringIO(image)
310 image = PIL.Image.open(image)
311
312 # Resize the image to the desired resolution
313 image.thumbnail((size, size), PIL.Image.ANTIALIAS)
314
315 f = StringIO.StringIO()
316
317 # If writing out the image does not work with optimization,
318 # we try to write it out without any optimization.
319 try:
320 image.save(f, "JPEG", optimize=True)
321 except:
322 image.save(f, "JPEG")
323
324 return f.getvalue()
325
326 def get_gravatar_url(self, size=128):
327 try:
328 gravatar_email = self.email.lower()
329 except:
330 gravatar_email = "nobody@ipfire.org"
331
332 # construct the url
333 gravatar_url = "http://www.gravatar.com/avatar/" + \
334 hashlib.md5(gravatar_email).hexdigest() + "?"
335 gravatar_url += urllib.urlencode({'d': "mm", 's': str(size)})
336
337 return gravatar_url
338
339
340 if __name__ == "__main__":
341 a = Accounts()
342
343 print a.list()