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")
35 results
= self
.ldap
.search_ext_s(search_base
, ldap
.SCOPE_SUBTREE
,
36 query
, attrlist
=attrlist
, sizelimit
=limit
)
38 # Close current connection
45 def search(self
, query
, limit
=0):
46 results
= self
._search
(query
, limit
=limit
)
49 for dn
, attrs
in results
:
50 account
= Account(self
.backend
, dn
, attrs
)
51 accounts
.append(account
)
53 return sorted(accounts
)
55 def search_one(self
, query
):
56 result
= self
.search(query
, limit
=1)
57 assert len(result
) <= 1
63 # Only return developers (group with ID 500)
64 return self
.search("(&(objectClass=posixAccount)(gidNumber=500))")
68 def get_by_uid(self
, uid
):
69 return self
.search_one("(&(objectClass=posixAccount)(uid=%s))" % uid
)
71 def get_by_mail(self
, mail
):
72 return self
.search_one("(&(objectClass=posixAccount)(mail=%s))" % mail
)
76 def find_account(self
, s
):
77 account
= self
.get_by_uid(s
)
81 return self
.get_by_mail(s
)
83 def get_by_sip_id(self
, sip_id
):
84 return self
.search_one("(|(&(objectClass=sipUser)(sipAuthenticationUser=%s)) \
85 (&(objectClass=sipRoutingObject)(sipLocalAddress=%s)))" % (sip_id
, sip_id
))
89 def _cleanup_expired_sessions(self
):
90 self
.db
.execute("DELETE FROM sessions WHERE time_expires <= NOW()")
92 def create_session(self
, account
, host
):
93 self
._cleanup
_expired
_sessions
()
95 res
= self
.db
.get("INSERT INTO sessions(host, uid) VALUES(%s, %s) \
96 RETURNING session_id, time_expires", host
, account
.uid
)
98 # Session could not be created
102 logging
.info("Created session %s for %s which expires %s" \
103 % (res
.session_id
, account
, res
.time_expires
))
104 return res
.session_id
, res
.time_expires
106 def destroy_session(self
, session_id
, host
):
107 logging
.info("Destroying session %s" % session_id
)
109 self
.db
.execute("DELETE FROM sessions \
110 WHERE session_id = %s AND host = %s", session_id
, host
)
111 self
._cleanup
_expired
_sessions
()
113 def get_by_session(self
, session_id
, host
):
114 logging
.debug("Looking up session %s" % session_id
)
116 res
= self
.db
.get("SELECT uid FROM sessions WHERE session_id = %s \
117 AND host = %s AND NOW() BETWEEN time_created AND time_expires",
120 # Session does not exist or has expired
124 # Update the session expiration time
125 self
.db
.execute("UPDATE sessions SET time_expires = NOW() + INTERVAL '14 days' \
126 WHERE session_id = %s AND host = %s", session_id
, host
)
128 return self
.get_by_uid(res
.uid
)
131 class Account(Object
):
132 def __init__(self
, backend
, dn
, attrs
=None):
133 Object
.__init
__(self
, backend
)
136 self
.__attrs
= attrs
or {}
139 return "<%s %s>" % (self
.__class
__.__name
__, self
.dn
)
141 def __cmp__(self
, other
):
142 return cmp(self
.name
, other
.name
)
146 return self
.accounts
.ldap
149 def attributes(self
):
152 def _get_first_attribute(self
, attr
, default
=None):
153 if not self
.attributes
.has_key(attr
):
156 res
= self
.attributes
.get(attr
, [])
162 attribute
= self
.attributes
[key
]
164 raise AttributeError(key
)
166 if len(attribute
) == 1:
171 def check_password(self
, password
):
173 Bind to the server with given credentials and return
174 true if password is corrent and false if not.
176 Raises exceptions from the server on any other errors.
179 logging
.debug("Checking credentials for %s" % self
.dn
)
181 self
.ldap
.simple_bind_s(self
.dn
, password
.encode("utf-8"))
182 except ldap
.INVALID_CREDENTIALS
:
183 logging
.debug("Account credentials are invalid.")
186 logging
.debug("Successfully authenticated.")
190 return "wheel" in self
.groups
192 def is_talk_enabled(self
):
193 return "sipUser" in self
.classes
or "sipRoutingObject" in self
.classes \
194 or self
.telephone_numbers
or self
.address
198 return self
.attributes
.get("objectClass", [])
202 return self
._get
_first
_attribute
("uid")
206 return self
._get
_first
_attribute
("cn")
209 def first_name(self
):
210 return self
._get
_first
_attribute
("givenName")
214 if not hasattr(self
, "_groups"):
217 res
= self
.accounts
._search
("(&(objectClass=posixGroup) \
218 (memberUid=%s))" % self
.uid
, ["cn"])
220 for dn
, attrs
in res
:
221 cns
= attrs
.get("cn")
223 self
._groups
.append(cns
[0])
229 address
= self
._get
_first
_attribute
("homePostalAddress", "")
230 address
= address
.replace(", ", "\n")
236 name
= self
.name
.lower()
237 name
= name
.replace(" ", ".")
238 name
= name
.replace("Ä", "Ae")
239 name
= name
.replace("Ö", "Oe")
240 name
= name
.replace("Ü", "Ue")
241 name
= name
.replace("ä", "ae")
242 name
= name
.replace("ö", "oe")
243 name
= name
.replace("ü", "ue")
245 for mail
in self
.attributes
.get("mail", []):
246 if mail
.startswith("%s@ipfire.org" % name
):
249 # If everything else fails, we will go with the UID
250 return "%s@ipfire.org" % self
.uid
254 if "sipUser" in self
.classes
:
255 return self
._get
_first
_attribute
("sipAuthenticationUser")
257 if "sipRoutingObject" in self
.classes
:
258 return self
._get
_first
_attribute
("sipLocalAddress")
261 def sip_password(self
):
262 return self
._get
_first
_attribute
("sipPassword")
266 return "%s@ipfire.org" % self
.sip_id
268 def uses_sip_forwarding(self
):
269 if self
.sip_routing_url
:
275 def sip_routing_url(self
):
276 if "sipRoutingObject" in self
.classes
:
277 return self
._get
_first
_attribute
("sipRoutingAddress")
279 def sip_is_online(self
):
282 if not hasattr(self
, "_is_online"):
283 self
._is
_online
= self
.backend
.talk
.user_is_online(self
.sip_id
)
285 return self
._is
_online
288 def telephone_numbers(self
):
289 return self
._telephone
_numbers
+ self
.mobile_telephone_numbers \
290 + self
.home_telephone_numbers
293 def _telephone_numbers(self
):
294 return self
.attributes
.get("telephoneNumber") or []
297 def home_telephone_numbers(self
):
298 return self
.attributes
.get("homePhone") or []
301 def mobile_telephone_numbers(self
):
302 return self
.attributes
.get("mobile") or []
304 def avatar_url(self
, size
=None):
305 if self
.backend
.debug
:
306 hostname
= "accounts.dev.ipfire.org"
308 hostname
= "accounts.ipfire.org"
310 url
= "https://%s/avatar/%s.jpg" % (hostname
, self
.uid
)
313 url
+= "?size=%s" % size
317 gravatar_icon
= avatar_url
319 def get_avatar(self
, size
=None):
320 avatar
= self
._get
_first
_attribute
("jpegPhoto")
327 return self
._resize
_avatar
(avatar
, size
)
329 def _resize_avatar(self
, image
, size
):
330 image
= StringIO
.StringIO(image
)
331 image
= PIL
.Image
.open(image
)
333 # Resize the image to the desired resolution
334 image
.thumbnail((size
, size
), PIL
.Image
.ANTIALIAS
)
336 f
= StringIO
.StringIO()
338 # If writing out the image does not work with optimization,
339 # we try to write it out without any optimization.
341 image
.save(f
, "JPEG", optimize
=True)
343 image
.save(f
, "JPEG")
347 def get_gravatar_url(self
, size
=128):
349 gravatar_email
= self
.email
.lower()
351 gravatar_email
= "nobody@ipfire.org"
354 gravatar_url
= "https://www.gravatar.com/avatar/" + \
355 hashlib
.md5(gravatar_email
).hexdigest() + "?"
356 gravatar_url
+= urllib
.urlencode({'d': "mm", 's': str(size
)})
361 if __name__
== "__main__":