Skip to content

Commit 5b43f8f

Browse files
committed
Merge remote-tracking branch 'saltstack/3006.x' into merge-forward
2 parents 7052405 + 0e6e971 commit 5b43f8f

File tree

1 file changed

+83
-69
lines changed

1 file changed

+83
-69
lines changed

salt/modules/mac_assistive.py

Lines changed: 83 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import hashlib
1212
import logging
1313
import sqlite3
14+
import time
1415

1516
import salt.utils.platform
1617
import salt.utils.stringutils
@@ -36,7 +37,7 @@ def __virtual__():
3637
return __virtualname__
3738

3839

39-
def install(app_id, enable=True):
40+
def install(app_id, enable=True, tries=3, wait=10):
4041
"""
4142
Install a bundle ID or command as being allowed to use
4243
assistive access.
@@ -47,20 +48,36 @@ def install(app_id, enable=True):
4748
enabled
4849
Sets enabled or disabled status. Default is ``True``.
4950
51+
tries
52+
How many times to try and write to a read-only tcc. Default is ``True``.
53+
54+
wait
55+
Number of seconds to wait between tries. Default is ``10``.
56+
5057
CLI Example:
5158
5259
.. code-block:: bash
5360
5461
salt '*' assistive.install /usr/bin/osascript
5562
salt '*' assistive.install com.smileonmymac.textexpander
5663
"""
57-
with TccDB() as db:
58-
try:
59-
return db.install(app_id, enable=enable)
60-
except sqlite3.Error as exc:
61-
raise CommandExecutionError(
62-
"Error installing app({}): {}".format(app_id, exc)
63-
)
64+
num_tries = 1
65+
while True:
66+
with TccDB() as db:
67+
try:
68+
return db.install(app_id, enable=enable)
69+
except sqlite3.Error as exc:
70+
if "attempt to write a readonly database" not in str(exc):
71+
raise CommandExecutionError(
72+
"Error installing app({}): {}".format(app_id, exc)
73+
)
74+
elif num_tries < tries:
75+
time.sleep(wait)
76+
num_tries += 1
77+
else:
78+
raise CommandExecutionError(
79+
"Error installing app({}): {}".format(app_id, exc)
80+
)
6481

6582

6683
def installed(app_id):
@@ -175,22 +192,21 @@ def __init__(self, path=None):
175192
def _check_table_digest(self):
176193
# This logic comes from https://github.com/jacobsalmela/tccutil which is
177194
# Licensed under GPL-2.0
178-
with self.connection as conn:
179-
cursor = conn.execute(
180-
"SELECT sql FROM sqlite_master WHERE name='access' and type='table'"
181-
)
182-
for row in cursor.fetchall():
183-
digest = hashlib.sha1(row["sql"].encode()).hexdigest()[:10]
184-
if digest in ("ecc443615f", "80a4bb6912"):
185-
# Mojave and Catalina
186-
self.ge_mojave_and_catalina = True
187-
elif digest in ("3d1c2a0e97", "cef70648de"):
188-
# BigSur and later
189-
self.ge_bigsur_and_later = True
190-
else:
191-
raise CommandExecutionError(
192-
"TCC Database structure unknown for digest '{}'".format(digest)
193-
)
195+
cursor = self.connection.execute(
196+
"SELECT sql FROM sqlite_master WHERE name='access' and type='table'"
197+
)
198+
for row in cursor.fetchall():
199+
digest = hashlib.sha1(row["sql"].encode()).hexdigest()[:10]
200+
if digest in ("ecc443615f", "80a4bb6912"):
201+
# Mojave and Catalina
202+
self.ge_mojave_and_catalina = True
203+
elif digest in ("3d1c2a0e97", "cef70648de"):
204+
# BigSur and later
205+
self.ge_bigsur_and_later = True
206+
else:
207+
raise CommandExecutionError(
208+
"TCC Database structure unknown for digest '{}'".format(digest)
209+
)
194210

195211
def _get_client_type(self, app_id):
196212
if app_id[0] == "/":
@@ -200,14 +216,13 @@ def _get_client_type(self, app_id):
200216
return 0
201217

