1    	import re
2    	from textwrap import dedent
3    	from unittest import TestCase
4    	
5    	from pcs.cli import nvset
6    	from pcs.common.pacemaker.nvset import (
7    	    CibNvpairDto,
8    	    CibNvsetDto,
9    	)
10   	from pcs.common.pacemaker.rule import CibRuleExpressionDto
11   	from pcs.common.types import (
12   	    CibRuleExpressionType,
13   	    CibRuleInEffectStatus,
14   	)
15   	
16   	
17   	def fixture_dto(in_effect):
18   	    return CibNvsetDto(
19   	        f"id-{in_effect}",
20   	        {"score": "150"},
21   	        CibRuleExpressionDto(
22   	            f"id-{in_effect}-rule",
23   	            CibRuleExpressionType.RULE,
24   	            in_effect,
25   	            {"boolean-op": "or"},
26   	            None,
27   	            None,
28   	            [
29   	                CibRuleExpressionDto(
30   	                    f"id-{in_effect}-rule-op",
31   	                    CibRuleExpressionType.OP_EXPRESSION,
32   	                    CibRuleInEffectStatus.UNKNOWN,
33   	                    {"name": "monitor"},
34   	                    None,
35   	                    None,
36   	                    [],
37   	                    "op monitor",
38   	                ),
39   	            ],
40   	            "op monitor",
41   	        ),
42   	        [CibNvpairDto(f"id-{in_effect}-pair1", "name1", "value1")],
43   	    )
44   	
45   	
46   	def fixture_dto_list():
47   	    return [fixture_dto(in_effect.value) for in_effect in CibRuleInEffectStatus]
48   	
49   	
50   	class FilterOutExpiredNvset(TestCase):
51   	    def test_filter(self):
52   	        self.maxDiff = None
53   	        list_with_expired_nvsets = [
54   	            CibNvsetDto(id="nvset-no-rule", options={}, rule=None, nvpairs=[]),
55   	            CibNvsetDto(
56   	                id="nvset-2",
57   	                options={},
58   	                rule=CibRuleExpressionDto(
59   	                    id="rule-another-expired",
60   	                    type=CibRuleExpressionType.DATE_EXPRESSION,
61   	                    in_effect=CibRuleInEffectStatus.EXPIRED,
62   	                    options={},
63   	                    date_spec=None,
64   	                    duration=None,
65   	                    expressions=[],
66   	                    as_string="",
67   	                ),
68   	                nvpairs=[],
69   	            ),
70   	        ] + fixture_dto_list()
71   	        expected_list = [
72   	            item
73   	            for idx, item in enumerate(list_with_expired_nvsets[:])
74   	            if idx not in [1, 4]
75   	        ]
76   	        self.assertEqual(
77   	            nvset.filter_out_expired_nvset(list_with_expired_nvsets),
78   	            expected_list,
79   	        )
80   	
81   	    def test_empty_list(self):
82   	        self.assertEqual(nvset.filter_out_expired_nvset([]), [])
83   	
84   	
85   	class FilterNvpairsByNames(TestCase):
86   	    test_nvsets = [
87   	        CibNvsetDto(
88   	            id="1",
89   	            options={},
90   	            rule=None,
91   	            nvpairs=[
92   	                CibNvpairDto(id="1a", name="a", value="1"),
93   	                CibNvpairDto(id="1b", name="b", value="2"),
94   	                CibNvpairDto(id="1c", name="c", value="3"),
95   	            ],
96   	        ),
97   	        CibNvsetDto(
98   	            id="2",
99   	            options={},
100  	            rule=None,
101  	            nvpairs=[
102  	                CibNvpairDto(id="2a", name="a", value="1"),
103  	                CibNvpairDto(id="2b", name="b", value="2"),
104  	            ],
105  	        ),
106  	    ]
107  	
108  	    def test_filter_no_match(self):
109  	        self.assertEqual(
110  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["x"]),
111  	            [
112  	                CibNvsetDto(id="1", options={}, rule=None, nvpairs=[]),
113  	                CibNvsetDto(id="2", options={}, rule=None, nvpairs=[]),
114  	            ],
115  	        )
116  	
117  	    def test_filter_match_in_one_set(self):
118  	        self.assertEqual(
119  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["c"]),
120  	            [
121  	                CibNvsetDto(
122  	                    id="1",
123  	                    options={},
124  	                    rule=None,
125  	                    nvpairs=[
126  	                        CibNvpairDto(id="1c", name="c", value="3"),
127  	                    ],
128  	                ),
129  	                CibNvsetDto(id="2", options={}, rule=None, nvpairs=[]),
130  	            ],
131  	        )
132  	
133  	    def test_filter_match_in_more_set(self):
134  	        self.assertEqual(
135  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["a"]),
136  	            [
137  	                CibNvsetDto(
138  	                    id="1",
139  	                    options={},
140  	                    rule=None,
141  	                    nvpairs=[
142  	                        CibNvpairDto(id="1a", name="a", value="1"),
143  	                    ],
144  	                ),
145  	                CibNvsetDto(
146  	                    id="2",
147  	                    options={},
148  	                    rule=None,
149  	                    nvpairs=[
150  	                        CibNvpairDto(id="2a", name="a", value="1"),
151  	                    ],
152  	                ),
153  	            ],
154  	        )
155  	
156  	    def test_filter_multiple_filter(self):
157  	        self.assertEqual(
158  	            nvset.filter_nvpairs_by_names(self.test_nvsets, ["a", "c", "x"]),
159  	            [
160  	                CibNvsetDto(
161  	                    id="1",
162  	                    options={},
163  	                    rule=None,
164  	                    nvpairs=[
165  	                        CibNvpairDto(id="1a", name="a", value="1"),
166  	                        CibNvpairDto(id="1c", name="c", value="3"),
167  	                    ],
168  	                ),
169  	                CibNvsetDto(
170  	                    id="2",
171  	                    options={},
172  	                    rule=None,
173  	                    nvpairs=[
174  	                        CibNvpairDto(id="2a", name="a", value="1"),
175  	                    ],
176  	                ),
177  	            ],
178  	        )
179  	
180  	
181  	class NvsetDtoToLines(TestCase):
182  	    def setUp(self):
183  	        self.label = "Meta Attributes"
184  	        self.full_dto = CibNvsetDto(
185  	            "my-id",
186  	            {"score": "150"},
187  	            CibRuleExpressionDto(
188  	                "my-id-rule",
189  	                CibRuleExpressionType.RULE,
190  	                CibRuleInEffectStatus.UNKNOWN,
191  	                {"boolean-op": "or"},
192  	                None,
193  	                None,
194  	                [
195  	                    CibRuleExpressionDto(
196  	                        "my-id-rule-op",
197  	                        CibRuleExpressionType.OP_EXPRESSION,
198  	                        CibRuleInEffectStatus.UNKNOWN,
199  	                        {"name": "monitor"},
200  	                        None,
201  	                        None,
202  	                        [],
203  	                        "op monitor",
204  	                    ),
205  	                ],
206  	                "op monitor",
207  	            ),
208  	            [
209  	                CibNvpairDto("my-id-pair1", "name1", "value1"),
210  	                CibNvpairDto("my-id-pair2", "name 2", "value 2"),
211  	                CibNvpairDto("my-id-pair3", "name=3", "value=3"),
212  	                CibNvpairDto("my-id-pair4", "secret1", "lrm://"),
213  	                CibNvpairDto("my-id-pair5", "secret 2", "lrm://"),
214  	                CibNvpairDto("my-id-pair6", "secret=3", "lrm://"),
215  	            ],
216  	        )
217  	
218  	    def _export(self, dto, with_ids, secrets_map):
219  	        return (
220  	            "\n".join(
221  	                nvset.nvset_dto_to_lines(
222  	                    dto,
223  	                    nvset_label=self.label,
224  	                    with_ids=with_ids,
225  	                    secrets_map=secrets_map,
226  	                )
227  	            )
228  	            + "\n"
229  	        )
230  	
231  	    def assert_lines(self, dto, lines, secrets_map=None):
232  	        self.assertEqual(
233  	            self._export(dto, True, secrets_map),
234  	            lines,
235  	        )
236  	        self.assertEqual(
237  	            self._export(dto, False, secrets_map),
238  	            re.sub(r" +\(id:.*\)", "", lines),
239  	        )
240  	
241  	    def test_minimal(self):
242  	        dto = CibNvsetDto("my-id", {}, None, [])
243  	        output = dedent(
244  	            f"""\
245  	              {self.label}: my-id
246  	            """
247  	        )
248  	        self.assert_lines(dto, output)
249  	
250  	    def test_full_without_secrets_map(self):
251  	        output = dedent(
252  	            f"""\
253  	            {self.label}: my-id score=150
254  	              "name 2"="value 2" (id: my-id-pair2)
255  	              name1=value1 (id: my-id-pair1)
256  	              "name=3"="value=3" (id: my-id-pair3)
257  	              "secret 2"=lrm:// (id: my-id-pair5)
258  	              secret1=lrm:// (id: my-id-pair4)
259  	              "secret=3"=lrm:// (id: my-id-pair6)
260  	              Rule: boolean-op=or (id: my-id-rule)
261  	                Expression: op monitor (id: my-id-rule-op)
262  	            """
263  	        )
264  	        self.assert_lines(self.full_dto, output)
265  	
266  	    def test_full_with_secrets_map(self):
267  	        output = dedent(
268  	            f"""\
269  	            {self.label}: my-id score=150
270  	              "name 2"="value 2" (id: my-id-pair2)
271  	              name1=value1 (id: my-id-pair1)
272  	              "name=3"="value=3" (id: my-id-pair3)
273  	              "secret=3"=lrm:// (id: my-id-pair6)
274  	              Secret {self.label}:
275  	                "secret 2" (id: my-id-pair5)
276  	                secret1=secret_value1 (id: my-id-pair4)
277  	              Rule: boolean-op=or (id: my-id-rule)
278  	                Expression: op monitor (id: my-id-rule-op)
279  	            """
280  	        )
281  	        secrets_map = {
(1) Event Sigma main event: A secret, such as a password, cryptographic key, or token is stored in plaintext directly in the source code, in an application's properties, or configuration file. Users with access to the secret may then use the secret to access resources that they otherwise would not have access to. Secret type: Secret (generic).
(2) Event remediation: Avoid setting sensitive configuration values as string literals. Instead, these values should be set using variables with the sensitive data loaded from an encrypted file or a secret store.
282  	            "secret1": "secret_value1",
283  	            "secret 2": None,
284  	        }
285  	        self.assert_lines(self.full_dto, output, secrets_map)
286  	
287  	
288  	class NvsetDtoListToLines(TestCase):
289  	    def setUp(self):
290  	        self.label = "Meta Attributes"
291  	
292  	    def _export(self, dto, with_ids):
293  	        return (
294  	            "\n".join(
295  	                nvset.nvset_dto_list_to_lines(
296  	                    dto,
297  	                    nvset_label=self.label,
298  	                    with_ids=with_ids,
299  	                )
300  	            )
301  	            + "\n"
302  	        )
303  	
304  	    def assert_lines(self, dto, lines):
305  	        self.assertEqual(
306  	            self._export(dto, True),
307  	            lines,
308  	        )
309  	        self.assertEqual(
310  	            self._export(dto, False),
311  	            re.sub(r" +\(id:.*\)", "", lines),
312  	        )
313  	
314  	    def test_lines(self):
315  	        self.maxDiff = None
316  	        output = dedent(
317  	            f"""\
318  	            {self.label} (not yet in effect): id-NOT_YET_IN_EFFECT score=150
319  	              name1=value1 (id: id-NOT_YET_IN_EFFECT-pair1)
320  	              Rule (not yet in effect): boolean-op=or (id: id-NOT_YET_IN_EFFECT-rule)
321  	                Expression: op monitor (id: id-NOT_YET_IN_EFFECT-rule-op)
322  	            {self.label}: id-IN_EFFECT score=150
323  	              name1=value1 (id: id-IN_EFFECT-pair1)
324  	              Rule: boolean-op=or (id: id-IN_EFFECT-rule)
325  	                Expression: op monitor (id: id-IN_EFFECT-rule-op)
326  	            {self.label} (expired): id-EXPIRED score=150
327  	              name1=value1 (id: id-EXPIRED-pair1)
328  	              Rule (expired): boolean-op=or (id: id-EXPIRED-rule)
329  	                Expression: op monitor (id: id-EXPIRED-rule-op)
330  	            {self.label}: id-UNKNOWN score=150
331  	              name1=value1 (id: id-UNKNOWN-pair1)
332  	              Rule: boolean-op=or (id: id-UNKNOWN-rule)
333  	                Expression: op monitor (id: id-UNKNOWN-rule-op)
334  	        """
335  	        )
336  	        self.assert_lines(fixture_dto_list(), output)
337