msad 0.4.0__py3-none-any.whl → 0.4.2__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.
msad/search.py CHANGED
@@ -49,7 +49,7 @@ def users(conn, search_base, string, limit, attributes=None):
49
49
  """Search users inside AD
50
50
  filter: is the cn or userPrincipalName or samaccoutnname or mail to be searched. Can contain *
51
51
  """
52
- search_filter = f"(&(objectclass=user)(|(samaccountname={string})(mail={string})(cn={string})(userPrincipalName={string})))"
52
+ search_filter = f"(&(objectclass=user)(|(samaccountname={string})(mail={string})(cn={string}*)(userPrincipalName={string}*)))"
53
53
  return search(conn, search_base, search_filter, limit=limit, attributes=attributes)
54
54
 
55
55
 
msad/user.py CHANGED
@@ -20,11 +20,11 @@ import logging
20
20
  import getpass
21
21
  import ldap3
22
22
  import datetime
23
- from .search import get_dn, search
24
- from .group import *
23
+ from .search import disabled_users, get_dn, search, locked_users, never_expires_password
24
+ from .group import group_member
25
25
 
26
26
 
27
- def _enter_password(text):
27
+ def _enter_password(text: str):
28
28
  try:
29
29
  p = getpass.getpass(text)
30
30
  except Exception as error:
@@ -34,7 +34,7 @@ def _enter_password(text):
34
34
  return p
35
35
 
36
36
 
37
- def change_password(conn, search_base, user):
37
+ def change_password(conn, search_base: str, user: str):
38
38
  user_dn = get_dn(conn, search_base, user)
39
39
 
40
40
  if not user_dn:
@@ -47,7 +47,7 @@ def change_password(conn, search_base, user):
47
47
  conn.extend.microsoft.modify_password(user_dn, newpwd, oldpwd)
48
48
 
49
49
 
50
- def is_disabled(conn, search_base, user):
50
+ def is_disabled(conn, search_base: str, user: str):
51
51
  result = disabled_users(
52
52
  conn, search_base, f"(samaccountname={user})", limit=1, attributes=None
53
53
  )
@@ -55,37 +55,21 @@ def is_disabled(conn, search_base, user):
55
55
  return True if len(result) == 1 else None
56
56
 
57
57
 
58
- def is_locked(conn, search_base, user):
58
+ def is_locked(conn, search_base: str, user: str):
59
59
  result = locked_users(
60
60
  conn, search_base, f"(samaccountname={user})", limit=1, attributes=None
61
61
  )
62
62
  return True if len(result) == 1 else None
63
63
 
64
64
 
65
- def has_never_expires_password(conn, search_base, user):
65
+ def has_never_expires_password(conn, search_base: str, user: str):
66
66
  result = never_expires_password(
67
67
  conn, search_base, f"(samaccountname={user})", limit=1, attributes=None
68
68
  )
69
69
  return True if len(result) == 1 else None
70
70
 
71
71
 
72
- def password_changed_in_days(conn, search_base, user, limit=1000):
73
- search_filter = f"(samaccountname={user})"
74
- result = search(
75
- conn, search_base, search_filter, limit=limit, attributes=["sAMAccountName","pwdLastSet"]
76
- )
77
-
78
- if len(result) == 0:
79
- return None
80
-
81
- now = datetime.datetime.now()
82
- result = [ {"sAMAccountName": u["sAMAccountName"],
83
- "days": (now - u["pwdLastSet"].replace(tzinfo=None)).days} for u in result]
84
-
85
- return result
86
-
87
-
88
- def has_expired_password(conn, search_base, user, max_age):
72
+ def password_changed_in_days(conn, search_base: str, user: str, limit: int = 2000, max_age: int):
89
73
  # return search(conn, search_base, search_filter, attributes=attributes)
90
74
  search_filter = f"(samaccountname={user})"
