Development of an internal social media platform with personalised dashboards for students
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.

checkGroupsMemberships.py 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. """
  2. """
  3. # Created on 2016.05.14
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2016 - 2018 Giovanni Cannata
  8. #
  9. # This file is part of ldap3.
  10. #
  11. # ldap3 is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU Lesser General Public License as published
  13. # by the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # ldap3 is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Lesser General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Lesser General Public License
  22. # along with ldap3 in the COPYING and COPYING.LESSER files.
  23. # If not, see <http://www.gnu.org/licenses/>.
  24. from .addMembersToGroups import edir_add_members_to_groups
  25. from ...core.exceptions import LDAPInvalidDnError
  26. from ... import SEQUENCE_TYPES, BASE, DEREF_NEVER
  27. from ...utils.dn import safe_dn
  28. def _check_members_have_memberships(connection,
  29. members_dn,
  30. groups_dn):
  31. """
  32. :param connection: a bound Connection object
  33. :param members_dn: the list of members to add to groups
  34. :param groups_dn: the list of groups where members are to be added
  35. :return: two booleans. The first when True means that all members have membership in all groups, The second when True means that
  36. there are inconsistences in the securityEquals attribute
  37. Checks user's group membership.
  38. Raises LDAPInvalidDNError if member is not found in the DIT.
  39. """
  40. if not isinstance(members_dn, SEQUENCE_TYPES):
  41. members_dn = [members_dn]
  42. if not isinstance(groups_dn, SEQUENCE_TYPES):
  43. groups_dn = [groups_dn]
  44. partial = False # True when a member has groupMembership but doesn't have securityEquals
  45. for member in members_dn:
  46. result = connection.search(member, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER, attributes=['groupMembership', 'securityEquals'])
  47. if not connection.strategy.sync:
  48. response, result = connection.get_response(result)
  49. else:
  50. response, result = connection.response, connection.result
  51. if not result['description'] == 'success': # member not found in DIT
  52. raise LDAPInvalidDnError(member + ' not found')
  53. existing_security_equals = response[0]['attributes']['securityEquals'] if 'securityEquals' in response[0]['attributes'] else []
  54. existing_group_membership = response[0]['attributes']['groupMembership'] if 'groupMembership' in response[0]['attributes'] else []
  55. existing_security_equals = [element.lower() for element in existing_security_equals]
  56. existing_group_membership = [element.lower() for element in existing_group_membership]
  57. for group in groups_dn:
  58. if group.lower() not in existing_group_membership:
  59. return False, False
  60. if group.lower() not in existing_security_equals:
  61. partial = True
  62. return True, partial
  63. def _check_groups_contain_members(connection,
  64. groups_dn,
  65. members_dn):
  66. """
  67. :param connection: a bound Connection object
  68. :param members_dn: the list of members to add to groups
  69. :param groups_dn: the list of groups where members are to be added
  70. :return: two booleans. The first when True means that all members have membership in all groups, The second when True means that
  71. there are inconsistences in the EquivalentToMe attribute
  72. Checks if groups have members in their 'member' attribute.
  73. Raises LDAPInvalidDNError if member is not found in the DIT.
  74. """
  75. if not isinstance(groups_dn, SEQUENCE_TYPES):
  76. groups_dn = [groups_dn]
  77. if not isinstance(members_dn, SEQUENCE_TYPES):
  78. members_dn = [members_dn]
  79. partial = False # True when a group has member but doesn't have equivalentToMe
  80. for group in groups_dn:
  81. result = connection.search(group, '(objectclass=*)', BASE, dereference_aliases=DEREF_NEVER, attributes=['member', 'equivalentToMe'])
  82. if not connection.strategy.sync:
  83. response, result = connection.get_response(result)
  84. else:
  85. response, result = connection.response, connection.result
  86. if not result['description'] == 'success':
  87. raise LDAPInvalidDnError(group + ' not found')
  88. existing_members = response[0]['attributes']['member'] if 'member' in response[0]['attributes'] else []
  89. existing_equivalent_to_me = response[0]['attributes']['equivalentToMe'] if 'equivalentToMe' in response[0]['attributes'] else []
  90. existing_members = [element.lower() for element in existing_members]
  91. existing_equivalent_to_me = [element.lower() for element in existing_equivalent_to_me]
  92. for member in members_dn:
  93. if member.lower() not in existing_members:
  94. return False, False
  95. if member.lower() not in existing_equivalent_to_me:
  96. partial = True
  97. return True, partial
  98. def edir_check_groups_memberships(connection,
  99. members_dn,
  100. groups_dn,
  101. fix,
  102. transaction):
  103. """
  104. :param connection: a bound Connection object
  105. :param members_dn: the list of members to check
  106. :param groups_dn: the list of groups to check
  107. :param fix: checks for inconsistences in the users-groups relation and fixes them
  108. :param transaction: activates an LDAP transaction when fixing
  109. :return: a boolean where True means that the operation was successful and False means an error has happened
  110. Checks and fixes users-groups relations following the eDirectory rules: groups are checked against 'groupMembership'
  111. attribute in the member object while members are checked against 'member' attribute in the group object.
  112. Raises LDAPInvalidDnError if members or groups are not found in the DIT.
  113. """
  114. if not isinstance(groups_dn, SEQUENCE_TYPES):
  115. groups_dn = [groups_dn]
  116. if not isinstance(members_dn, SEQUENCE_TYPES):
  117. members_dn = [members_dn]
  118. if connection.check_names: # builds new lists with sanitized dn
  119. safe_members_dn = []
  120. safe_groups_dn = []
  121. for member_dn in members_dn:
  122. safe_members_dn.append(safe_dn(member_dn))
  123. for group_dn in groups_dn:
  124. safe_groups_dn.append(safe_dn(group_dn))
  125. members_dn = safe_members_dn
  126. groups_dn = safe_groups_dn
  127. try:
  128. members_have_memberships, partial_member_security = _check_members_have_memberships(connection, members_dn, groups_dn)
  129. groups_contain_members, partial_group_security = _check_groups_contain_members(connection, groups_dn, members_dn)
  130. except LDAPInvalidDnError:
  131. return False
  132. if not members_have_memberships and not groups_contain_members:
  133. return False
  134. if fix: # fix any inconsistences
  135. if (members_have_memberships and not groups_contain_members) \
  136. or (groups_contain_members and not members_have_memberships) \
  137. or partial_group_security \
  138. or partial_member_security:
  139. for member in members_dn:
  140. for group in groups_dn:
  141. edir_add_members_to_groups(connection, member, group, True, transaction)
  142. return True