python-win-ad 0.6.1__py3-none-any.whl → 0.6.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyad/__init__.py CHANGED
@@ -1,3 +1,7 @@
1
+ # package logger
2
+ import logging
3
+ logging.basicConfig(level=logging.WARNING)
4
+
1
5
  __all__ = [
2
6
  "set_defaults",
3
7
  "ADQuery",
pyad/adbase.py CHANGED
@@ -1,17 +1,20 @@
1
+ import logging
1
2
  import sys
2
3
  import win32com.client
3
4
 
4
5
  from .pyadexceptions import SetupError
5
6
 
7
+ logger = logging.getLogger(__name__)
6
8
  _adsi_provider = win32com.client.Dispatch("ADsNameSpaces")
7
9
 
8
10
  try:
9
11
  # Discover default domain and forest information
10
12
  __default_domain_obj = _adsi_provider.GetObject("", "LDAP://rootDSE")
11
13
  except:
12
- # If there was an error, this this computer might not be on a domain.
13
- print(
14
- "WARN: unable to connect to default domain. Computer is likely not attached to an AD domain"
14
+ # If there was an error, this computer might not be on a domain.
15
+ logger.info(
16
+ "Unable to connect to default domain. "
17
+ "Computer is likely not attached to an AD domain."
15
18
  )
16
19
  __default_domain_obj = None
17
20
  _default_detected_forest = None
pyad/adcontainer.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import pywintypes
2
2
 
3
+ from typing import Any, Dict, List
3
4
  from .adobject import ADObject
4
5
  from .aduser import ADUser
5
6
  from .adcomputer import ADComputer
@@ -9,7 +10,21 @@ from . import pyadutils
9
10
 
10
11
 
11
12
  class ADContainer(ADObject):
12
- def get_children_iter(self, recursive=False, filter=None):
13
+ def get_children_iter(
14
+ self, recursive: bool = False, filter: List[ADObject] = None
15
+ ) -> ADObject:
16
+ """
17
+ Iterate over the children objects in the container.
18
+
19
+ :param recursive: enumerate all containers with in the object,
20
+ defaults to False
21
+ :type recursive: bool, optional
22
+ :param filter: filter to only specific object classes ie ADUser,
23
+ defaults to None
24
+ :type filter: List[ADObject], optional
25
+ :yield: _description_
26
+ :rtype: _type_
27
+ """
13
28
  for com_object in self._ldap_adsi_obj:
14
29
  q = ADObject.from_com_object(com_object)
15
30
  q.adjust_pyad_type()
@@ -20,28 +35,59 @@ class ADContainer(ADObject):
20
35
  if not filter or q.__class__ in filter:
21
36
  yield q
22
37
 
23
- def get_children(self, recursive=False, filter=None) -> list:
38
+ def get_children(
39
+ self, recursive: bool = False, filter: List[ADObject] = None
40
+ ) -> list:
24
41
  """
25
- Iterate over the children objects in the container.
42
+ returns a list of child containers with in the current container. Optionally
43
+ the list can be filtered to specific object classes and search through child
44
+ containers recursively.
26
45
 
27
46
  :param recursive: include children from sub-containers, defaults to False
28
47
  :type recursive: bool, optional
29
48
  :param filter: filter to only specific object classes ie ADUser, defaults to None
30
- :type filter: object, optional
49
+ :type filter: List[ADObject], optional
31
50
  :return: a list of all child objects
32
51
  :rtype: list
33
52
  """
53
+
34
54
  return list(self.get_children_iter(recursive=recursive, filter=filter))
35
55
 
36
- def __create_object(self, type_, name):
56
+ def __create_object(self, type_: str, name: str) -> ADObject:
37
57
  prefix = "ou" if type_ == "organizationalUnit" else "cn"
38
58
  prefixed_name = "=".join((prefix, name))
39
59
  return self._ldap_adsi_obj.Create(type_, prefixed_name)
40
60
 
41
61
  def create_user(
42
- self, name, password=None, upn_suffix=None, enable=True, optional_attributes={}
43
- ):
44
- """Create a new user object in the container"""
62
+ self,
63
+ name: str,
64
+ password: str = None,
65
+ upn_suffix: str = None,
66
+ enable: bool = True,
67
+ optional_attributes: dict = {},
68
+ ) -> ADUser:
69
+ """
70
+ Create a new user object in the container.
71
+
72
+ :param name: The name of the user
73
+ :type name: str
74
+ :param password: The password for the user it is strongly recommended to
75
+ populate this parameter as the account will be created with password
76
+ not required which will not get clear within AD leading to a security
77
+ vulnerability.
78
+ :type password: str, optional
79
+ :param upn_suffix: The upn suffix for the user, defaults to default upn
80
+ suffix for the domain.
81
+ :type upn_suffix: str, optional
82
+ :param enable: Whether the user should be enabled or disabled, defaults to True
83
+ :type enable: bool, optional
84
+ :param optional_attributes: List of additional attribute to set, defaults to {}
85
+ :type optional_attributes: dict, optional
86
+ :return: the created user object
87
+ :rtype: ADUser
88
+ """
89
+
90
+ pyadobj = None
45
91
 
46
92
  try:
47
93
  if not upn_suffix:
@@ -52,21 +98,49 @@ class ADContainer(ADObject):
52
98
  obj.Put("userPrincipalName", upn)
53
99
  obj.SetInfo()
54
100
  pyadobj = ADUser.from_com_object(obj)
101
+
55
102
  if enable:
56
103
  pyadobj.enable()
104
+
57
105
  if password:
58
106
  pyadobj.set_password(password)
107
+ pyadobj.set_user_account_control_setting("PASSWD_NOTREQD", False)
108
+
59
109
  pyadobj.set_user_account_control_setting("NORMAL_ACCOUNT", True)
60
- pyadobj.set_user_account_control_setting("PASSWD_NOTREQD", False)
61
110
  pyadobj.update_attributes(optional_attributes)
62
111
  return pyadobj
63
112
  except pywintypes.com_error as e:
113
+ if pyadobj:
114
+ # clean up the object if it was created
115
+ pyadobj.delete()
64
116
  pyadutils.pass_up_com_exception(e)
65
117
 
66
118
  def create_group(
67
- self, name, security_enabled=True, scope="GLOBAL", optional_attributes={}
68
- ):
69
- """Create a new group object in the container"""
119
+ self,
120
+ name: str,
121
+ security_enabled: bool = True,
122
+ scope: str = "GLOBAL",
123
+ optional_attributes: Dict[str, Any] = {},
124
+ ) -> ADGroup:
125
+ """
126
+ Create a new group object in the container
127
+
128
+ :param name: The Group Name
129
+ :type name: str
130
+ :param security_enabled: If this is a security enabled group or a distribution
131
+ group, defaults to True
132
+ :type security_enabled: bool, optional
133
+ :param scope: The scope of group. Must be one of [GLOBAL, LOCAL, UNIVERSAL].
134
+ Defaults to "GLOBAL"
135
+ :type scope: str, optional
136
+ :param optional_attributes: Additional attributes to set when creating the
137
+ object, defaults to {}
138
+ :type optional_attributes: Dict[str,Any], optional
139
+ :return: The created group object
140
+ :rtype: ADGroup
141
+ """
142
+
143
+ obj = None
70
144
 