91
75
  result = search(
@@ -106,7 +90,7 @@ def has_expired_password(conn, search_base, user, max_age):
106
90
  return True if days > max_age else False
107
91
 
108
92
 
109
- def check_user(conn, search_base, user, max_age, groups=[]):
93
+ def check_user(conn, search_base:str, user:str, max_age:int, groups=[]):
110
94
  # result = {}
111
95
  yield ({"is_disabled": is_disabled(conn, search_base, user)})
112
96
  yield ({"is_locked": is_locked(conn, search_base, user)})
@@ -127,13 +111,13 @@ def check_user(conn, search_base, user, max_age, groups=[]):
127
111
  yield (
128
112
  {
129
113
  f"membership_{group}": group_member(
130
- conn, search_base, group_name=group, user_name=user
114
+ conn, search_base, group=group, user=user
131
115
  )
132
116
  }
133
117
  )
134
118
 
135
119
 
136
- def user_groups(conn, search_base, limit, user, nested=True):
120
+ def user_groups(conn, search_base:str , limit: int, user: str, nested: bool =True):
137
121
  """retrieve all groups (also nested) of a user"""
138
122
 
139
123
  user_dn = get_dn(conn, search_base, user)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: msad
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: msad is a commandline for interacting with Active Directory
5
5
  Project-URL: Homepage, https://github.com/matteoredaelli/msad
6
6
  Project-URL: Issues, https://github.com/matteoredaelli/msad/issues
@@ -9,11 +9,12 @@ License-Expression: GPL-3.0-or-later
9
9
  License-File: LICENSE
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
- Requires-Python: >=3.9
13
- Requires-Dist: cryptography
14
- Requires-Dist: gssapi
15
- Requires-Dist: ldap3
16
- Requires-Dist: typer
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: click==8.1.8
14
+ Requires-Dist: cryptography==44.0.2
15
+ Requires-Dist: gssapi==1.9.0
16
+ Requires-Dist: ldap3==2.9.1
17
+ Requires-Dist: typer==0.15.2
17
18
  Description-Content-Type: text/markdown
18
19
 
19
20
  # msAD
@@ -24,21 +25,22 @@ It supports authentication with user/pwd and kerberos
24
25
 
25
26
  It supports paginations: it can retreive more than 2000 objects (a limit of AD)
26
27
 
27
- It can be used for:
28
- - search objects (users, groups, computers,..)
29
- - search (recursively) group memberships and all user's groups
30
- - add/remove members to/from AD groups using DN or sAMaccoutName
31
- - change AD passwords
32
- - check if a user is disabled or locked, group membership
28
+ Features:
29
+
30
+ - [X] search objects (users, groups, computers,..)
31
+ - [X] search (recursively) group memberships and all user's groups
32
+ - [X] add/remove members to/from AD groups using DN or sAMaccoutName
33
+ - [X] change AD passwords
34
+ - [ ] check if a user is disabled or locked
33
35
 
34
36
  ## Prerequisites
35
37
 
36
- python >= 3.9
38
+ python >= 3.11
37
39
 
38
40
  For kerboros auth
39
41
 
40
42
  - krb5 lib and tools (like kinit, ...)
41
- - a keytab file or
43
+ - a keytab file or krb5.conf configured
42
44
 
43
45
  ## Installation
44
46
 
@@ -46,7 +48,7 @@ For kerboros auth
46
48
  pipx install msad
47
49
  ```
48
50
 
49
- ## COnfiguration
51
+ ## Configuration
50
52
 
51
53
  Create a configuration file in $HOME/.msad.toml as suggested by
52
54
 
@@ -54,7 +56,6 @@ Create a configuration file in $HOME/.msad.toml as suggested by
54
56
  msad get-sample-config
55
57
  ```
56
58
 
57
-
58
59
  ## Usage
59
60
 
60
61
 
@@ -65,19 +66,16 @@ python -m msad --help
65
66
 
66
67
  ```
67
68
 
68
-
69
69
  For kerberos authentication, first you need to login to AD / get a ticket kerberos with
70
70
 
71
71
  ```bash
72
- kinit youraduser
72
+ kinit # or kinit myaduser
73
73
  ```
74
74
 
75
-
76
-
77
75
  ```text
78
- msad search "(samaccountname=matteo)" --out-format=json
76
+ msad search "(samaccountname=matteo)" --out-format=json # show all attributes
79
77
 
80
- msad search "(cn=redaelli*)" --attribute mail --attribute samaccountname --out-format=json
78
+ msad search "(cn=redaelli*)" --attributes mail --attributes samaccountname --out-format=json
81
79
 
82
80
  msad group-members qlik_analyzer_users --nested
83
81
 
@@ -89,10 +87,6 @@ msad user-groups matteo --nested
89
87
 
90
88
  ```
91
89
 
92
- ## Sample
93
-
94
-
95
-
96
90
  ## License
97
91
 
98
92
  Copyright © 2021 - 2025 Matteo Redaelli
@@ -0,0 +1,12 @@
1
+ msad/__init__.py,sha256=DJ6egXlvzOK_k0XlN7BpmMRuwu4MM7eQoxLE0Ou-sLI,63
2
+ msad/__main__.py,sha256=RCZmmoCNOWC7rAfIDm_LaymsybXIzE6McYbUEEkf9P8,60
3
+ msad/ad.py,sha256=C3dknAgRY6Jnotk0RgPSye7uzNxUd-7B3oGloGR674E,5679
4
+ msad/group.py,sha256=3HNpXfYK4yXxDNsXn72JOZSMEwjj0kMiDvVBca1oVuI,2510
5
+ msad/main.py,sha256=5wBSPOZPkOnYfe-gow1YFt65FpawJooZ3eOKp_RFP0A,10573
6
+ msad/search.py,sha256=ADbUD8j_Tbh2XIWUYiWcgkg6pmKDciIL3DnCHf_Twzs,3478
7
+ msad/user.py,sha256=MWv-U8_FYP-dsuzQQjKP1T-IToP-kz4VBbX1W6QLliY,4241
8
+ msad-0.4.2.dist-info/METADATA,sha256=OJ8NAAF7BvPZJmOK4p6Rv31X-DQGLOipfuXs73pA-cI,2379
9
+ msad-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ msad-0.4.2.dist-info/entry_points.txt,sha256=UKSjeppC0YX2dfybmBfxLXCqF_HMZFfPX1MRw88rtpI,39
11
+ msad-0.4.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
12
+ msad-0.4.2.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- msad/__init__.py,sha256=DJ6egXlvzOK_k0XlN7BpmMRuwu4MM7eQoxLE0Ou-sLI,63
2
- msad/__main__.py,sha256=RCZmmoCNOWC7rAfIDm_LaymsybXIzE6McYbUEEkf9P8,60
3
- msad/ad.py,sha256=C3dknAgRY6Jnotk0RgPSye7uzNxUd-7B3oGloGR674E,5679
4
- msad/group.py,sha256=3HNpXfYK4yXxDNsXn72JOZSMEwjj0kMiDvVBca1oVuI,2510
5
- msad/main.py,sha256=5wBSPOZPkOnYfe-gow1YFt65FpawJooZ3eOKp_RFP0A,10573
6
- msad/search.py,sha256=j8PedOZVf7eFBG34ZfHoVEfEPbYE0nOtznKTLwu48Eg,3476
7
- msad/user.py,sha256=k9INOsuSekCB4w0EYYrcgPQChvOfawIzLYv7h37-DXM,4557
8
- msad-0.4.0.dist-info/METADATA,sha256=KL37POSU35W_FjShUlAU8Qf48EVUpGwPkCbWMUmQ5T0,2288
9
- msad-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- msad-0.4.0.dist-info/entry_points.txt,sha256=UKSjeppC0YX2dfybmBfxLXCqF_HMZFfPX1MRw88rtpI,39
11
- msad-0.4.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
12
- msad-0.4.0.dist-info/RECORD,,
File without changes