msad 0.4.0__tar.gz → 0.4.1__tar.gz
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-0.4.0 → msad-0.4.1}/PKG-INFO +15 -22
- {msad-0.4.0 → msad-0.4.1}/README.md +13 -20
- msad-0.4.1/build.sh +1 -0
- {msad-0.4.0 → msad-0.4.1}/pyproject.toml +2 -2
- {msad-0.4.0 → msad-0.4.1}/src/msad/user.py +11 -27
- {msad-0.4.0 → msad-0.4.1}/.gitignore +0 -0
- {msad-0.4.0 → msad-0.4.1}/LICENSE +0 -0
- {msad-0.4.0 → msad-0.4.1}/a +0 -0
- {msad-0.4.0 → msad-0.4.1}/requirements.txt +0 -0
- {msad-0.4.0 → msad-0.4.1}/src/msad/__init__.py +0 -0
- {msad-0.4.0 → msad-0.4.1}/src/msad/__main__.py +0 -0
- {msad-0.4.0 → msad-0.4.1}/src/msad/ad.py +0 -0
- {msad-0.4.0 → msad-0.4.1}/src/msad/group.py +0 -0
- {msad-0.4.0 → msad-0.4.1}/src/msad/main.py +0 -0
- {msad-0.4.0 → msad-0.4.1}/src/msad/search.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: msad
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.1
|
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,7 +9,7 @@ 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.
|
12
|
+
Requires-Python: >=3.11
|
13
13
|
Requires-Dist: cryptography
|
14
14
|
Requires-Dist: gssapi
|
15
15
|
Requires-Dist: ldap3
|
@@ -24,21 +24,22 @@ It supports authentication with user/pwd and kerberos
|
|
24
24
|
|
25
25
|
It supports paginations: it can retreive more than 2000 objects (a limit of AD)
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
- search (
|
30
|
-
-
|
31
|
-
-
|
32
|
-
-
|
27
|
+
Features:
|
28
|
+
|
29
|
+
- [X] search objects (users, groups, computers,..)
|
30
|
+
- [X] search (recursively) group memberships and all user's groups
|
31
|
+
- [X] add/remove members to/from AD groups using DN or sAMaccoutName
|
32
|
+
- [X] change AD passwords
|
33
|
+
- [ ] check if a user is disabled or locked
|
33
34
|
|
34
35
|
## Prerequisites
|
35
36
|
|
36
|
-
python >= 3.
|
37
|
+
python >= 3.11
|
37
38
|
|
38
39
|
For kerboros auth
|
39
40
|
|
40
41
|
- krb5 lib and tools (like kinit, ...)
|
41
|
-
- a keytab file or
|
42
|
+
- a keytab file or krb5.conf configured
|
42
43
|
|
43
44
|
## Installation
|
44
45
|
|
@@ -46,7 +47,7 @@ For kerboros auth
|
|
46
47
|
pipx install msad
|
47
48
|
```
|
48
49
|
|
49
|
-
##
|
50
|
+
## Configuration
|
50
51
|
|
51
52
|
Create a configuration file in $HOME/.msad.toml as suggested by
|
52
53
|
|
@@ -54,7 +55,6 @@ Create a configuration file in $HOME/.msad.toml as suggested by
|
|
54
55
|
msad get-sample-config
|
55
56
|
```
|
56
57
|
|
57
|
-
|
58
58
|
## Usage
|
59
59
|
|
60
60
|
|
@@ -65,19 +65,16 @@ python -m msad --help
|
|
65
65
|
|
66
66
|
```
|
67
67
|
|
68
|
-
|
69
68
|
For kerberos authentication, first you need to login to AD / get a ticket kerberos with
|
70
69
|
|
71
70
|
```bash
|
72
|
-
kinit
|
71
|
+
kinit # or kinit myaduser
|
73
72
|
```
|
74
73
|
|
75
|
-
|
76
|
-
|
77
74
|
```text
|
78
|
-
msad search "(samaccountname=matteo)" --out-format=json
|
75
|
+
msad search "(samaccountname=matteo)" --out-format=json # show all attributes
|
79
76
|
|
80
|
-
msad search "(cn=redaelli*)" --
|
77
|
+
msad search "(cn=redaelli*)" --attributes mail --attributes samaccountname --out-format=json
|
81
78
|
|
82
79
|
msad group-members qlik_analyzer_users --nested
|
83
80
|
|
@@ -89,10 +86,6 @@ msad user-groups matteo --nested
|
|
89
86
|
|
90
87
|
```
|
91
88
|
|
92
|
-
## Sample
|
93
|
-
|
94
|
-
|
95
|
-
|
96
89
|
## License
|
97
90
|
|
98
91
|
Copyright © 2021 - 2025 Matteo Redaelli
|
@@ -6,21 +6,22 @@ It supports authentication with user/pwd and kerberos
|
|
6
6
|
|
7
7
|
It supports paginations: it can retreive more than 2000 objects (a limit of AD)
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
- search (
|
12
|
-
-
|
13
|
-
-
|
14
|
-
-
|
9
|
+
Features:
|
10
|
+
|
11
|
+
- [X] search objects (users, groups, computers,..)
|
12
|
+
- [X] search (recursively) group memberships and all user's groups
|
13
|
+
- [X] add/remove members to/from AD groups using DN or sAMaccoutName
|
14
|
+
- [X] change AD passwords
|
15
|
+
- [ ] check if a user is disabled or locked
|
15
16
|
|
16
17
|
## Prerequisites
|
17
18
|
|
18
|
-
python >= 3.
|
19
|
+
python >= 3.11
|
19
20
|
|
20
21
|
For kerboros auth
|
21
22
|
|
22
23
|
- krb5 lib and tools (like kinit, ...)
|
23
|
-
- a keytab file or
|
24
|
+
- a keytab file or krb5.conf configured
|
24
25
|
|
25
26
|
## Installation
|
26
27
|
|
@@ -28,7 +29,7 @@ For kerboros auth
|
|
28
29
|
pipx install msad
|
29
30
|
```
|
30
31
|
|
31
|
-
##
|
32
|
+
## Configuration
|
32
33
|
|
33
34
|
Create a configuration file in $HOME/.msad.toml as suggested by
|
34
35
|
|
@@ -36,7 +37,6 @@ Create a configuration file in $HOME/.msad.toml as suggested by
|
|
36
37
|
msad get-sample-config
|
37
38
|
```
|
38
39
|
|
39
|
-
|
40
40
|
## Usage
|
41
41
|
|
42
42
|
|
@@ -47,19 +47,16 @@ python -m msad --help
|
|
47
47
|
|
48
48
|
```
|
49
49
|
|
50
|
-
|
51
50
|
For kerberos authentication, first you need to login to AD / get a ticket kerberos with
|
52
51
|
|
53
52
|
```bash
|
54
|
-
kinit
|
53
|
+
kinit # or kinit myaduser
|
55
54
|
```
|
56
55
|
|
57
|
-
|
58
|
-
|
59
56
|
```text
|
60
|
-
msad search "(samaccountname=matteo)" --out-format=json
|
57
|
+
msad search "(samaccountname=matteo)" --out-format=json # show all attributes
|
61
58
|
|
62
|
-
msad search "(cn=redaelli*)" --
|
59
|
+
msad search "(cn=redaelli*)" --attributes mail --attributes samaccountname --out-format=json
|
63
60
|
|
64
61
|
msad group-members qlik_analyzer_users --nested
|
65
62
|
|
@@ -71,10 +68,6 @@ msad user-groups matteo --nested
|
|
71
68
|
|
72
69
|
```
|
73
70
|
|
74
|
-
## Sample
|
75
|
-
|
76
|
-
|
77
|
-
|
78
71
|
## License
|
79
72
|
|
80
73
|
Copyright © 2021 - 2025 Matteo Redaelli
|
msad-0.4.1/build.sh
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
python3 -m build && python3 -m twine upload dist/*
|
@@ -1,12 +1,12 @@
|
|
1
1
|
[project]
|
2
2
|
name = "msad"
|
3
|
-
version = "0.4.
|
3
|
+
version = "0.4.1"
|
4
4
|
authors = [
|
5
5
|
{ name="Matteo Redaelli", email="matteo.redaelli@gmail.com" },
|
6
6
|
]
|
7
7
|
description = "msad is a commandline for interacting with Active Directory"
|
8
8
|
readme = "README.md"
|
9
|
-
requires-python = ">=3.
|
9
|
+
requires-python = ">=3.11"
|
10
10
|
|
11
11
|
dependencies = [
|
12
12
|
"ldap3",
|
@@ -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=
|
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,
|
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)
|
File without changes
|
File without changes
|
{msad-0.4.0 → msad-0.4.1}/a
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|