71
145
  try:
72
146
  obj = self.__create_object("group", name)
@@ -80,10 +154,30 @@ class ADContainer(ADObject):
80
154
  pyadobj.update_attributes(optional_attributes)
81
155
  return pyadobj
82
156
  except pywintypes.com_error as e:
157
+ if obj:
158
+ obj.delete()
83
159
  pyadutils.pass_up_com_exception(e)
160
+ except KeyError:
161
+ if obj:
162
+ obj.delete()
163
+ raise ValueError(f"Invalid scope: {scope}")
164
+
165
+ def create_container(
166
+ self, name: str, optional_attributes: Dict[str, Any] = {}
167
+ ) -> "ADContainer":
168
+ """
169
+ Create a new organizational unit in the container
170
+
171
+ :param name: The name of the container
172
+ :type name: str
173
+ :param optional_attributes: Additional attributes to set when creating the
174
+ object, defaults to {}
175
+ :type optional_attributes: Dict[str,Any], optional
176
+ :return: the created container object
177
+ :rtype: ADContainer
178
+ """
84
179
 
85
- def create_container(self, name, optional_attributes={}):
86
- """Create a new organizational unit in the container"""
180
+ obj = None
87
181
 
88
182
  try:
89
183
  obj = self.__create_object("organizationalUnit", name)
