ldaptool-0.6

-----BEGIN PGP SIGNATURE-----
 
 iQJYBAABCgBCFiEEcdms641aWv8vJSXMIcx4mUG+20gFAmRdCeokHHN0ZWZhbi5i
 dWVobGVyQHRpay51bmktc3R1dHRnYXJ0LmRlAAoJECHMeJlBvttI1UgP/25ZthYC
 I063KZ+r/ZjF2JpF8sWBRNpnL6spP2NyeTOF9SxGW/caD/Cc0wIVpweBVynevCx3
 vo7x0WS0jcIDKZ7eUwvzM+bN6qdoxlb3Z1SHnz/eDcg8qTHgroZ3e77UGm6hlHkh
 mOWcj2+fJ0PXdb0+gxnF8djeqATioq9Ijql/QpGg4FR7fo5uZhXbefa+n1h5qFDp
 a9KCn9E1hyoZWFv/5z33dhooFkS78jRtUco+ZDsuav9N6zRqKH78MXxv3NpynnIT
 dCXzSBST3v/Ki5G64aU0fDQctqE3j5oEAnasoVTp/yRwJfIWnaFf6reN/CzCC7C3
 8/N4vDEQ8+aOtRt61R48tvAwgzqtW4hppLyv55DdU4Y4HFOm5E39bnPUqQ2VDwww
 56lQPZ1xTiPnunUIYfw9qTBMOjb2DyW3Blx7Q2Rm63rVhLZac0b6E8iwpIkyMWy8
 8pGm7O8a754QSKpngBy55rWZMuBmBh3wPI29txaZyvCSqiLA7xIdf82DXQqOyLRs
 fMwy/U3Si+HeWuD7ON/D3rWn7OaWHKh6WR2FY8NWUT/aIyZSMVtRp6k+WqIeoxH0
 KExJ8YEQ6XGNUhInWF8//gWvjbLXIEs8sCFim4KClFA8ejAWehbB0eF0gCuar3tA
 b8XdMOGoxPcymvI+h/ivroQLWO297DJQ28BM
 =Spnc
 -----END PGP SIGNATURE-----

Merge tag 'ldaptool-0.6' into debian

ldaptool-0.6
This commit is contained in:
Stefan Bühler 2023-05-11 17:29:58 +02:00
commit ba6cc242f1
3 changed files with 28 additions and 24 deletions

View File

@ -9,7 +9,7 @@ CLI tool to query LDAP/AD servers
* Integration with password managers
* Various output formats
* Classic LDIF
* JSON stream (with detailed or simplified attribute values)
* JSON stream (with simplified or detailed attribute values)
* CSV
* Markdown table with stretched columns (for viewing in CLI/for monospaces fonts); requires csvlook from [csvkit](https://csvkit.readthedocs.io/)
* HTML

View File

@ -55,13 +55,17 @@ class Arguments(search.Arguments):
help="Sorted table output - defaults to markdown --table unless --csv is given",
),
)
full_json: bool = dataclasses.field(
default=False,
metadata=argclasses.arg(
help="Use full json output (dn as str, attributes as list of dicts containing various represenatations)",
),
)
json: bool = dataclasses.field(
default=False,
metadata=argclasses.arg(help="Use full json output"),
)
human: bool = dataclasses.field(
default=False,
metadata=argclasses.arg(help="Use simple json output (join multiple values of one attribute)"),
metadata=argclasses.arg(
help="Use simple json output (dn as str, attributes map to list of human-readable strings)",
),
)
def __post_init__(self) -> None:
@ -85,15 +89,15 @@ class Arguments(search.Arguments):
if self.table_output:
if not self.columns:
raise SystemExit("Table output requires attributes")
if self.json:
if self.full_json:
raise SystemExit("Can't use both table output and --json")
if self.human:
if self.json:
raise SystemExit("Can't use both table output and --human")
if self.raw:
if self.table_output:
raise SystemExit("Table output requires decode; --raw not allowed")
if self.json or self.human:
if self.full_json or self.json:
raise SystemExit("Decode options require decode; --raw not allowed")
@ -183,7 +187,7 @@ class _Context:
num_responses = 0
num_entries = 0
ldif_output = not (self.arguments.json or self.arguments.human)
ldif_output = not (self.arguments.full_json or self.arguments.json)
if ldif_output:
print("# extended LDIF")
@ -214,11 +218,11 @@ class _Context:
num_entries += 1
if ldif_output:
decoder.read_and_emit_ldif(dn=dn, entry=entry, file=stream)
elif self.arguments.human:
decoder.read_and_emit_human(dn=dn, entry=entry, file=stream)
elif self.arguments.json:
decoder.read_and_emit_simple_json(dn=dn, entry=entry, file=stream)
else:
assert self.arguments.json
decoder.read_and_emit_json(dn=dn, entry=entry, file=stream)
assert self.arguments.full_json
decoder.read_and_emit_full_json(dn=dn, entry=entry, file=stream)
except SizeLimitExceeded as e:
raise SystemExit(f"Error: {e}")

