11 from .decorators
import *
12 from .misc
import Object
14 class Accounts(Object
):
17 if not hasattr(self
, "_ldap"):
18 # Connect to LDAP server
19 ldap_uri
= self
.settings
.get("ldap_uri")
20 self
._ldap
= ldap
.initialize(ldap_uri
)
22 # Bind with username and password
23 bind_dn
= self
.settings
.get("ldap_bind_dn")
25 bind_pw
= self
.settings
.get("ldap_bind_pw", "")
26 self
._ldap
.simple_bind(bind_dn
, bind_pw
)
30 def _search(self
, query
, attrlist
=None, limit
=0):
31 logging
.debug("Performing LDAP query: %s" % query
)
33 search_base
= self
.settings
.get("ldap_search_base")
36 results
= self
.ldap
.search_ext_s(search_base
, ldap
.SCOPE_SUBTREE
,
37 query
, attrlist
=attrlist
, sizelimit
=limit
)
39 # Close current connection
46 def search(self
, query
, limit
=0):
47 results
= self
._search
(query
, limit
=limit
)
50 for dn
, attrs
in results
:
51 account
= Account(self
.backend
, dn
, attrs
)
52 accounts
.append(account
)
54 return sorted(accounts
)
56 def search_one(self
, query
):
57 result
= self
.search(query
, limit
=1)
58 assert len(result
) <= 1
64 # Only return developers (group with ID 1000)
65 return self
.search("(&(objectClass=posixAccount)(gidNumber=1000))")
69 def get_by_uid(self
, uid
):
70 return self
.search_one("(&(objectClass=posixAccount)(uid=%s))" % uid
)
72 def get_by_mail(self
, mail
):
73 return self
.search_one("(&(objectClass=posixAccount)(mail=%s))" % mail
)
77 def find_account(self
, s
):
78 account
= self
.get_by_uid(s
)
82 return self
.get_by_mail(s
)
84 def get_by_sip_id(self
, sip_id
):
85 return self
.search_one("(|(&(objectClass=sipUser)(sipAuthenticationUser=%s)) \
86 (&(objectClass=sipRoutingObject)(sipLocalAddress=%s)))" % (sip_id
, sip_id
))
90 def _cleanup_expired_sessions(self
):
91 self
.db
.execute("DELETE FROM sessions WHERE time_expires <= NOW()")
93 def create_session(self
, account
, host
):
94 self
._cleanup
_expired
_sessions
()
96 res
= self
.db
.get("INSERT INTO sessions(host, uid) VALUES(%s, %s) \
97 RETURNING session_id, time_expires", host
, account
.uid
)
99 # Session could not be created
103 logging
.info("Created session %s for %s which expires %s" \
104 % (res
.session_id
, account
, res
.time_expires
))
105 return res
.session_id
, res
.time_expires
107 def destroy_session(self
, session_id
, host
):
108 logging
.info("Destroying session %s" % session_id
)
110 self
.db
.execute("DELETE FROM sessions \
111 WHERE session_id = %s AND host = %s", session_id
, host
)
112 self
._cleanup
_expired
_sessions
()
114 def get_by_session(self
, session_id
, host
):
115 logging
.debug("Looking up session %s" % session_id
)
117 res
= self
.db
.get("SELECT uid FROM sessions WHERE session_id = %s \
118 AND host = %s AND NOW() BETWEEN time_created AND time_expires",
121 # Session does not exist or has expired
125 # Update the session expiration time
126 self
.db
.execute("UPDATE sessions SET time_expires = NOW() + INTERVAL '14 days' \
127 WHERE session_id = %s AND host = %s", session_id
, host
)
129 return self
.get_by_uid(res
.uid
)
132 class Account(Object
):
133 def __init__(self
, backend
, dn
, attrs
=None):
134 Object
.__init
__(self
, backend
)
137 self
.__attrs
= attrs
or {}
143 return "<%s %s>" % (self
.__class
__.__name
__, self
.dn
)
145 def __eq__(self
, other
):
146 if isinstance(other
, self
.__class
__):
147 return self
.dn
== other
.dn
149 def __lt__(self
, other
):
150 if isinstance(other
, self
.__class
__):
151 return self
.name
< other
.name
155 return self
.accounts
.ldap
158 def attributes(self
):
161 def _get_first_attribute(self
, attr
, default
=None):
162 if attr
not in self
.attributes
:
165 res
= self
.attributes
.get(attr
, [])
167 return res
[0].decode()
171 attribute
= self
.attributes
[key
]
173 raise AttributeError(key
)
175 if len(attribute
) == 1:
180 def check_password(self
, password
):
182 Bind to the server with given credentials and return
183 true if password is corrent and false if not.
185 Raises exceptions from the server on any other errors.
188 logging
.debug("Checking credentials for %s" % self
.dn
)
190 self
.ldap
.simple_bind_s(self
.dn
, password
.encode("utf-8"))
191 except ldap
.INVALID_CREDENTIALS
:
192 logging
.debug("Account credentials are invalid.")
195 logging
.debug("Successfully authenticated.")
199 return "wheel" in self
.groups
201 def is_talk_enabled(self
):
202 return "sipUser" in self
.classes
or "sipRoutingObject" in self
.classes \
203 or self
.telephone_numbers
or self
.address
207 return (x
.decode() for x
in self
.attributes
.get("objectClass", []))
211 return self
._get
_first
_attribute
("uid")
215 return self
._get
_first
_attribute
("cn")
218 def first_name(self
):
219 return self
._get
_first
_attribute
("givenName")
223 if not hasattr(self
, "_groups"):
226 res
= self
.accounts
._search
("(&(objectClass=posixGroup) \
227 (memberUid=%s))" % self
.uid
, ["cn"])
229 for dn
, attrs
in res
:
230 cns
= attrs
.get("cn")
232 self
._groups
.append(cns
[0].decode())
238 address
= self
._get
_first
_attribute
("homePostalAddress", "")
239 address
= address
.replace(", ", "\n")
245 name
= self
.name
.lower()
246 name
= name
.replace(" ", ".")
247 name
= name
.replace("Ä", "Ae")
248 name
= name
.replace("Ö", "Oe")
249 name
= name
.replace("Ü", "Ue")
250 name
= name
.replace("ä", "ae")
251 name
= name
.replace("ö", "oe")
252 name
= name
.replace("ü", "ue")
254 for mail
in self
.attributes
.get("mail", []):
255 if mail
.decode().startswith("%s@ipfire.org" % name
):
258 # If everything else fails, we will go with the UID
259 return "%s@ipfire.org" % self
.uid
263 if "sipUser" in self
.classes
:
264 return self
._get
_first
_attribute
("sipAuthenticationUser")
266 if "sipRoutingObject" in self
.classes
:
267 return self
._get
_first
_attribute
("sipLocalAddress")
270 def sip_password(self
):
271 return self
._get
_first
_attribute
("sipPassword")
275 return "%s@ipfire.org" % self
.sip_id
277 def uses_sip_forwarding(self
):
278 if self
.sip_routing_url
:
284 def sip_routing_url(self
):
285 if "sipRoutingObject" in self
.classes
:
286 return self
._get
_first
_attribute
("sipRoutingAddress")
288 def sip_is_online(self
):
291 if not hasattr(self
, "_is_online"):
292 self
._is
_online
= self
.backend
.talk
.user_is_online(self
.sip_id
)
294 return self
._is
_online
297 def sip_registrations(self
):
298 sip_registrations
= []
300 for reg
in self
.backend
.talk
.freeswitch
.get_sip_registrations(self
.sip_url
):
303 sip_registrations
.append(reg
)
305 return sip_registrations
308 def telephone_numbers(self
):
309 return self
._telephone
_numbers
+ self
.mobile_telephone_numbers \
310 + self
.home_telephone_numbers
313 def _telephone_numbers(self
):
314 return self
.attributes
.get("telephoneNumber") or []
317 def home_telephone_numbers(self
):
318 return self
.attributes
.get("homePhone") or []
321 def mobile_telephone_numbers(self
):
322 return self
.attributes
.get("mobile") or []
324 def avatar_url(self
, size
=None):
325 if self
.backend
.debug
:
326 hostname
= "accounts.dev.ipfire.org"
328 hostname
= "accounts.ipfire.org"
330 url
= "https://%s/avatar/%s.jpg" % (hostname
, self
.uid
)
333 url
+= "?size=%s" % size
337 def get_avatar(self
, size
=None):
338 avatar
= self
._get
_first
_attribute
("jpegPhoto")
345 return self
._resize
_avatar
(avatar
, size
)
347 def _resize_avatar(self
, image
, size
):
348 image
= io
.StringIO(image
)
349 image
= PIL
.Image
.open(image
)
351 # Resize the image to the desired resolution
352 image
.thumbnail((size
, size
), PIL
.Image
.ANTIALIAS
)
356 # If writing out the image does not work with optimization,
357 # we try to write it out without any optimization.
359 image
.save(f
, "JPEG", optimize
=True)
361 image
.save(f
, "JPEG")
366 if __name__
== "__main__":