@@ -92,10 +186,29 @@ class ADContainer(ADObject):
92
186
  pyadobj.update_attributes(optional_attributes)
93
187
  return pyadobj
94
188
  except pywintypes.com_error as e:
189
+ if obj:
190
+ obj.delete()
95
191
  pyadutils.pass_up_com_exception(e)
96
192
 
97
- def create_computer(self, name, enable=True, optional_attributes={}):
98
- """Create a new computer object in the container"""
193
+ def create_computer(
194
+ self, name: str, enable: bool = True, optional_attributes: Dict[str, Any] = {}
195
+ ) -> ADComputer:
196
+ """
197
+ Create a new computer object in the container
198
+
199
+ :param name: The computer name
200
+ :type name: str
201
+ :param enable: Whether the computer should be enabled or disabled,
202
+ defaults to True
203
+ :type enable: bool, optional
204
+ :param optional_attributes: Additional attributes to set when creating the
205
+ object, defaults to {}
206
+ :type optional_attributes: Dict[str,Any], optional
207
+ :return: the created computer object
208
+ :rtype: ADComputer
209
+ """
210
+
211
+ obj = None
99
212
 
100
213
  try:
101
214
  obj = self.__create_object("computer", name)
@@ -111,9 +224,11 @@ class ADContainer(ADObject):
111
224
  pyadobj.update_attributes(optional_attributes)
112
225
  return pyadobj
113
226
  except pywintypes.com_error as e:
227
+ if obj:
228
+ obj.delete()
114
229
  pyadutils.pass_up_com_exception(e)
115
230
 
116
- def remove_child(self, child) -> None:
231
+ def remove_child(self, child: ADObject) -> None:
117
232
  """Removes the child object from the domain"""
118
233
 
119
234
  self._ldap_adsi_obj.Delete(child.type, child.prefixed_cn)
pyad/adobject.py CHANGED
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import win32com
2
3
  import pywintypes
3
4
  import time
@@ -16,6 +17,7 @@ from .pyadconstants import (
16
17
  ADS_USER_FLAG,
17
18
  )
18
19
 
20
+ logger = logging.getLogger(__name__)
19
21
 
20
22
  @total_ordering
21
23
  class ADObject(ADBase):
@@ -231,6 +233,37 @@ class ADObject(ADBase):
231
233
  else:
232
234
  raise AttributeError(attribute)
233
235
 
236
+ def __getitem__(self, key: str):
237
+ """
238
+ Get an attribute from the ADObject attributes.
239
+
240
+ :param key: The attribute name to retrieve
241
+ :type key: str
242
+ :raises AttributeError: if the key does not exist
243
+ :return: The value of the attribute
244
+ """
245
+
246
+ if hasattr(self._ldap_adsi_obj, key):
247
+ return self.get_attribute(key, False)
248
+ else:
249
+ raise AttributeError(key)
250
+
251
+ def __setitem__(self, key: str, value):
252
+ """
253
+ Set an attribute on the ADObject.
254
+
255
+ :param key: The attribute name to set
256
+ :type key: str
257
+ :param value: The value to set the attribute to
258
+ :type value: Any
259
+ :raises AttributeError: if the key does not exist
260
+ """
261
+
262
+ if hasattr(self._ldap_adsi_obj, key):
263
+ self.update_attribute(key, value)
264
+ else:
265
+ raise AttributeError(key)
266
+
234
267
  def _flush(self):
235
268
  """Commits any changes to the AD object."""
236
269
  self._ldap_adsi_obj.SetInfo()
@@ -633,7 +666,7 @@ class ADObject(ADBase):
633
666
  )
