diff --git a/README.md b/README.md index 363d553..a99f561 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/ldaptool/_main.py b/src/ldaptool/_main.py index ac8d0b6..3cbbb0c 100644 --- a/src/ldaptool/_main.py +++ b/src/ldaptool/_main.py @@ -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}") diff --git a/src/ldaptool/decode/_decoder.py b/src/ldaptool/decode/_decoder.py index 793431a..236cf39 100644 --- a/src/ldaptool/decode/_decoder.py +++ b/src/ldaptool/decode/_decoder.py @@ -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)