mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge remote-tracking branch 'saltstack/3006.x' into merge-forward
This commit is contained in:
commit
7018b14a7c
2 changed files with 86 additions and 70 deletions
|
@ -11,6 +11,7 @@ This module allows you to manage assistive access on macOS minions with 10.9+
|
|||
import hashlib
|
||||
import logging
|
||||
import sqlite3
|
||||
import time
|
||||
|
||||
import salt.utils.platform
|
||||
import salt.utils.stringutils
|
||||
|
@ -36,7 +37,7 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def install(app_id, enable=True):
|
||||
def install(app_id, enable=True, tries=3, wait=10):
|
||||
"""
|
||||
Install a bundle ID or command as being allowed to use
|
||||
assistive access.
|
||||
|
@ -47,6 +48,12 @@ def install(app_id, enable=True):
|
|||
enabled
|
||||
Sets enabled or disabled status. Default is ``True``.
|
||||
|
||||
tries
|
||||
How many times to try and write to a read-only tcc. Default is ``True``.
|
||||
|
||||
wait
|
||||
Number of seconds to wait between tries. Default is ``10``.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -54,13 +61,23 @@ def install(app_id, enable=True):
|
|||
salt '*' assistive.install /usr/bin/osascript
|
||||
salt '*' assistive.install com.smileonmymac.textexpander
|
||||
"""
|
||||
with TccDB() as db:
|
||||
try:
|
||||
return db.install(app_id, enable=enable)
|
||||
except sqlite3.Error as exc:
|
||||
raise CommandExecutionError(
|
||||
"Error installing app({}): {}".format(app_id, exc)
|
||||
)
|
||||
num_tries = 1
|
||||
while True:
|
||||
with TccDB() as db:
|
||||
try:
|
||||
return db.install(app_id, enable=enable)
|
||||
except sqlite3.Error as exc:
|
||||
if "attempt to write a readonly database" not in str(exc):
|
||||
raise CommandExecutionError(
|
||||
"Error installing app({}): {}".format(app_id, exc)
|
||||
)
|
||||
elif num_tries < tries:
|
||||
time.sleep(wait)
|
||||
num_tries += 1
|
||||
else:
|
||||
raise CommandExecutionError(
|
||||
"Error installing app({}): {}".format(app_id, exc)
|
||||
)
|
||||
|
||||
|
||||
def installed(app_id):
|
||||
|
@ -175,22 +192,21 @@ class TccDB:
|
|||
def _check_table_digest(self):
|
||||
# This logic comes from https://github.com/jacobsalmela/tccutil which is
|
||||
# Licensed under GPL-2.0
|
||||
with self.connection as conn:
|
||||
cursor = conn.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE name='access' and type='table'"
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
digest = hashlib.sha1(row["sql"].encode()).hexdigest()[:10]
|
||||
if digest in ("ecc443615f", "80a4bb6912"):
|
||||
# Mojave and Catalina
|
||||
self.ge_mojave_and_catalina = True
|
||||
elif digest in ("3d1c2a0e97", "cef70648de"):
|
||||
# BigSur and later
|
||||
self.ge_bigsur_and_later = True
|
||||
else:
|
||||
raise CommandExecutionError(
|
||||
"TCC Database structure unknown for digest '{}'".format(digest)
|
||||
)
|
||||
cursor = self.connection.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE name='access' and type='table'"
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
digest = hashlib.sha1(row["sql"].encode()).hexdigest()[:10]
|
||||
if digest in ("ecc443615f", "80a4bb6912"):
|
||||
# Mojave and Catalina
|
||||
self.ge_mojave_and_catalina = True
|
||||
elif digest in ("3d1c2a0e97", "cef70648de"):
|
||||
# BigSur and later
|
||||
self.ge_bigsur_and_later = True
|
||||
else:
|
||||
raise CommandExecutionError(
|
||||
"TCC Database structure unknown for digest '{}'".format(digest)
|
||||
)
|
||||
|
||||
def _get_client_type(self, app_id):
|
||||
if app_id[0] == "/":
|
||||
|
@ -200,14 +216,13 @@ class TccDB:
|
|||
return 0
|
||||
|
||||
def installed(self, app_id):
|
||||
with self.connection as conn:
|
||||
cursor = conn.execute(
|
||||
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
|
||||
(app_id,),
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
if row:
|
||||
return True
|
||||
cursor = self.connection.execute(
|
||||
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
|
||||
(app_id,),
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
if row:
|
||||
return True
|
||||
return False
|
||||
|
||||
def install(self, app_id, enable=True):
|
||||
|
@ -234,9 +249,8 @@ class TccDB:
|
|||
# indirect_object_identifier
|
||||
# ),
|
||||
# FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
|
||||
with self.connection as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
self.connection.execute(
|
||||
"""
|
||||
INSERT or REPLACE INTO access VALUES (
|
||||
'kTCCServiceAccessibility',
|
||||
?,
|
||||
|
@ -253,8 +267,9 @@ class TccDB:
|
|||
0
|
||||
)
|
||||
""",
|
||||
(app_id, client_type, auth_value),
|
||||
)
|
||||
(app_id, client_type, auth_value),
|
||||
)
|
||||
self.connection.commit()
|
||||
elif self.ge_mojave_and_catalina:
|
||||
# CREATE TABLE IF NOT EXISTS "access" (
|
||||
# service TEXT NOT NULL,
|
||||
|
@ -276,9 +291,8 @@ class TccDB:
|
|||
# indirect_object_identifier
|
||||
# ),
|
||||
# FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
|
||||
with self.connection as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
self.connection.execute(
|
||||
"""
|
||||
INSERT or REPLACE INTO access VALUES(
|
||||
'kTCCServiceAccessibility',
|
||||
?,
|
||||
|
@ -294,8 +308,9 @@ class TccDB:
|
|||
0
|
||||
)
|
||||
""",
|
||||
(app_id, client_type, auth_value),
|
||||
)
|
||||
(app_id, client_type, auth_value),
|
||||
)
|
||||
self.connection.commit()
|
||||
return True
|
||||
|
||||
def enabled(self, app_id):
|
||||
|
@ -303,14 +318,13 @@ class TccDB:
|
|||
column = "auth_value"
|
||||
elif self.ge_mojave_and_catalina:
|
||||
column = "allowed"
|
||||
with self.connection as conn:
|
||||
cursor = conn.execute(
|
||||
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
|
||||
(app_id,),
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
if row[column]:
|
||||
return True
|
||||
cursor = self.connection.execute(
|
||||
"SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'",
|
||||
(app_id,),
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
if row[column]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def enable(self, app_id):
|
||||
|
@ -320,13 +334,13 @@ class TccDB:
|
|||
column = "auth_value"
|
||||
elif self.ge_mojave_and_catalina:
|
||||
column = "allowed"
|
||||
with self.connection as conn:
|
||||
conn.execute(
|
||||
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
|
||||
column
|
||||
),
|
||||
(1, app_id),
|
||||
)
|
||||
self.connection.execute(
|
||||
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
|
||||
column
|
||||
),
|
||||
(1, app_id),
|
||||
)
|
||||
self.connection.commit()
|
||||
return True
|
||||
|
||||
def disable(self, app_id):
|
||||
|
@ -336,23 +350,23 @@ class TccDB:
|
|||
column = "auth_value"
|
||||
elif self.ge_mojave_and_catalina:
|
||||
column = "allowed"
|
||||
with self.connection as conn:
|
||||
conn.execute(
|
||||
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
|
||||
column
|
||||
),
|
||||
(0, app_id),
|
||||
)
|
||||
self.connection.execute(
|
||||
"UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'".format(
|
||||
column
|
||||
),
|
||||
(0, app_id),
|
||||
)
|
||||
self.connection.commit()
|
||||
return True
|
||||
|
||||
def remove(self, app_id):
|
||||
if not self.installed(app_id):
|
||||
return False
|
||||
with self.connection as conn:
|
||||
conn.execute(
|
||||
"DELETE from access where client IS ? AND service IS 'kTCCServiceAccessibility'",
|
||||
(app_id,),
|
||||
)
|
||||
self.connection.execute(
|
||||
"DELETE from access where client IS ? AND service IS 'kTCCServiceAccessibility'",
|
||||
(app_id,),
|
||||
)
|
||||
self.connection.commit()
|
||||
return True
|
||||
|
||||
def __enter__(self):
|
||||
|
|
|
@ -64,6 +64,7 @@ def test_pillar_timeout(salt_master_factory):
|
|||
"auto_accept": True,
|
||||
"worker_threads": 2,
|
||||
"peer": True,
|
||||
"minion_data_cache": False,
|
||||
}
|
||||
minion_overrides = {
|
||||
"auth_timeout": 20,
|
||||
|
@ -77,7 +78,7 @@ def test_pillar_timeout(salt_master_factory):
|
|||
- name: example
|
||||
- changes: True
|
||||
- result: True
|
||||
- comment: "Nothing has actually been changed"
|
||||
- comment: "Nothing has actually been changed {{ pillar['foo'] }}"
|
||||
"""
|
||||
master = salt_master_factory.salt_master_daemon(
|
||||
"pillar-timeout-master",
|
||||
|
@ -103,6 +104,7 @@ def test_pillar_timeout(salt_master_factory):
|
|||
sls_tempfile = master.state_tree.base.temp_file(f"{sls_name}.sls", sls_contents)
|
||||
with master.started(), minion1.started(), minion2.started(), minion3.started(), minion4.started(), sls_tempfile:
|
||||
proc = cli.run("state.sls", sls_name, minion_tgt="*")
|
||||
print(proc)
|
||||
# At least one minion should have a Pillar timeout
|
||||
assert proc.returncode == 1
|
||||
minion_timed_out = False
|
||||
|
|
Loading…
Add table
Reference in a new issue