634
667
  node.appendChild(text)
635
668
  except:
636
- print("attribute: %s not xml-able" % attribute)
669
+ logger.error("attribute: %s not xml-able" % attribute)
637
670
  else:
638
671
  node.setAttribute("type", "multiValued")
639
672
  ok_elem = False
@@ -653,7 +686,7 @@ class ADObject(ADBase):
653
686
  node.appendChild(valnode)
654
687
  ok_elem = True
655
688
  except:
656
- print("attribute: %s not xml-able" % attribute)
689
+ logger.error("attribute: %s not xml-able" % attribute)
657
690
  if ok_elem:
658
691
  adobj_xml_doc.appendChild(node)
659
692
  return doc.toxml(encoding="UTF-8")
pyad/pyadexceptions.py CHANGED
@@ -46,14 +46,18 @@ class InvalidObjectException(noObjectFoundException, win32Exception):
46
46
  class InvalidAttribute(BaseException, AttributeError):
47
47
  def __str__(self):
48
48
  return (
49
- 'The attribute "%s" is not permitted by the schema definition of the object "%s" (the requested attribute does not exist).'
49
+ 'The attribute "%s" is not permitted by the schema definition of the '
50
+ 'object "%s" (the requested attribute does not exist).'
50
51
  % (self.attribute, self.obj)
51
52
  )
52
53
 
53
54
 
54
55
  class noExecutedQuery(BaseException):
55
56
  def __str__(self):
56
- return "No query has been executed. Therefore there are no results to return. Execute a query before requesting results."
57
+ return (
58
+ "No query has been executed. Therefore there are no results to return. "
59
+ "Execute a query before requesting results."
60
+ )
57
61
 
58
62
 
59
63
  class invalidResults(BaseException):
@@ -62,8 +66,8 @@ class invalidResults(BaseException):
62
66
 
63
67
  def __str__(self):
64
68
  return (
65
- "The specified query returned %i results. getSingleResults only functions with a single result."
66
- % self.number_results
69
+ "The specified query returned %i results. getSingleResults only functions "
70
+ "with a single result." % self.number_results
67
71
  )
68
72
 
69
73
 
