11 from misc
import Object
13 class Accounts(Object
):
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
)
21 # Bind with username and password
22 bind_dn
= self
.settings
.get("ldap_bind_dn")
24 bind_pw
= self
.settings
.get("ldap_bind_pw", "")
25 self
._ldap
.simple_bind(bind_dn
, bind_pw
)
29 def _search(self
, query
, attrlist
=None, limit
=0):
30 logging
.debug("Performing LDAP query: %s" % query
)
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
)
38 def search(self
, query
, limit
=0):
39 results
= self
._search
(query
, limit
=limit
)
42 for dn
, attrs
in results
:
43 account
= Account(self
.backend
, dn
, attrs
)
44 accounts
.append(account
)
46 return sorted(accounts
)
48 def search_one(self
, query
):
49 result
= self
.search(query
, limit
=1)
50 assert len(result
) <= 1
56 # Only return developers (group with ID 500)
57 return self
.search("(&(objectClass=posixAccount)(gidNumber=500))")
61 def get_by_uid(self
, uid
):
62 return self
.search_one("(&(objectClass=posixAccount)(uid=%s))" % uid
)
64 def get_by_mail(self
, mail
):
65 return self
.search_one("(&(objectClass=posixAccount)(mail=%s))" % mail
)
69 def find_account(self
, s
):
70 account
= self
.get_by_uid(s
)
74 return self
.get_by_mail(s
)
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
))
82 def _cleanup_expired_sessions(self
):
83 self
.db
.execute("DELETE FROM sessions WHERE time_expires <= NOW()")
85 def create_session(self
, account
, host
):
86 self
._cleanup
_expired
_sessions
()
88 res
= self
.db
.get("INSERT INTO sessions(host, uid) VALUES(%s, %s) \
89 RETURNING session_id, time_expires", host
, account
.uid
)
91 # Session could not be created
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
99 def destroy_session(self
, session_id
, host
):
100 logging
.info("Destroying session %s" % session_id
)
102 self
.db
.execute("DELETE FROM sessions \
103 WHERE session_id = %s AND host = %s", session_id
, host
)
104 self
._cleanup
_expired
_sessions
()
106 def get_by_session(self
, session_id
, host
):
107 logging
.debug("Looking up session %s" % session_id
)
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",
113 # Session does not exist or has expired
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
)
121 return self
.get_by_uid(res
.uid
)
124 class Account(Object
):
125 def __init__(self
, backend
, dn
, attrs
=None):
126 Object
.__init
__(self
, backend
)
129 self
.__attrs
= attrs
or {}
132 return "<%s %s>" % (self
.__class
__.__name
__, self
.dn
)
134 def __cmp__(self
, other
):
135 return cmp(self
.name
, other
.name
)
139 return self
.accounts
.ldap
142 def attributes(self
):
145 def _get_first_attribute(self
, attr
, default
=None):
146 if not self
.attributes
.has_key(attr
):
149 res
= self
.attributes
.get(attr
, [])
155 attribute
= self
.attributes
[key
]
157 raise AttributeError(key
)
159 if len(attribute
) == 1:
164 def check_password(self
, password
):
166 Bind to the server with given credentials and return
167 true if password is corrent and false if not.
169 Raises exceptions from the server on any other errors.
172 logging
.debug("Checking credentials for %s" % self
.dn
)
174 self
.ldap
.simple_bind_s(self
.dn
, password
.encode("utf-8"))
175 except ldap
.INVALID_CREDENTIALS
:
176 logging
.debug("Account credentials are invalid.")
179 logging
.debug("Successfully authenticated.")
183 return "admins" in self
.groups
185 def is_talk_enabled(self
):
186 return "sipUser" in self
.classes
or "sipRoutingObject" in self
.classes \
187 or self
.telephone_numbers
or self
.address
191 return self
.attributes
.get("objectClass", [])
195 return self
._get
_first
_attribute
("uid")
199 return self
._get
_first
_attribute
("cn")
202 def first_name(self
):
203 return self
._get
_first
_attribute
("givenName")
207 if not hasattr(self
, "_groups"):
210 res
= self
.accounts
._search
("(&(objectClass=posixGroup) \
211 (memberUid=%s))" % self
.uid
, ["cn"])
213 for dn
, attrs
in res
:
214 cns
= attrs
.get("cn")
216 self
._groups
.append(cns
[0])
222 address
= self
._get
_first
_attribute
("homePostalAddress", "")
223 address
= address
.replace(", ", "\n")
229 name
= self
.name
.lower()
230 name
= name
.replace(" ", ".")
231 name
= name
.replace("Ä", "Ae")
232 name
= name
.replace("Ö", "Oe")
233 name
= name
.replace("Ü", "Ue")
234 name
= name
.replace("ä", "ae")
235 name
= name
.replace("ö", "oe")
236 name
= name
.replace("ü", "ue")
238 for mail
in self
.attributes
.get("mail", []):
239 if mail
.startswith("%s@ipfire.org" % name
):
242 # If everything else fails, we will go with the UID
243 return "%s@ipfire.org" % self
.uid
247 if "sipUser" in self
.classes
:
248 return self
._get
_first
_attribute
("sipAuthenticationUser")
250 if "sipRoutingObject" in self
.classes
:
251 return self
._get
_first
_attribute
("sipLocalAddress")
254 def sip_password(self
):
255 return self
._get
_first
_attribute
("sipPassword")
259 return "%s@ipfire.org" % self
.sip_id
261 def uses_sip_forwarding(self
):
262 if self
.sip_routing_url
:
268 def sip_routing_url(self
):
269 if "sipRoutingObject" in self
.classes
:
270 return self
._get
_first
_attribute
("sipRoutingAddress")
272 def sip_is_online(self
):
275 if not hasattr(self
, "_is_online"):
276 self
._is
_online
= self
.backend
.talk
.user_is_online(self
.sip_id
)
278 return self
._is
_online
281 def telephone_numbers(self
):
282 return self
._telephone
_numbers
+ self
.mobile_telephone_numbers \
283 + self
.home_telephone_numbers
286 def _telephone_numbers(self
):
287 return self
.attributes
.get("telephoneNumber") or []
290 def home_telephone_numbers(self
):
291 return self
.attributes
.get("homePhone") or []
294 def mobile_telephone_numbers(self
):
295 return self
.attributes
.get("mobile") or []
297 def avatar_url(self
, size
=None):
298 if self
.backend
.debug
:
299 hostname
= "accounts.dev.ipfire.org"
301 hostname
= "accounts.ipfire.org"
303 url
= "http://%s/avatar/%s.jpg" % (hostname
, self
.uid
)
306 url
+= "?size=%s" % size
310 gravatar_icon
= avatar_url
312 def get_avatar(self
, size
=None):
313 avatar
= self
._get
_first
_attribute
("jpegPhoto")
320 return self
._resize
_avatar
(avatar
, size
)
322 def _resize_avatar(self
, image
, size
):
323 image
= StringIO
.StringIO(image
)
324 image
= PIL
.Image
.open(image
)
326 # Resize the image to the desired resolution
327 image
.thumbnail((size
, size
), PIL
.Image
.ANTIALIAS
)
329 f
= StringIO
.StringIO()
331 # If writing out the image does not work with optimization,
332 # we try to write it out without any optimization.
334 image
.save(f
, "JPEG", optimize
=True)
336 image
.save(f
, "JPEG")
340 def get_gravatar_url(self
, size
=128):
342 gravatar_email
= self
.email
.lower()
344 gravatar_email
= "nobody@ipfire.org"
347 gravatar_url
= "http://www.gravatar.com/avatar/" + \
348 hashlib
.md5(gravatar_email
).hexdigest() + "?"
349 gravatar_url
+= urllib
.urlencode({'d': "mm", 's': str(size
)})
354 if __name__
== "__main__":