Skip to content

Commit 1ec8a02

Browse files
ui: fixed exporting/importing rules
When exporting rules, use rfc3339 format for the Created field. We were exporting it as timestamp, which caused issues when importing the rules. Related: 58aa979 issue #1140 (cherry picked from commit 552aed5)
1 parent 7519db7 commit 1ec8a02

File tree

1 file changed

+62
-33
lines changed

1 file changed

+62
-33
lines changed

ui/opensnitch/rules.py

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@
77

88
import os
99
import json
10+
from slugify import slugify
1011
from datetime import datetime
1112
from google.protobuf.json_format import MessageToJson, Parse
1213

14+
DefaultRulesPath = "/etc/opensnitchd/rules"
15+
16+
# date format displayed on the GUI (created column)
17+
DBDateFieldFormat = "%Y-%m-%d %H:%M:%S"
18+
1319
class Rule():
1420
def __init__(self):
1521
pass
@@ -41,7 +47,7 @@ def new_from_records(records):
4147
created = int(datetime.now().timestamp())
4248
if records.value(RuleFields.Created) != "":
4349
created = int(datetime.strptime(
44-
records.value(RuleFields.Created), "%Y-%m-%d %H:%M:%S"
50+
records.value(RuleFields.Created), DBDateFieldFormat
4551
).timestamp())
4652
rule.created = created
4753

@@ -77,14 +83,14 @@ def add(self, time, node, name, description, enabled, precedence, nolog, action,
7783
def add_rules(self, addr, rules):
7884
try:
7985
for _,r in enumerate(rules):
80-
self.add(datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
86+
self.add(datetime.now().strftime(DBDateFieldFormat),
8187
addr,
8288
r.name, r.description, str(r.enabled),
8389
str(r.precedence), str(r.nolog), r.action, r.duration,
8490
r.operator.type,
8591
str(r.operator.sensitive),
8692
r.operator.operand, r.operator.data,
87-
str(datetime.fromtimestamp(r.created).strftime("%Y-%m-%d %H:%M:%S")))
93+
str(datetime.fromtimestamp(r.created).strftime(DBDateFieldFormat)))
8894

8995
return True
9096
except Exception as e:
@@ -135,6 +141,12 @@ def update_time(self, time, name, addr):
135141
action_on_conflict="OR REPLACE"
136142
)
137143

144+
def _timestamp_to_rfc3339(self, time):
145+
"""converts timestamp to rfc3339 format"""
146+
return "{0}Z".format(
147+
datetime.fromtimestamp(time).isoformat(timespec='microseconds')
148+
)
149+
138150
def rule_to_json(self, node, rule_name):
139151
try:
140152
records = self._db.get_rule(rule_name, node)
@@ -146,16 +158,33 @@ def rule_to_json(self, node, rule_name):
146158
# exclude this field when exporting to json
147159
tempRule = MessageToJson(rule)
148160
jRule = json.loads(tempRule)
149-
jRule['created'] = "{0}Z".format(
150-
datetime.fromtimestamp(rule.created).isoformat(timespec='microseconds')
151-
)
161+
jRule['created'] = self._timestamp_to_rfc3339(rule.created)
152162
return json.dumps(jRule, indent=" ")
153163
except Exception as e:
154164
print("rule_to_json() exception:", e)
155165
return None
156166

167+
def _export_rule_common(self, node, records, outdir):
168+
try:
169+
rule = Rule.new_from_records(records)
170+
rulename = rule.name
171+
if ".json" not in rulename:
172+
rulename = rulename + ".json"
173+
with open(outdir + "/" + rulename, 'w') as jsfile:
174+
actual_json_text = MessageToJson(rule)
175+
jRule = json.loads(actual_json_text)
176+
jRule['created'] = self._timestamp_to_rfc3339(rule.created)
177+
actual_json_text = json.dumps(jRule, indent=" ")
178+
jsfile.write( actual_json_text )
179+
180+
return True
181+
except Exception as e:
182+
print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e)
183+
184+
return False
185+
157186
def export_rule(self, node, rule_name, outdir):
158-
"""Gets the the rule from the DB and writes it out to a directory.
187+
"""Gets the rule from the DB and writes it out to a directory.
159188
A new directory per node will be created.
160189
"""
161190
try:
@@ -164,51 +193,38 @@ def export_rule(self, node, rule_name, outdir):
164193
print("export_rule() get_error 2:", records)
165194
return False
166195

167-
rule = Rule.new_from_records(records)
168-
rulesdir = outdir + "/" + node
196+
rulesdir = outdir + "/" + slugify(node)
169197
try:
170198
os.makedirs(rulesdir, 0o700)
171199
except Exception as e:
172200
print("exception creating dirs:", e)
173-
rulename = rule.name
174-
if ".json" not in rulename:
175-
rulename = rulename + ".json"
176-
with open(rulesdir + "/" + rulename, 'w') as jsfile:
177-
actual_json_text = MessageToJson(rule)
178-
jsfile.write( actual_json_text )
179201

180-
return True
202+
return self._export_rule_common(node, records, rulesdir)
203+
181204
except Exception as e:
182-
print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e)
205+
print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e)
183206

184207
return False
185208

186209
def export_rules(self, node, outdir):
187-
"""Gets the the rules from the DB and writes them out to a directory.
210+
"""Gets the rules from the DB and writes them out to a directory.
188211
A new directory per node will be created.
189212
"""
190213
records = self._db.get_rules(node)
191214
if records == None:
192215
return False
193216

217+
rulesdir = outdir + "/" + slugify(node)
218+
try:
219+
os.makedirs(rulesdir, 0o700)
220+
except Exception as e:
221+
print("exception creating dirs:", e)
194222
try:
195223
while records.next() != False:
196-
rule = Rule.new_from_records(records)
197-
198-
rulesdir = outdir + "/" + node
199-
try:
200-
os.makedirs(rulesdir, 0o700)
201-
except:
202-
pass
203-
rulename = rule.name
204-
if ".json" not in rulename:
205-
rulename = rulename + ".json"
206-
with open(rulesdir + "/" + rulename, 'w') as jsfile:
207-
actual_json_text = MessageToJson(rule)
208-
jsfile.write( actual_json_text )
224+
self._export_rule_common(node, records, rulesdir)
209225

210226
except Exception as e:
211-
print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e)
227+
print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e)
212228
return False
213229

214230
return True
@@ -222,7 +238,20 @@ def import_rules(self, rulesdir):
222238
for rulename in os.listdir(rulesdir):
223239
with open(rulesdir + "/" + rulename, 'r') as f:
224240
jsrule = f.read()
225-
pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True)
241+
# up until v1.6.5/v1.7.0, 'created' field was exported as timestamp.
242+
# since > v1.6.5 it's exported in rfc3339 format, so if we fail to
243+
# parse the rule, we'll try to convert the 'created' value from
244+
# timestamp to rfc3339.
245+
try:
246+
pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True)
247+
except:
248+
jRule = json.loads(jsrule)
249+
created = int(datetime.strptime(
250+
jRule['created'], "%Y-%m-%dT%H:%M:%S.%fZ"
251+
).timestamp())
252+
jRule['created'] = created
253+
jsrule = json.dumps(jRule)
254+
pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True)
226255
rules.append(pb_rule)
227256

228257
return rules

0 commit comments

Comments
 (0)