pyad/pyadutils.py CHANGED
@@ -41,17 +41,34 @@ def validate_credentials(
41
41
  return None
42
42
 
43
43
 
44
- def convert_error_code(error_code):
44
+ def convert_error_code(error_code: int) -> int:
45
45
  """
46
46
  Convert error code from the format returned by pywin32 to the format that Microsoft
47
47
  documents everything in.
48
+
49
+ :param error_code: error code
50
+ :type error_code: int
51
+ :return: The error code in the format Microsoft documents it
52
+ :rtype: int
48
53
  """
49
54
 
50
55
  return error_code % 2**32
51
56
 
52
57
 
53
- # expects the actually pywintypes.com_error exception that's thrown...
54
- def interpret_com_exception(excp, additional_info={}):
58
+ def interpret_com_exception(
59
+ excp: "pywintype.com_error", additional_info: dict = {}
60
+ ) -> dict:
61
+ """
62
+ Convert a pywin32 com_error exception into a dictionary of error information.
63
+
64
+ :param excp: pywin32 com_error exception
65
+ :type excp: pywintype.com_error
66
+ :param additional_info: any additional information with the error, defaults to {}
67
+ :type additional_info: dict, optional
68
+ :return: a dictionary of error information
69
+ :rtype: dict
70
+ """
71
+
55
72
  d = {}
56
73
  d["error_num"] = convert_error_code(excp.args[2][5])
57
74
  # for some reason hex() includes the L for long in the hex...
@@ -87,7 +104,18 @@ def interpret_com_exception(excp, additional_info={}):
87
104
  return d
88
105
 
89
106
 
90
- def pass_up_com_exception(excp, additional_info={}):
107
+ def pass_up_com_exception(excp: "pywintype.com_error", additional_info: dict = {}):
108
+ """
109
+ reparse the com_error into a sane exception and raise it.
110
+
111
+ :param excp: the com_error exception
112
+ :type excp: pywintype.com_error
113
+ :param additional_info: Additional exception details, defaults to {}
114
+ :type additional_info: dict, optional
115
+ :raises excp: if we don't know how to handle the exception raise the original
116
+ exception
117
+ """
118
+
91
119
  if excp.__class__ in (genericADSIException, comException, win32Exception):
92
120
  raise excp
93
121
  else:
@@ -110,9 +138,14 @@ def pass_up_com_exception(excp, additional_info={}):
110
138
 
111
139
 
112
140
  def convert_datetime(adsi_time_com_obj):
113
- """Converts 64-bit integer COM object representing time into a python datetime object."""
114
- # credit goes to John Nielsen who documented this at
115
- # http://docs.activestate.com/activepython/2.6/pywin32/html/com/help/active_directory.html.
141
+ """
142
+ Converts 64-bit integer COM object representing time into
143
+ a python datetime object.
144
+
145
+ Credit goes to John Nielsen who documented this at
146
+ `<http://docs.activestate.com/activepython/2.6/pywin32/html/com/help/active_directory.html>`_.
147
+ """
148
+
116
149
  if not hasattr(adsi_time_com_obj, "highpart") or not hasattr(
117
150
  adsi_time_com_obj, "lowpart"
118
151
  ):
@@ -133,19 +166,38 @@ def convert_datetime(adsi_time_com_obj):
133
166
  return datetime.datetime.fromtimestamp(date_value)
134
167
 
135
168
 
136
- def convert_bigint(obj):
137
- # based on http://www.selfadsi.org/ads-attributes/user-usnChanged.htm
169
+ def convert_bigint(obj) -> int:
170
+ """
171
+ Converts a ADSI time object to an integer.
172
+
173
+ based on http://www.selfadsi.org/ads-attributes/user-usnChanged.htm
174
+
175
+ :param obj: the AD bigint object
176
+ :raises AttributeError: invalid object type
177
+ :return: the decimal value of the object
178
+ :rtype: int
179
+ """
180
+
138
181
  if hasattr(obj, "HighPart") and hasattr(obj, "LowPart"):
139
182
  h, l = obj.HighPart, obj.LowPart
140
183
  if l < 0:
141
184
  h += 1
142
185
  return (h << 32) + l
143
186
  else:
144
- raise ValueError(f"Expected adsi time object got '{obj.__class__.__name__}'")
187
+ raise AttributeError(
188
+ f"Expected adsi time object got '{obj.__class__.__name__}'"
189
+ )
190
+
145
191
 
192
+ def convert_timespan(obj) -> datetime.timedelta:
193
+ """
194
+ Converts COM object representing time span to a python time span object.
195
+
196
+ :param obj: ADSI time span object
197
+ :return: the python timedelta object
198
+ :rtype: datetime.timedelta
199
+ """
146
200
 
147
- def convert_timespan(obj):
148
- """Converts COM object representing time span to a python time span object."""
149
201
  as_seconds = (
150
202
  abs(convert_bigint(obj)) / 10000000
151
203
  ) # number of 100 nanoseconds in a second
@@ -160,7 +212,17 @@ def convert_sid(sid_object):
160
212
  return pywintypes.SID(bytes(sid_object))
161
213
 
162
214
 
163
- def generate_list(input):
215
+ def generate_list(input) -> list:
216
+ """
217
+ converts a set or tuple to a list or returns the input in a list if it is not
218
+ a list.
219
+
220
+ :param input: a list like object or any
221
+ :type input: list, set, tuple, Any
222
+ :return: a list
223
+ :rtype: list
224
+ """
225
+
164
226
  if type(input) is list:
165
227
  return input
166
228
  elif type(input) in (set, tuple):
@@ -171,7 +233,16 @@ def generate_list(input):
171
233
  ]
172
234
 
173
235
 
