Skip to content

Commit 883f0c9

Browse files
net-user parser fixup
1 parent ffaf455 commit 883f0c9

File tree

2 files changed

+117
-61
lines changed

2 files changed

+117
-61
lines changed

jc/parsers/net_user.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
]
7575
}
7676
77-
$ net users /domain | jc --net-user -p | jq
77+
$ net users /domain | jc --net-user -p
7878
{
7979
"account_origin": "\\\\DESKTOP-WIN10-PRO.somecompany.corp",
8080
"domain": "somecompany.corp",
@@ -121,7 +121,7 @@
121121
]
122122
}
123123
124-
$ net users Administrator | jc --net-user -p | jq
124+
$ net users Administrator | jc --net-user -p
125125
{
126126
"domain": "",
127127
"user_accounts": [
@@ -179,12 +179,9 @@
179179
]
180180
}
181181
"""
182-
183-
184182
from datetime import datetime
185183
import re
186184
import jc.utils
187-
from jc.exceptions import ParseError
188185

189186

190187
class info():
@@ -193,7 +190,7 @@ class info():
193190
description = '`net user` command parser'
194191
author = 'joehacksalot'
195192
author_email = 'joehacksalot@gmail.com'
196-
compatible = ['windows']
193+
compatible = ['win32']
197194
magic_commands = ['net user']
198195
tags = ['command']
199196

@@ -220,14 +217,13 @@ def parse(data, raw=False, quiet=False):
220217

221218
raw_output = {}
222219
if jc.utils.has_data(data):
223-
try:
224-
raw_output = _parse(data)
225-
return raw_output if raw else _process(raw_output)
226-
except Exception as e:
227-
if not quiet:
228-
raise ParseError('Could not parse data due to unexpected format.')
229-
return {}
230-
220+
raw_output = _parse(data)
221+
222+
if not raw_output:
223+
raw_output = {}
224+
225+
return raw_output if raw else _process(raw_output)
226+
231227
def _set_if_not_none(output_dict, key, value):
232228
if value is not None:
233229
output_dict[key] = value
@@ -267,7 +263,11 @@ def _process(proc_data):
267263
_set_if_not_none(user_account, "password_required", _process_string_is_yes(user_account.get("password_required", None)))
268264
_set_if_not_none(user_account, "user_may_change_password", _process_string_is_yes(user_account.get("user_may_change_password", None)))
269265
_set_if_not_none(user_account, "last_logon", _process_date(user_account.get("last_logon", None)))
270-
return proc_data # No further processing is needed
266+
267+
if not proc_data:
268+
proc_data = {}
269+
270+
return proc_data
271271

272272

273273
class _PushbackIterator:
@@ -339,7 +339,7 @@ def _parse(data):
339339
"domain": "",
340340
"user_accounts": []
341341
}
342-
342+
343343
lines = data.splitlines()
344344
lines = [line.rstrip() for line in lines if line.strip() != ""]
345345

@@ -358,11 +358,11 @@ def _parse(data):
358358
elif "User name" in line:
359359
line_iter.pushback(line)
360360
user_account_keypairs = {}
361-
361+
362362
# Regular expression to match key-value pairs
363363
kv_pattern = re.compile(r'^([\w\s\/\'\-]{1,29})\s*(.+)?$')
364364
key = None
365-
365+
366366
while True:
367367
# Process each line
368368
# Break when done
@@ -371,7 +371,7 @@ def _parse(data):
371371
line = line.strip()
372372
if not line:
373373
continue # Skip empty lines
374-
374+
375375
match = kv_pattern.match(line)
376376
if "The command completed" in line:
377377
break
@@ -437,5 +437,5 @@ def _parse(data):
437437
break
438438
else:
439439
raise ValueError(f"Unexpected line: {line}")
440-
440+
441441
return result

tests/test_net_user.py

Lines changed: 97 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,112 @@
1-
import json
21
import os
32
import unittest
4-
import jc.parsers.ipconfig
5-
import jc.parsers.net_localgroup
6-
import jc.parsers.net_user
3+
import json
4+
from typing import Dict
5+
from jc.parsers.net_user import parse
76

87
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
98

109

1110
class MyTests(unittest.TestCase):
12-
test_files = [
13-
"tests/fixtures/windows/windows-xp/net_user",
14-
"tests/fixtures/windows/windows-xp/net_user.administrator",
15-
"tests/fixtures/windows/windows-7/net_user",
16-
"tests/fixtures/windows/windows-7/net_user.administrator",
17-
"tests/fixtures/windows/windows-2008/net_user",
18-
"tests/fixtures/windows/windows-2008/net_user.administrator",
19-
"tests/fixtures/windows/windows-2016/net_user.administrators",
20-
"tests/fixtures/windows/windows-2016/net_user",
21-
"tests/fixtures/windows/windows-10/net_user",
22-
"tests/fixtures/windows/windows-10/net_user.administrator",
23-
"tests/fixtures/windows/windows-11/net_user",
24-
"tests/fixtures/windows/windows-11/net_user.administrator"
25-
]
26-
27-
def setUp(self):
28-
for tf in MyTests.test_files:
29-
in_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.out")
30-
out_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.json")
31-
32-
with open(in_file, "r", encoding="utf-8") as f:
33-
setattr(self, self.varName(tf), f.read())
34-
with open(out_file, "r", encoding="utf-8") as f:
35-
setattr(self, self.varName(tf) + "_json", json.loads(f.read()))
36-
37-
def varName(self, path):
38-
return (
39-
path.replace("tests/fixtures/windows", "")
40-
.replace("-", "_")
41-
.replace("/", "_")
11+
f_in: Dict = {}
12+
f_json: Dict = {}
13+
14+
@classmethod
15+
def setUpClass(cls):
16+
fixtures = {
17+
'windows_xp_net_user': (
18+
'fixtures/windows/windows-xp/net_user.out',
19+
'fixtures/windows/windows-xp/net_user.json'),
20+
'windows_7_net_user': (
21+
'fixtures/windows/windows-7/net_user.out',
22+
'fixtures/windows/windows-7/net_user.json'),
23+
'windows_2008_net_user': (
24+
'fixtures/windows/windows-2008/net_user.out',
25+
'fixtures/windows/windows-2008/net_user.json'),
26+
'windows_2016_net_user': (
27+
'fixtures/windows/windows-2016/net_user.out',
28+
'fixtures/windows/windows-2016/net_user.json'),
29+
'windows_10_net_user': (
30+
'fixtures/windows/windows-10/net_user.out',
31+
'fixtures/windows/windows-10/net_user.json'),
32+
'windows_11_net_user': (
33+
'fixtures/windows/windows-11/net_user.out',
34+
'fixtures/windows/windows-11/net_user.json'),
35+
}
36+
37+
for file, filepaths in fixtures.items():
38+
with open(os.path.join(THIS_DIR, filepaths[0]), 'r', encoding='utf-8') as a, \
39+
open(os.path.join(THIS_DIR, filepaths[1]), 'r', encoding='utf-8') as b:
40+
cls.f_in[file] = a.read()
41+
cls.f_json[file] = json.loads(b.read())
42+
43+
44+
def test_net_user_nodata(self):
45+
"""
46+
Test 'net_user' with no data
47+
"""
48+
self.assertEqual(parse('', quiet=True), {})
49+
50+
51+
def test_net_user_windows_xp(self):
52+
"""
53+
Test 'net_user' on Windows XP
54+
"""
55+
self.assertEqual(
56+
parse(self.f_in['windows_xp_net_user'], quiet=True),
57+
self.f_json['windows_xp_net_user']
4258
)
4359

44-
def test_windows_net_localgroup(self):
60+
61+
def test_net_user_windows_7(self):
4562
"""
46-
Test a sample Windows "net localgroup" command output
63+
Test 'net_user' on Windows 7
4764
"""
48-
for tf in MyTests.test_files:
49-
in_var = getattr(self, self.varName(tf))
50-
out_var = getattr(self, self.varName(tf) + "_json")
65+
self.assertEqual(
66+
parse(self.f_in['windows_7_net_user'], quiet=True),
67+
self.f_json['windows_7_net_user']
68+
)
69+
5170

52-
self.assertEqual(jc.parsers.net_user.parse(in_var, quiet=True), out_var)
71+
def test_net_user_windows_2008(self):
72+
"""
73+
Test 'net_user' on Windows 2008
74+
"""
75+
self.assertEqual(
76+
parse(self.f_in['windows_2008_net_user'], quiet=True),
77+
self.f_json['windows_2008_net_user']
78+
)
79+
80+
81+
def test_net_user_windows_2016(self):
82+
"""
83+
Test 'net_user' on Windows 2016
84+
"""
85+
self.assertEqual(
86+
parse(self.f_in['windows_2016_net_user'], quiet=True),
87+
self.f_json['windows_2016_net_user']
88+
)
89+
90+
91+
def test_net_user_windows_10(self):
92+
"""
93+
Test 'net_user' on Windows 10
94+
"""
95+
self.assertEqual(
96+
parse(self.f_in['windows_10_net_user'], quiet=True),
97+
self.f_json['windows_10_net_user']
98+
)
99+
100+
101+
def test_net_user_windows_11(self):
102+
"""
103+
Test 'net_user' on Windows 11
104+
"""
105+
self.assertEqual(
106+
parse(self.f_in['windows_11_net_user'], quiet=True),
107+
self.f_json['windows_11_net_user']
108+
)
53109

54110

55-
if __name__ == "__main__":
111+
if __name__ == '__main__':
56112
unittest.main()

0 commit comments

Comments
 (0)