202218
def installed(self, app_id):
203-
with self.connection as conn:
204-
cursor = conn.execute(
205-
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
206-
(app_id,),
207-
)
208-
for row in cursor.fetchall():
209-
if row:
210-
return True
219+
cursor = self.connection.execute(
220+
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
221+
(app_id,),
222+
)
223+
for row in cursor.fetchall():
224+
if row:
225+
return True
211226
return False
212227

213228
def install(self, app_id, enable=True):
@@ -234,9 +249,8 @@ def install(self, app_id, enable=True):
234249
# indirect_object_identifier
235250
# ),
236251
# FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
237-
with self.connection as conn:
238-
conn.execute(
239-
"""
252+
self.connection.execute(
253+
"""
240254
INSERT or REPLACE INTO access VALUES (
241255
'kTCCServiceAccessibility',
242256
?,
@@ -253,8 +267,9 @@ def install(self, app_id, enable=True):
253267
0
254268
)
255269
""",
256-
(app_id, client_type, auth_value),
257-
)
270+
(app_id, client_type, auth_value),
271+
)
272+
self.connection.commit()
258273
elif self.ge_mojave_and_catalina:
259274
# CREATE TABLE IF NOT EXISTS "access" (
260275
# service TEXT NOT NULL,
@@ -276,9 +291,8 @@ def install(self, app_id, enable=True):
276291
# indirect_object_identifier
277292
# ),
278293
# FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
279-
with self.connection as conn:
280-
conn.execute(
281-
"""
294+
self.connection.execute(
295+
"""
282296
INSERT or REPLACE INTO access VALUES(
283297
'kTCCServiceAccessibility',
284298
?,
@@ -294,23 +308,23 @@ def install(self, app_id, enable=True):
294308
0
295309
)
296310
""",
297-
(app_id, client_type, auth_value),
298-
)
311+
(app_id, client_type, auth_value),
312+
)
313+
self.connection.commit()
299314
return True
300315

301316
def enabled(self, app_id):
302317
if self.ge_bigsur_and_later:
303318
column = "auth_value"
304319
elif self.ge_mojave_and_catalina:
305320
column = "allowed"
306-
with self.connection as conn:
307-
cursor = conn.execute(
308-
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
309-
(app_id,),
310-
)
311-
for row in cursor.fetchall():
312-
if row[column]:
313-
return True
321+
cursor = self.connection.execute(
322+
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
323+
(app_id,),
324+
)
325+
for row in cursor.fetchall():
326+
if row[column]:
327+
return True
314328
return False
315329

316330
def enable(self, app_id):
@@ -320,13 +334,13 @@ def enable(self, app_id):
320334
column = "auth_value"
321335
elif self.ge_mojave_and_catalina:
322336
column = "allowed"
323-
with self.connection as conn:
324-
conn.execute(
325-
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
326-
column
327-
),
328-
(1, app_id),
329-
)
337+
self.connection.execute(
338+
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
339+
column
340+
),
341+
(1, app_id),
342+
)
343+
self.connection.commit()
330344
return True
331345

332346
def disable(self, app_id):
@@ -336,23 +350,23 @@ def disable(self, app_id):
336350
column = "auth_value"
337351
elif self.ge_mojave_and_catalina:
338352
column = "allowed"
339-
with self.connection as conn:
340-
conn.execute(
341-
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
342-
column
343-
),
344-
(0, app_id),
345-
)
353+
self.connection.execute(
354+
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
355+
column
356+
),
357+
(0, app_id),
358+
)
359+
self.connection.commit()
346360
return True
347361

348362
def remove(self, app_id):
349363
if not self.installed(app_id):
350364
return False
351-
with self.connection as conn:
352-
conn.execute(
353-
"DELETE from access where client IS ? AND service IS 'kTCCServiceAccessibility'",
354-
(app_id,),
355-
)
365+
self.connection.execute(
366+
"DELETE from access where client IS ? AND service IS 'kTCCServiceAccessibility'",
367+
(app_id,),
368+
)
369+
self.connection.commit()
356370
return True
357371

358372
def __enter__(self):

0 commit comments

Comments
 (0)