1"""
2This module contains routines used for the salt mine
3"""
4
5
6import logging
7
8import salt.utils.data
9
10log = logging.getLogger(__name__)
11
12MINE_ITEM_ACL_ID = "__saltmine_acl__"
13MINE_ITEM_ACL_VERSION = 1
14MINE_ITEM_ACL_DATA = "__data__"
15
16
17def minion_side_acl_denied(minion_acl_cache, mine_minion, mine_function, req_minion):
18    """
19    Helper function to determine if a ``req_minion`` is not allowed to retrieve
20    ``mine_function``-data from the mine of ``mine_minion``.
21
22    :param dict minion_acl_cache: Contains minion_id as first level key, and mine
23        function as 2nd level key. Value of 2nd level is a list of minions that
24        are allowed to retrieve the function from the mine of the minion.
25    :param str mine_minion: The minion that the mine value originated from.
26    :param str mine_function: The mine function that is requested.
27    :param str req_minion: The minion that is requesting the mine data.
28
29    :rtype: bool
30    :return:
31        False if no ACL has been defined for ``mine_minion``, ``mine_function``.
32        False if an ACL has been defined and it grants access.
33        True if an ACL has been defined and does not grant access.
34    """
35    minion_acl_entry = minion_acl_cache.get(mine_minion, {}).get(mine_function, [])
36    ret = minion_acl_entry and req_minion not in minion_acl_entry
37    if ret:
38        log.debug(
39            "Salt mine request from %s for function %s on minion %s denied.",
40            req_minion,
41            mine_function,
42            mine_minion,
43        )
44    return ret
45
46
47def wrap_acl_structure(function_data, allow_tgt=None, allow_tgt_type=None):
48    """
49    Helper function to convert an non-ACL mine entry into the new entry which
50    includes ACL data.
51
52    :param dict function_data: The function data to wrap.
53    :param str allow_tgt: The targeting string that designates which minions can
54        request this mine entry.
55    :param str allow_tgt_type: The type of targeting string.
56        .. seealso:: :ref:`targeting`
57
58    :rtype: dict
59    :return: Mine entry structured to include ACL data.
60    """
61    res = {
62        MINE_ITEM_ACL_DATA: function_data,
63        MINE_ITEM_ACL_ID: MINE_ITEM_ACL_VERSION,
64    }
65    # Add minion-side ACL
66    if allow_tgt:
67        res.update(
68            salt.utils.data.filter_falsey(
69                {"allow_tgt": allow_tgt, "allow_tgt_type": allow_tgt_type}
70            )
71        )
72    return res
73
74
75def parse_function_definition(function_definition):
76    """
77    Helper function to parse the mine_function definition as provided in config,
78    or pillar.
79
80    :param function_definition: The function definition to parse.
81    :type function_definition: list or dict
82
83    :rtype: tuple
84    :return: Tuple with function_name, function_args, function_kwargs, minion_acl (dict)
85    """
86    function_name = None
87    function_args = []
88    function_kwargs = {}
89    minion_acl = {}
90    if isinstance(function_definition, dict):
91        # dictionary format for specifying mine function
92        function_name = function_definition.pop("mine_function", None)
93        function_kwargs = function_definition
94    elif isinstance(function_definition, list):
95        for item in function_definition:
96            if isinstance(item, dict):
97                # if len(item) > 1: # Multiple kwargs in a single list item
98                function_kwargs.update(item)
99            else:
100                function_args.append(item)
101        function_name = function_kwargs.pop("mine_function", None)
102
103    minion_acl = salt.utils.data.filter_falsey(
104        {
105            "allow_tgt": function_kwargs.pop("allow_tgt", None),
106            "allow_tgt_type": function_kwargs.pop("allow_tgt_type", None),
107        }
108    )
109
110    return (function_name, function_args, function_kwargs, minion_acl)
111