1"""
2Basic functions for accessing the SDB interface
3
4For configuration options, see the docs for specific sdb
5modules.
6"""
7
8import random
9
10import salt.loader
11from salt.exceptions import SaltInvocationError
12
13
14def sdb_get(uri, opts, utils=None, strict=False):
15    """
16    Get a value from a db, using a uri in the form of ``sdb://<profile>/<key>``. If
17    the uri provided is not valid, then it will be returned as-is, unless ``strict=True`` was passed.
18    """
19    if not isinstance(uri, str) or not uri.startswith("sdb://"):
20        if strict:
21            raise SaltInvocationError('SDB uri must start with "sdb://"')
22        else:
23            return uri
24
25    if utils is None:
26        utils = salt.loader.utils(opts)
27
28    sdlen = len("sdb://")
29    indx = uri.find("/", sdlen)
30
31    if (indx == -1) or not uri[(indx + 1) :]:
32        if strict:
33            raise SaltInvocationError(
34                "SDB uri must have a profile name as a first part of the uri before"
35                " the /"
36            )
37        else:
38            return uri
39
40    profile = opts.get(uri[sdlen:indx], {})
41    if not profile:
42        profile = opts.get("pillar", {}).get(uri[sdlen:indx], {})
43    if "driver" not in profile:
44        if strict:
45            raise SaltInvocationError(
46                'SDB profile "{}" wasnt found in the minion configuration'.format(
47                    uri[sdlen:indx]
48                )
49            )
50        else:
51            return uri
52
53    fun = "{}.get".format(profile["driver"])
54    query = uri[indx + 1 :]
55
56    loaded_db = salt.loader.sdb(opts, fun, utils=utils)
57    return loaded_db[fun](query, profile=profile)
58
59
60def sdb_set(uri, value, opts, utils=None):
61    """
62    Set a value in a db, using a uri in the form of ``sdb://<profile>/<key>``.
63    If the uri provided does not start with ``sdb://`` or the value is not
64    successfully set, return ``False``.
65    """
66    if not isinstance(uri, str) or not uri.startswith("sdb://"):
67        return False
68
69    if utils is None:
70        utils = salt.loader.utils(opts)
71
72    sdlen = len("sdb://")
73    indx = uri.find("/", sdlen)
74
75    if (indx == -1) or not uri[(indx + 1) :]:
76        return False
77
78    profile = opts.get(uri[sdlen:indx], {})
79    if not profile:
80        profile = opts.get("pillar", {}).get(uri[sdlen:indx], {})
81    if "driver" not in profile:
82        return False
83
84    fun = "{}.set".format(profile["driver"])
85    query = uri[indx + 1 :]
86
87    loaded_db = salt.loader.sdb(opts, fun, utils=utils)
88    return loaded_db[fun](query, value, profile=profile)
89
90
91def sdb_delete(uri, opts, utils=None):
92    """
93    Delete a value from a db, using a uri in the form of ``sdb://<profile>/<key>``. If
94    the uri provided does not start with ``sdb://`` or the value is not successfully
95    deleted, return ``False``.
96    """
97    if not isinstance(uri, str) or not uri.startswith("sdb://"):
98        return False
99
100    if utils is None:
101        utils = salt.loader.utils(opts)
102
103    sdlen = len("sdb://")
104    indx = uri.find("/", sdlen)
105
106    if (indx == -1) or not uri[(indx + 1) :]:
107        return False
108
109    profile = opts.get(uri[sdlen:indx], {})
110    if not profile:
111        profile = opts.get("pillar", {}).get(uri[sdlen:indx], {})
112    if "driver" not in profile:
113        return False
114
115    fun = "{}.delete".format(profile["driver"])
116    query = uri[indx + 1 :]
117
118    loaded_db = salt.loader.sdb(opts, fun, utils=utils)
119    return loaded_db[fun](query, profile=profile)
120
121
122def sdb_get_or_set_hash(
123    uri,
124    opts,
125    length=8,
126    chars="abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)",
127    utils=None,
128):
129    """
130    Check if value exists in sdb.  If it does, return, otherwise generate a
131    random string and store it.  This can be used for storing secrets in a
132    centralized place.
133    """
134    if not isinstance(uri, str) or not uri.startswith("sdb://"):
135        return False
136
137    if utils is None:
138        utils = salt.loader.utils(opts)
139
140    ret = sdb_get(uri, opts, utils=utils)
141
142    if ret is None:
143        val = "".join([random.SystemRandom().choice(chars) for _ in range(length)])
144        sdb_set(uri, val, opts, utils)
145
146    return ret or val
147