LILiK login and user managment web interface
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
5.6 KiB

  1. #!/bin/usr/env python3
  2. import ldap3
  3. import utils
  4. import config
  5. def connection_decorator(f):
  6. with ldap3.Connection(config.SERVER, auto_bind=True) as conn:
  7. def wrapped_f(*args, **kwargs):
  8. return f(args[0], conn, *args[1:], **kwargs)
  9. return wrapped_f
  10. def admin_connection_decorator(f):
  11. with ldap3.Connection(config.SERVER, user=config.ADMIN_CN, password=config.ADMIN_PASSWORD, auto_bind=True) as conn:
  12. def wrapped_f(*args, **kwargs):
  13. return f(args[0], conn, *args[1:], **kwargs)
  14. return wrapped_f
  15. class LILiK_USER(object):
  16. _attributes = {'uid': [], 'cn': ['givenName']}
  17. _flags = {'mail': 'accountActive'}
  18. _hosts = ['ltsp',
  19. 'users']
  20. _groups = ['admin',
  21. 'wiki',
  22. 'lilik.it',
  23. 'cloud',
  24. 'projects',
  25. 'teambox',
  26. 'im']
  27. _posix_groups = ['users_sites']
  28. def __init__(self, entry, posix_groups):
  29. results = {}
  30. for attribute in self._attributes.keys():
  31. self.__setattr__(attribute, str(entry.__getattr__(attribute)))
  32. services = {}
  33. for service, flag in self._flags.items():
  34. services[service] = flag in entry and bool(entry[flag])
  35. for host in self._hosts:
  36. services[host] = 'host' in entry and host in entry['host']
  37. for group in self._groups:
  38. services[group] = 'memberOf' in entry and utils.ldap_path("cn=%s"%utils.clean_value(group), config.GROUP, config.DOMAIN) in list(entry['memberOf'])
  39. for posix_group in self._posix_groups:
  40. services[posix_group] = posix_group in posix_groups and str(entry.uid) in list(posix_groups[posix_group])
  41. self.__setattr__('services', services)
  42. def to_json(self):
  43. return json.dumps(self, default=lambda o: o.__dict__,)
  44. def to_dict(self):
  45. return self.__dict__
  46. @admin_connection_decorator
  47. def update(self, conn, new_lilik_user):
  48. user_cn = utils.ldap_path('uid=%s'%self.uid, config.PEOPLE, config.DOMAIN)
  49. diff = utils.DictDiffer(new_lilik_user, self.__dict__)
  50. modifiers = {user_cn: {}}
  51. if 'userPassword' in diff.added():
  52. modifiers[user_cn]['userPassword'] = [(ldap3.MODIFY_REPLACE, [new_lilik_user['userPassword']])] #TODO add hash encryption?
  53. for changed in diff.changed():
  54. if changed == 'services':
  55. services_diff = utils.DictDiffer(self.__dict__[changed], new_lilik_user[changed])
  56. for service_changed in services_diff.changed():
  57. if service_changed in self._flags:
  58. flag = self._flags[service_changed]
  59. modifiers[user_cn][flag] = [(ldap3.MODIFY_REPLACE, [new_lilik_user['services'][service_changed]])]
  60. elif service_changed in self._hosts:
  61. action = ldap3.MODIFY_ADD if new_lilik_user['services'][service_changed] else ldap3.MODIFY_DELETE
  62. modifiers[user_cn]['host'] = [(action, [service_changed])]
  63. elif service_changed in self._groups:
  64. group_cn = utils.ldap_path('cn=%s'%service_changed, config.GROUP, config.DOMAIN)
  65. action = ldap3.MODIFY_ADD if new_lilik_user['services'][service_changed] else ldap3.MODIFY_DELETE
  66. modifiers[group_cn] = {'member': [(action, [user_cn])]}
  67. elif service_changed in self._posix_groups:
  68. group_cn = utils.ldap_path('cn=%s'%service_changed, config.GROUP, config.DOMAIN)
  69. action = ldap3.MODIFY_ADD if new_lilik_user['services'][service_changed] else ldap3.MODIFY_DELETE
  70. modifiers[group_cn] = {'memberUid': [(action, [self.uid])]}
  71. else:
  72. raise Exception('Unknown user attribute')
  73. else:
  74. for alias in self._attributes[changed]:
  75. modifiers[user_cn][alias] = [(ldap3.MODIFY_REPLACE, [new_lilik_user[changed]])]
  76. modifiers[user_cn][changed] = [(ldap3.MODIFY_REPLACE, [new_lilik_user[changed]])]
  77. for entry_cn, modifier in modifiers.items():
  78. if modifier:
  79. conn.modify(entry_cn, modifier)
  80. if conn.result['result'] != 0:
  81. return False
  82. return True
  83. class LILiK_LDAP(object):
  84. def login(self, user_name, password):
  85. bind_dn = utils.ldap_path('uid=%s'%utils.clean_user_name(user_name), config.PEOPLE, config.DOMAIN)
  86. c = ldap3.Connection(config.SERVER, user=bind_dn, password=password)
  87. return c.bind()
  88. @connection_decorator
  89. def get_users(self, conn):
  90. conn.search(utils.ldap_path(config.PEOPLE, config.DOMAIN), '(objectclass=posixAccount)', attributes=['uid'])
  91. return [str(a.uid) for a in conn.entries]
  92. @connection_decorator
  93. def get_user(self, conn, user_name):
  94. conn.search(utils.ldap_path(config.PEOPLE, config.DOMAIN), '(&(objectclass=posixAccount)(uid=%s))'%utils.clean_user_name(user_name), attributes=['*', 'memberOf'])
  95. # return clean_user_name(user_name)
  96. if len(conn.entries) == 0:
  97. return None
  98. entry = conn.entries[0]
  99. return LILiK_USER(entry, self.get_posix_groups())
  100. @connection_decorator
  101. def get_posix_groups(self, conn):
  102. conn.search(utils.ldap_path(config.GROUP, config.DOMAIN), '(objectclass=posixGroup)', attributes=['*'])
  103. # return clean_user_name(user_name)
  104. results = {}
  105. for group in conn.entries:
  106. results[str(group.cn)] = list(group.memberUid) if 'memberUid' in group else []
  107. return results