View File

@ -101,7 +101,7 @@ class Attribute:
return
def _try_decode(self, args: Arguments) -> None:
if self.name in ("objectSid","securityIdentifier"):
if self.name in ("objectSid", "securityIdentifier"):
self._try_decode_sid()
elif self.name in ("msExchMailboxGuid", "objectGUID"):
self._try_decode_uuid()
@ -192,30 +192,30 @@ class Decoder:
def human(self, *, dn: str, obj: TDecoded) -> dict[str, str]:
emit: dict[str, typing.Any] = dict(dn=dn)
for name, attrs in obj.items():
emit[name] = self.arguments.human_separator.join(attr.human() for attr in attrs)
emit[name] = [attr.human() for attr in attrs]
return emit
def emit_human(self, *, dn: str, obj: TDecoded, file: typing.IO[str] = sys.stdout) -> None:
def emit_simple_json(self, *, dn: str, obj: TDecoded, file: typing.IO[str] = sys.stdout) -> None:
emit = self.human(dn=dn, obj=obj)
json.dump(emit, file, ensure_ascii=False)
print(file=file) # terminate output dicts by newline
def read_and_emit_human(self, *, dn: str, entry: TEntry, file: typing.IO[str] = sys.stdout) -> None:
self.emit_human(dn=dn, obj=self.read(dn=dn, entry=entry), file=file)
def read_and_emit_simple_json(self, *, dn: str, entry: TEntry, file: typing.IO[str] = sys.stdout) -> None:
self.emit_simple_json(dn=dn, obj=self.read(dn=dn, entry=entry), file=file)
def json(self, *, dn: str, obj: TDecoded) -> dict[str, str]:
def full_json(self, *, dn: str, obj: TDecoded) -> dict[str, str]:
emit: dict[str, typing.Any] = dict(dn=dn)
for name, attrs in obj.items():
emit[name] = [attr.to_json() for attr in attrs]
return emit
def emit_json(self, *, dn: str, obj: TDecoded, file: typing.IO[str] = sys.stdout) -> None:
emit = self.json(dn=dn, obj=obj)
def emit_full_json(self, *, dn: str, obj: TDecoded, file: typing.IO[str] = sys.stdout) -> None:
emit = self.full_json(dn=dn, obj=obj)
json.dump(emit, file, ensure_ascii=False)
print(file=file) # terminate output dicts by newline
def read_and_emit_json(self, *, dn: str, entry: TEntry, file: typing.IO[str] = sys.stdout) -> None:
self.emit_json(dn=dn, obj=self.read(dn=dn, entry=entry), file=file)
def read_and_emit_full_json(self, *, dn: str, entry: TEntry, file: typing.IO[str] = sys.stdout) -> None:
self.emit_full_json(dn=dn, obj=self.read(dn=dn, entry=entry), file=file)
def emit_ldif(self, *, dn: str, obj: TDecoded, file: typing.IO[str] = sys.stdout) -> None:
print(f"dn: {dn}", file=file)