2020-04-21 00:28:12 +00:00
|
|
|
import os
|
|
|
|
|
2020-06-02 22:17:00 +00:00
|
|
|
from attr import asdict
|
2020-05-29 19:17:51 +00:00
|
|
|
from ldap3.core.exceptions import LDAPExceptionError
|
|
|
|
|
2020-06-02 22:17:00 +00:00
|
|
|
from crc import app, db
|
2020-06-04 22:03:59 +00:00
|
|
|
from ldap3 import Connection, Server, MOCK_SYNC, RESTARTABLE
|
2020-04-20 20:02:13 +00:00
|
|
|
|
|
|
|
from crc.api.common import ApiError
|
2020-06-02 22:17:00 +00:00
|
|
|
from crc.models.ldap import LdapModel, LdapSchema
|
2020-04-20 19:16:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
class LdapService(object):
|
|
|
|
search_base = "ou=People,o=University of Virginia,c=US"
|
2020-05-25 20:00:36 +00:00
|
|
|
attributes = ['uid', 'cn', 'sn', 'displayName', 'givenName', 'mail', 'objectClass', 'UvaDisplayDepartment',
|
2020-04-20 19:16:33 +00:00
|
|
|
'telephoneNumber', 'title', 'uvaPersonIAMAffiliation', 'uvaPersonSponsoredType']
|
2020-05-19 20:11:43 +00:00
|
|
|
uid_search_string = "(&(objectclass=person)(uid=%s))"
|
2020-05-29 19:17:51 +00:00
|
|
|
user_or_last_name_search = "(&(objectclass=person)(|(uid=%s*)(sn=%s*)))"
|
|
|
|
cn_single_search = '(&(objectclass=person)(cn=%s*))'
|
|
|
|
cn_double_search = '(&(objectclass=person)(&(cn=%s*)(cn=*%s*)))'
|
2020-06-05 21:49:55 +00:00
|
|
|
temp_cache = {}
|
2020-06-04 18:59:36 +00:00
|
|
|
conn = None
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def __get_conn():
|
|
|
|
if not LdapService.conn:
|
2020-06-17 01:43:20 +00:00
|
|
|
if app.config['TESTING'] or app.config['LDAP_URL'] == 'mock':
|
2020-06-04 18:59:36 +00:00
|
|
|
server = Server('my_fake_server')
|
|
|
|
conn = Connection(server, client_strategy=MOCK_SYNC)
|
|
|
|
file_path = os.path.abspath(os.path.join(app.root_path, '..', 'tests', 'data', 'ldap_response.json'))
|
|
|
|
conn.strategy.entries_from_json(file_path)
|
2020-06-04 19:38:45 +00:00
|
|
|
conn.bind()
|
2020-06-04 18:59:36 +00:00
|
|
|
else:
|
|
|
|
server = Server(app.config['LDAP_URL'], connect_timeout=app.config['LDAP_TIMEOUT_SEC'])
|
2020-06-04 19:38:45 +00:00
|
|
|
conn = Connection(server, auto_bind=True,
|
2020-06-04 18:59:36 +00:00
|
|
|
receive_timeout=app.config['LDAP_TIMEOUT_SEC'],
|
2020-06-04 22:03:59 +00:00
|
|
|
client_strategy=RESTARTABLE)
|
2020-06-04 18:59:36 +00:00
|
|
|
LdapService.conn = conn
|
|
|
|
return LdapService.conn
|
2020-04-20 19:16:33 +00:00
|
|
|
|
|
|
|
|
2020-06-04 18:59:36 +00:00
|
|
|
@staticmethod
|
|
|
|
def user_info(uva_uid):
|
2020-06-02 22:17:00 +00:00
|
|
|
user_info = db.session.query(LdapModel).filter(LdapModel.uid == uva_uid).first()
|
|
|
|
if not user_info:
|
2020-06-05 21:49:55 +00:00
|
|
|
app.logger.info("No cache for " + uva_uid)
|
2020-06-02 22:17:00 +00:00
|
|
|
search_string = LdapService.uid_search_string % uva_uid
|
2020-06-04 18:59:36 +00:00
|
|
|
conn = LdapService.__get_conn()
|
|
|
|
conn.search(LdapService.search_base, search_string, attributes=LdapService.attributes)
|
|
|
|
if len(conn.entries) < 1:
|
2020-06-02 22:17:00 +00:00
|
|
|
raise ApiError("missing_ldap_record", "Unable to locate a user with id %s in LDAP" % uva_uid)
|
2020-06-04 18:59:36 +00:00
|
|
|
entry = conn.entries[0]
|
2020-06-02 22:17:00 +00:00
|
|
|
user_info = LdapModel.from_entry(entry)
|
|
|
|
db.session.add(user_info)
|
2020-06-05 21:49:55 +00:00
|
|
|
db.session.commit()
|
2020-06-02 22:17:00 +00:00
|
|
|
return user_info
|
2020-05-19 20:11:43 +00:00
|
|
|
|
2020-06-04 18:59:36 +00:00
|
|
|
@staticmethod
|
|
|
|
def search_users(query, limit):
|
2020-05-29 19:17:51 +00:00
|
|
|
if len(query.strip()) < 3:
|
|
|
|
return []
|
|
|
|
elif query.endswith(' '):
|
|
|
|
search_string = LdapService.cn_single_search % (query.strip())
|
|
|
|
elif query.strip().count(',') == 1:
|
|
|
|
f, l = query.split(",")
|
|
|
|
search_string = LdapService.cn_double_search % (l.strip(), f.strip())
|
|
|
|
elif query.strip().count(' ') == 1:
|
|
|
|
f,l = query.split(" ")
|
|
|
|
search_string = LdapService.cn_double_search % (f, l)
|
|
|
|
else:
|
|
|
|
# Search by user_id or last name
|
|
|
|
search_string = LdapService.user_or_last_name_search % (query, query)
|
2020-05-19 20:11:43 +00:00
|
|
|
results = []
|
2020-06-05 02:37:28 +00:00
|
|
|
app.logger.info(search_string)
|
2020-05-29 19:17:51 +00:00
|
|
|
try:
|
2020-06-04 18:59:36 +00:00
|
|
|
conn = LdapService.__get_conn()
|
|
|
|
conn.search(LdapService.search_base, search_string, attributes=LdapService.attributes)
|
2020-05-29 19:17:51 +00:00
|
|
|
# Entries are returned as a generator, accessing entries
|
|
|
|
# can make subsequent calls to the ldap service, so limit
|
|
|
|
# those here.
|
|
|
|
count = 0
|
2020-06-04 18:59:36 +00:00
|
|
|
for entry in conn.entries:
|
2020-05-29 19:17:51 +00:00
|
|
|
if count > limit:
|
|
|
|
break
|
2020-06-02 22:17:00 +00:00
|
|
|
results.append(LdapSchema().dump(LdapModel.from_entry(entry)))
|
2020-05-29 19:17:51 +00:00
|
|
|
count += 1
|
|
|
|
except LDAPExceptionError as le:
|
|
|
|
app.logger.info("Failed to execute ldap search. %s", str(le))
|
|
|
|
|
2020-05-19 20:11:43 +00:00
|
|
|
return results
|