174
- def escape_path(path):
236
+ def escape_path(path: str) -> str:
237
+ """
238
+ escapes a path for use in ADSI.
239
+
240
+ :param path: the raw path to escape
241
+ :type path: str
242
+ :return: the escaped path
243
+ :rtype: str
244
+ """
245
+
175
246
  escapes = (
176
247
  ("\+", "+"),
177
248
  ("\*", "*"),
@@ -1,83 +1,82 @@
1
- Metadata-Version: 2.1
2
- Name: python-win-ad
3
- Version: 0.6.1
4
- Summary: An Object-Oriented Active Directory management framework built on ADSI
5
- Home-page: https://github.com/jcarswell/pyad/
6
- Author: Zakir Durumeric
7
- Author-email: zakird@gmail.com
8
- Maintainer: Josh Carswell
9
- Maintainer-email: Josh.Carswell@thecarswells.ca
10
- License: Apache License, Version 2.0
11
- Project-URL: Documentation, https://jcarswell.github.io/pyad/
12
- Project-URL: Issues, https://github.com/jcarswell/pyad/issues/
13
- Keywords: python microsoft windows active directory AD adsi
14
- Classifier: License :: OSI Approved :: Apache Software License
15
- Classifier: Intended Audience :: System Administrators
16
- Classifier: Natural Language :: English
17
- Classifier: Operating System :: Microsoft :: Windows
18
- Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP
19
- Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.6
21
- Classifier: Programming Language :: Python :: 3.7
22
- Classifier: Programming Language :: Python :: 3.8
23
- Classifier: Programming Language :: Python :: 3.9
24
- Classifier: Programming Language :: Python :: 3.10
25
- Obsoletes: pyad
26
- Requires-Python: >=3.6
27
- License-File: LICENSE
28
- Requires-Dist: setuptools
29
- Requires-Dist: pywin32
30
-
31
- .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
32
- :align: center
33
- :target: https://github.com/psf/black
34
- :alt: code style: black
35
-
36
- Introduction
37
- ------------
38
-
39
- pyad is a Python library designed to provide a simple, Pythonic interface to Active Directory
40
- through ADSI on the Windows platform. Complete documentation can be found at
41
- http://jcarswell.github.io/pyad/. Code is maintained at https://github.com/jcarswell/pyad. The
42
- library can be downloaded from PyPI at https://github.com/jcarswell/pyad.
43
-
44
- Breaking Changes from upstream
45
- ------------------------------
46
-
47
- ADObject:
48
-
49
- - _get_password_last_set - Act's like AD and returns 1970-01-01 if the date can't be parsed
50
- - get_last_login - Act's like AD and returns 1970-01-01 if the date can't be parsed
51
-
52
- Importing pyad directly exposes set_defaults, ADQuery, ADComputer, ADContainer, ADDomain,
53
- ADGroup, ADUser, from_cn, from_dn, from_guid. Importing pyad.pyad no longer imports
54
- the sub modules
55
-
56
- Most ADObject update methods now take flush as an optional argument that defaults to True
57
- to maintain compatibility with upstream code. For large updates it's recommended to set
58
- this to False until you are ready to write out the change, otherwise you may run into a
59
- back-off period in AD where all further changes will fail.
60
-
61
- Requirements
62
- ------------
63
-
64
- pyad requires pywin32, available at https://github.com/mhammond/pywin32.
65
-
66
-
67
- Testing
68
- -------
69
-
70
- To run unittest you will need to set the configuration to be specific to your environment.
71
- To do this you will need to edit config.py located in the tests folder.
72
-
73
-
74
- License
75
- -------
76
-
77
- pyad is licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy
78
- of the License at http://www.apache.org/licenses/LICENSE-2.0.
79
-
80
- Unless required by applicable law or agreed to in writing, software distributed under the
81
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
82
- either express or implied. See the License for the specific language governing permissions
83
- and limitations under the License.
1
+ Metadata-Version: 2.1
2
+ Name: python-win-ad
3
+ Version: 0.6.3
4
+ Summary: An Object-Oriented Active Directory management framework built on ADSI
5
+ Home-page: https://github.com/jcarswell/pyad/
6
+ Author: Zakir Durumeric
7
+ Author-email: zakird@gmail.com
8
+ Maintainer: Josh Carswell
9
+ Maintainer-email: Josh.Carswell@thecarswells.ca
10
+ License: Apache License, Version 2.0
11
+ Project-URL: Documentation, https://jcarswell.github.io/pyad/
12
+ Project-URL: Issues, https://github.com/jcarswell/pyad/issues/
13
+ Keywords: python microsoft windows active directory AD adsi
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.9
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Obsoletes: pyad
25
+ Requires-Python: >=3.6
26
+ License-File: LICENSE
27
+ Requires-Dist: setuptools
28
+ Requires-Dist: pywin32
29
+
30
+ .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
31
+ :align: center
32
+ :target: https://github.com/psf/black
33
+ :alt: code style: black
34
+
35
+ Introduction
36
+ ------------
37
+
38
+ pyad is a Python library designed to provide a simple, Pythonic interface to Active Directory
39
+ through ADSI on the Windows platform. Complete documentation can be found at
40
+ http://jcarswell.github.io/pyad/. Code is maintained at https://github.com/jcarswell/pyad. The
41
+ library can be downloaded from PyPI at https://github.com/jcarswell/pyad.
42
+
43
+ Breaking Changes from upstream
44
+ ------------------------------
45
+
46
+ ADObject:
47
+
48
+ - _get_password_last_set - Act's like AD and returns 1970-01-01 if the date can't be parsed
49
+ - get_last_login - Act's like AD and returns 1970-01-01 if the date can't be parsed
50
+
51
+ Importing pyad directly exposes set_defaults, ADQuery, ADComputer, ADContainer, ADDomain,
52
+ ADGroup, ADUser, from_cn, from_dn, from_guid. Importing pyad.pyad no longer imports
53
+ the sub modules
54
+
55
+ Most ADObject update methods now take flush as an optional argument that defaults to True
56
+ to maintain compatibility with upstream code. For large updates it's recommended to set
57
+ this to False until you are ready to write out the change, otherwise you may run into a
58
+ back-off period in AD where all further changes will fail.
59
+
60
+ Requirements
61
+ ------------
62
+
63
+ pyad requires pywin32, available at https://github.com/mhammond/pywin32.
64
+
65
+
66
+ Testing
67
+ -------
68
+
69
+ To run unittest you will need to set the configuration to be specific to your environment.
70
+ To do this you will need to edit config.py located in the tests folder.
71
+
72
+
73
+ License
74
+ -------
75
+
76
+ pyad is licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy
77
+ of the License at http://www.apache.org/licenses/LICENSE-2.0.
78
+
79
+ Unless required by applicable law or agreed to in writing, software distributed under the
80
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
81
+ either express or implied. See the License for the specific language governing permissions
82
+ and limitations under the License.
@@ -0,0 +1,19 @@
1
+ pyad/__init__.py,sha256=RlMEJyo2g6VwxlOhDJzfPfrUGw90SVFZThTN7Gmf5Nk,1771
2
+ pyad/adbase.py,sha256=hghiIpcQjJs0X9laqwbkShjlW21jiKIHJGBw3CDAMN4,3016
3
+ pyad/adcomputer.py,sha256=Wnsx-aD0ugOsgUyHx__6GWd2ogk3aiv4ARqhI_AoPZk,1109
4
+ pyad/adcontainer.py,sha256=dod_T6o7k-2cUmSVragtJnQrlaNwe7Mx5sNCexxbrjQ,8817
5
+ pyad/addomain.py,sha256=ppCUyONuNJAFVPxxCl4Z2o1-Lp0XesS3Wo1ao0YEoDg,850
6
+ pyad/adgroup.py,sha256=nevw3ZBAw58o_qYoXn7dN1bnOvIuwfsZMbZH6cF_dlM,11377
7
+ pyad/adobject.py,sha256=7_497WoGXjdvVGfm5SuszFXEH5rK4R6E7WFFdVM8Rc8,30791
8
+ pyad/adquery.py,sha256=tv0dKkBgcSnhU7Garv6jAEGiUJkwC256I3o2jUpCS7M,10820
9
+ pyad/adsearch.py,sha256=a24vVolTRINQpmuQ3Ldq_2hlHXl9r700vjilINqXk8M,2702
10
+ pyad/aduser.py,sha256=DK0ADC0qXLUodTW0cphvgg8-0iSewaoYw5rSTtje6Dg,3936
11
+ pyad/pyad.py,sha256=moPUvolc0vI-JIIIjW8otUclgkUov0fT1pym1vufdNs,1091
12
+ pyad/pyadconstants.py,sha256=wny29f6aAYhRosWp-lXp_2Rn8HCn3J0BI_G1XFMg5DA,6739
13
+ pyad/pyadexceptions.py,sha256=R8q5dSsqOgULpqVYoz2yvNi6lENZ9wg0PZQnp2Y1sWg,2149
14
+ pyad/pyadutils.py,sha256=0c3wTZEUCv3WE59SO3bzJYfhaO10HsCbQS1D3lo9X10,10486
15
+ python_win_ad-0.6.3.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
+ python_win_ad-0.6.3.dist-info/METADATA,sha256=Hv4j9QaJBQdPgxPcdNcRv6zYqYfCk2jDQxDt6nATpXo,3346
17
+ python_win_ad-0.6.3.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
18
+ python_win_ad-0.6.3.dist-info/top_level.txt,sha256=nRuluAlQ0ORm6J1nBi_n7OlMbbqTp7JDtl3g14wpSmI,5
19
+ python_win_ad-0.6.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.1)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,19 +0,0 @@
1
- pyad/__init__.py,sha256=y_NCgMefngD034sVrtAC--JMW1pXZ7_2Glwp9-4nqCw,1691
2
- pyad/adbase.py,sha256=cdFIadGftHJYZHH1_Byz6h1JbNgRihy4CqqH_Na1Ak4,2954
3
- pyad/adcomputer.py,sha256=Wnsx-aD0ugOsgUyHx__6GWd2ogk3aiv4ARqhI_AoPZk,1109
4
- pyad/adcontainer.py,sha256=KXQyQFYMggPt8lyYftZQit1WCnr4bdc5eBIq-y3IzDc,4867
5
- pyad/addomain.py,sha256=ppCUyONuNJAFVPxxCl4Z2o1-Lp0XesS3Wo1ao0YEoDg,850
6
- pyad/adgroup.py,sha256=nevw3ZBAw58o_qYoXn7dN1bnOvIuwfsZMbZH6cF_dlM,11377
7
- pyad/adobject.py,sha256=V1Vt4eCK5DY0bnI3m_-I_FJn_3geZ_5rrlklznLF0eg,29784
8
- pyad/adquery.py,sha256=tv0dKkBgcSnhU7Garv6jAEGiUJkwC256I3o2jUpCS7M,10820
9
- pyad/adsearch.py,sha256=a24vVolTRINQpmuQ3Ldq_2hlHXl9r700vjilINqXk8M,2702
10
- pyad/aduser.py,sha256=DK0ADC0qXLUodTW0cphvgg8-0iSewaoYw5rSTtje6Dg,3936
11
- pyad/pyad.py,sha256=moPUvolc0vI-JIIIjW8otUclgkUov0fT1pym1vufdNs,1091
12
- pyad/pyadconstants.py,sha256=wny29f6aAYhRosWp-lXp_2Rn8HCn3J0BI_G1XFMg5DA,6739
13
- pyad/pyadexceptions.py,sha256=9spQLlnQ1R0Pp9W93DoZTEM_aim406SA6g7SEAYJLdA,2088
14
- pyad/pyadutils.py,sha256=Hx0vYwjBZSuLAjlhu9dXgv4H5k68G-EKsYkWHNZ41JU,8695
15
- python_win_ad-0.6.1.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
- python_win_ad-0.6.1.dist-info/METADATA,sha256=eCN0uVdn-MO0JtYu2Uk-a1EKLJy5tmHrzfk2SRglo4M,3313
17
- python_win_ad-0.6.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
18
- python_win_ad-0.6.1.dist-info/top_level.txt,sha256=nRuluAlQ0ORm6J1nBi_n7OlMbbqTp7JDtl3g14wpSmI,5
19
- python_win_ad-0.6.1.dist-info/RECORD,,