glasswall.content_management.policies.policy

  1from typing import Optional, Union
  2
  3import glasswall
  4from glasswall import utils
  5from glasswall.content_management.config_elements.config_element import ConfigElement
  6from glasswall.content_management.errors.config_elements import ConfigElementNotFound
  7from glasswall.content_management.errors.switches import SwitchNotFound
  8from glasswall.content_management.switches.switch import Switch
  9from lxml import etree
 10
 11
 12class Policy:
 13    """ A Content Management Policy made up of a list of ConfigElement instances. """
 14
 15    def __init__(self,
 16                 config_elements: list = [],
 17                 default: Optional[str] = None,
 18                 default_config_elements: list = [],
 19                 config: dict = {},
 20                 **kwargs
 21                 ):
 22        self.config_elements = config_elements or []
 23        self.default = default
 24        self.default_config_elements = default_config_elements or []
 25        self.config = config or {}
 26
 27        # Add default config elements
 28        for config_element in self.default_config_elements:
 29            self.add_config_element(config_element)
 30
 31        # Add customised config elements provided in `config`
 32        for config_element_name, switches in config.items():
 33            # Create config element
 34            config_element = getattr(
 35                glasswall.content_management.config_elements,
 36                config_element_name,
 37                ConfigElement
 38            )
 39
 40            if config_element == glasswall.content_management.config_elements.archiveConfig:
 41                # ArchiveManager archiveConfig special case, use default_archive_manager (no_action, discard, process)
 42                config_element = config_element(default=self.default_archive_manager)
 43            elif config_element == glasswall.content_management.config_elements.textSearchConfig:
 44                # WordSearch textSearchConfig special case, pass attributes and subelements
 45                # construct directly within textSearchConfig as the format is very different
 46                config_element = config_element(attributes=Policy.get_attributes(switches), textList_subelements=switches.get("textList", []))
 47                self.add_config_element(config_element)
 48                continue
 49            elif config_element == glasswall.content_management.config_elements.ConfigElement:
 50                # base ConfigElement class
 51                config_element = config_element(name=config_element_name, default=self.default)
 52            else:
 53                # subclasses of ConfigElement that provide their own name
 54                # if this config_element has a non-None `default` attribute, assign as `self.default`
 55                if getattr(config_element(), "default", None):
 56                    config_element = config_element(default=self.default)
 57                else:
 58                    # Don't set attribute `default`, e.g. `sysConfig`
 59                    config_element = config_element()
 60
 61            for switch_name, switch_value in switches.items():
 62                # If switch is an attribute, update attributes instead of adding switch
 63                if switch_name.startswith("@"):
 64                    config_element.attributes.update({switch_name.replace("@", "", 1): switch_value})
 65                    continue
 66
 67                # If switch is in switches_module, add it to this config element
 68                if hasattr(config_element.switches_module, switch_name):
 69                    config_element.add_switch(getattr(
 70                        config_element.switches_module,
 71                        switch_name
 72                    )(value=switch_value))
 73
 74                # Otherwise, create a new Switch and add it
 75                else:
 76                    config_element.add_switch(Switch(name=switch_name, value=switch_value))
 77
 78            self.add_config_element(config_element)
 79
 80        # Sort self.config_elements by .name and .switches
 81        self.config_elements.sort()
 82
 83    def __str__(self):
 84        return self.text
 85
 86    def __getattr__(self, name):
 87        # Try to return matching ConfigElement from nonexistant attribute
 88        config_element = next(iter(c for c in self.config_elements if c.name == name), None)
 89
 90        if config_element:
 91            return config_element
 92
 93        raise AttributeError(name)
 94
 95    @property
 96    def text(self):
 97        """ String representation of XML. """
 98        string = '<?xml version="1.0" encoding="utf-8"?>'
 99        string += "\n<config>"
100        for config_element in self.config_elements:
101            config_element._indent = 1
102            string += f"\n{config_element.text}"
103        string += '\n</config>'
104
105        return string
106
107    def encode(self, *args):
108        """ UTF-8 encoded string representation of XML. """
109        return str(self).encode(*args)
110
111    def get_config_element_names(self):
112        """ Returns a sorted list of unique ConfigElement.name values from self.config_elements. """
113        return sorted(set(config_element.name for config_element in self.config_elements))
114
115    def remove_switch(self, config_element: Union[ConfigElement, str], switch: Union[Switch, str]):
116        """ Removes all Switch instances from config_element.switches that match arg "switch" where the ConfigElement instance in self.config_elements matches arg "config_element".
117
118        Args:
119            config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name to match.
120            switch (Union[Switch, str]): A Switch instance or Switch.name to match.
121
122        Returns:
123            self
124
125        Raises:
126            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
127            glasswall.content_management.errors.switches.SwitchNotFound: The switch was not found.
128        """
129        if isinstance(config_element, ConfigElement):
130            # If config_element is not in self.config_elements, raise error.
131            if config_element not in self.config_elements:
132                raise ConfigElementNotFound(config_element)
133
134            # The ConfigElement instance exists in self.config_elements, remove the switch.
135            config_element.remove_switch(switch=switch)
136
137        elif isinstance(config_element, str):
138            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
139            config_element_names = self.get_config_element_names()
140            if config_element not in config_element_names:
141                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
142
143            matched_config_elements = [c for c in self.config_elements if c.name == config_element]
144
145            if isinstance(switch, Switch):
146                matched_config_elements_with_switch = [c for c in matched_config_elements if switch in c.switches]
147            elif isinstance(switch, str):
148                matched_config_elements_with_switch = [c for c in matched_config_elements if switch in c.get_switch_names()]
149            else:
150                raise TypeError(switch)
151
152            # If no matching ConfigElement contains arg "switch" in .switches, raise error.
153            if not matched_config_elements_with_switch:
154                available_switches = sorted(set(utils.flatten_list([c.switches for c in matched_config_elements])))
155                switch_name = switch.name if isinstance(switch, Switch) else switch
156                raise SwitchNotFound(f"'{switch_name}' not in {available_switches}")
157
158            # Remove arg "switch" from .switches of all ConfigElement instances that contain arg "switch" in .switches.
159            for c in matched_config_elements_with_switch:
160                c.remove_switch(switch=switch)
161
162        return self
163
164    def add_switch(self, config_element: Union[ConfigElement, str], switch: Switch, replace: bool = True):
165        """ Adds a Switch to any ConfigElement in self.config_elements that matches arg "config_element".
166
167        Args:
168            config_element (Union[ConfigElement, str]): A ConfigElement instance or str to match ConfigElement.name.
169            switch (Switch): A Switch instance.
170            replace (bool, optional): Default True. Deletes any pre-existing Switch with the same .name attribute as arg "switch" within a ConfigElement that matches arg "config_element".
171
172        Returns:
173            self
174
175        Raises:
176            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
177        """
178        if isinstance(config_element, ConfigElement):
179            # If config_element is not in self.config_elements, raise error.
180            if config_element not in self.config_elements:
181                raise ConfigElementNotFound(config_element)
182
183            # The ConfigElement instance exists in self.config_elements, add the switch.
184            config_element.add_switch(switch=switch, replace=replace)
185
186        elif isinstance(config_element, str):
187            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
188            config_element_names = self.get_config_element_names()
189            if config_element not in config_element_names:
190                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
191
192            # At least one ConfigElement instance with the same .name as arg "config_element" exists, add the switch.
193            for c in self.config_elements:
194                if c.name == config_element:
195                    c.add_switch(switch=switch, replace=replace)
196
197        else:
198            raise TypeError(config_element)
199
200        return self
201
202    def remove_config_element(self, config_element: Union[ConfigElement, str]):
203        """ Removes all ConfigElement instances from self.config_elements that match arg "config_element".
204
205        Args:
206            config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name attribute to match.
207
208        Returns:
209            self
210
211        Raises:
212            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
213        """
214        if isinstance(config_element, ConfigElement):
215            # If config_element is not in self.config_elements, raise error.
216            if config_element not in self.config_elements:
217                raise ConfigElementNotFound(config_element)
218
219            while config_element in self.config_elements:
220                # Remove all ConfigElement instances from self.config_elements using the builtin list .remove method
221                self.config_elements.remove(config_element)
222
223        elif isinstance(config_element, str):
224            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
225            config_element_names = self.get_config_element_names()
226            if config_element not in config_element_names:
227                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
228
229            # Remove all ConfigElement instances with the same .name as arg "config_element"
230            self.config_elements = [c for c in self.config_elements if c.name != config_element]
231
232        else:
233            raise TypeError(config_element)
234
235        return self
236
237    def add_config_element(self, config_element: ConfigElement, replace=True):
238        """ Adds a ConfigElement instance to self.config_elements.
239
240        Args:
241            config_element (ConfigElement): A ConfigElement instance.
242            replace (bool, optional): Default True. Deletes any pre-existing ConfigElement with the same .name attribute in self.config_elements.
243
244        Returns:
245            self
246        """
247        if not isinstance(config_element, ConfigElement):
248            raise TypeError(config_element)
249
250        if replace:
251            try:
252                # Remove all ConfigElement instances with the same .name as arg "config_element"
253                self.remove_config_element(config_element=config_element.name)
254            except ConfigElementNotFound:
255                # No ConfigElement exists with the same .name as arg "config_element"
256                pass
257
258        # Sort the .switches attribute of config_element
259        config_element.switches.sort()
260
261        # Append config_element to self.config_elements
262        self.config_elements.append(config_element)
263
264        # Sort self.config_elements by .name and .switches
265        self.config_elements.sort()
266
267        return self
268
269    @staticmethod
270    def get_attributes(dictionary: dict):
271        """ Returns attributes from arg "dictionary". Attributes are key value pairs that have a key starting with "@". The "@" is excluded in the returned keys. """
272        return {
273            k.replace("@", "", 1): v
274            for k, v in dictionary.items()
275            if k.startswith("@")
276        }
277
278    @staticmethod
279    def get_switches(dictionary: dict):
280        """ Returns switches from arg "dictionary". Switches are key value pairs that do not have a key starting with "@". """
281        return {
282            k: v
283            for k, v in dictionary.items()
284            if not k.startswith("@")
285        }
286
287    @staticmethod
288    def from_string(string: str):
289        """ Create Policy object from string.
290
291        Args:
292            string (str): A string representation of an xml content management policy, or a file path.
293
294        Returns:
295            new_policy (glasswall.content_management.policies.Policy): A Policy object.
296        """
297        try:
298            string = glasswall.utils.validate_xml(string)
299        except ValueError:
300            raise glasswall.content_management.errors.policies.ContentManagementPolicyError(string)
301
302        config = etree.fromstring(string.encode("utf-8"))
303
304        if config.tag != "config":
305            raise glasswall.content_management.errors.policies.ContentManagementPolicyError(string)
306
307        new_policy = glasswall.content_management.policies.Policy()
308
309        for config_element in config:
310            if hasattr(glasswall.content_management.config_elements, config_element.tag):
311                # Known config element exists, e.g. pdfConfig
312                new_config_element = getattr(glasswall.content_management.config_elements, config_element.tag)(attributes=config_element.attrib)
313            else:
314                # Create custom config element
315                new_config_element = glasswall.content_management.config_elements.ConfigElement(name=config_element.tag, attributes=config_element.attrib)
316
317            for item in config_element:
318                # Add children, e.g. textList has child elements: textItem
319                if item.getchildren():
320                    # if getchildren() then item is a config element, such as textList
321                    textList = glasswall.content_management.config_elements.ConfigElement(name=item.tag, attributes=item.attrib)
322                    for textItem in item.getchildren():
323                        new_textItem = glasswall.content_management.config_elements.ConfigElement(name=textItem.tag, attributes=textItem.attrib)
324                        for switch in textItem:
325                            new_textItem.add_switch(glasswall.content_management.switches.Switch(name=switch.tag, value=switch.text, attributes=switch.attrib))
326                        textList.subelements.append(new_textItem)
327                    new_config_element.subelements.append(textList)
328                    continue
329
330                # if not getchildren() then item is a switch
331                if hasattr(new_config_element.switches_module, item.tag):
332                    # Known switch exists, e.g. pdf.internal_hyperlinks
333                    new_switch = getattr(new_config_element.switches_module, item.tag)(value=item.text)
334                else:
335                    new_switch = glasswall.content_management.switches.Switch(name=item.tag, value=item.text, attributes=item.attrib)
336                new_config_element.add_switch(new_switch)
337
338            new_policy.add_config_element(new_config_element)
339
340        return new_policy
class Policy:
 15class Policy:
 16    """ A Content Management Policy made up of a list of ConfigElement instances. """
 17
 18    def __init__(self,
 19                 config_elements: list = [],
 20                 default: Optional[str] = None,
 21                 default_config_elements: list = [],
 22                 config: dict = {},
 23                 **kwargs
 24                 ):
 25        self.config_elements = config_elements or []
 26        self.default = default
 27        self.default_config_elements = default_config_elements or []
 28        self.config = config or {}
 29
 30        # Add default config elements
 31        for config_element in self.default_config_elements:
 32            self.add_config_element(config_element)
 33
 34        # Add customised config elements provided in `config`
 35        for config_element_name, switches in config.items():
 36            # Create config element
 37            config_element = getattr(
 38                glasswall.content_management.config_elements,
 39                config_element_name,
 40                ConfigElement
 41            )
 42
 43            if config_element == glasswall.content_management.config_elements.archiveConfig:
 44                # ArchiveManager archiveConfig special case, use default_archive_manager (no_action, discard, process)
 45                config_element = config_element(default=self.default_archive_manager)
 46            elif config_element == glasswall.content_management.config_elements.textSearchConfig:
 47                # WordSearch textSearchConfig special case, pass attributes and subelements
 48                # construct directly within textSearchConfig as the format is very different
 49                config_element = config_element(attributes=Policy.get_attributes(switches), textList_subelements=switches.get("textList", []))
 50                self.add_config_element(config_element)
 51                continue
 52            elif config_element == glasswall.content_management.config_elements.ConfigElement:
 53                # base ConfigElement class
 54                config_element = config_element(name=config_element_name, default=self.default)
 55            else:
 56                # subclasses of ConfigElement that provide their own name
 57                # if this config_element has a non-None `default` attribute, assign as `self.default`
 58                if getattr(config_element(), "default", None):
 59                    config_element = config_element(default=self.default)
 60                else:
 61                    # Don't set attribute `default`, e.g. `sysConfig`
 62                    config_element = config_element()
 63
 64            for switch_name, switch_value in switches.items():
 65                # If switch is an attribute, update attributes instead of adding switch
 66                if switch_name.startswith("@"):
 67                    config_element.attributes.update({switch_name.replace("@", "", 1): switch_value})
 68                    continue
 69
 70                # If switch is in switches_module, add it to this config element
 71                if hasattr(config_element.switches_module, switch_name):
 72                    config_element.add_switch(getattr(
 73                        config_element.switches_module,
 74                        switch_name
 75                    )(value=switch_value))
 76
 77                # Otherwise, create a new Switch and add it
 78                else:
 79                    config_element.add_switch(Switch(name=switch_name, value=switch_value))
 80
 81            self.add_config_element(config_element)
 82
 83        # Sort self.config_elements by .name and .switches
 84        self.config_elements.sort()
 85
 86    def __str__(self):
 87        return self.text
 88
 89    def __getattr__(self, name):
 90        # Try to return matching ConfigElement from nonexistant attribute
 91        config_element = next(iter(c for c in self.config_elements if c.name == name), None)
 92
 93        if config_element:
 94            return config_element
 95
 96        raise AttributeError(name)
 97
 98    @property
 99    def text(self):
100        """ String representation of XML. """
101        string = '<?xml version="1.0" encoding="utf-8"?>'
102        string += "\n<config>"
103        for config_element in self.config_elements:
104            config_element._indent = 1
105            string += f"\n{config_element.text}"
106        string += '\n</config>'
107
108        return string
109
110    def encode(self, *args):
111        """ UTF-8 encoded string representation of XML. """
112        return str(self).encode(*args)
113
114    def get_config_element_names(self):
115        """ Returns a sorted list of unique ConfigElement.name values from self.config_elements. """
116        return sorted(set(config_element.name for config_element in self.config_elements))
117
118    def remove_switch(self, config_element: Union[ConfigElement, str], switch: Union[Switch, str]):
119        """ Removes all Switch instances from config_element.switches that match arg "switch" where the ConfigElement instance in self.config_elements matches arg "config_element".
120
121        Args:
122            config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name to match.
123            switch (Union[Switch, str]): A Switch instance or Switch.name to match.
124
125        Returns:
126            self
127
128        Raises:
129            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
130            glasswall.content_management.errors.switches.SwitchNotFound: The switch was not found.
131        """
132        if isinstance(config_element, ConfigElement):
133            # If config_element is not in self.config_elements, raise error.
134            if config_element not in self.config_elements:
135                raise ConfigElementNotFound(config_element)
136
137            # The ConfigElement instance exists in self.config_elements, remove the switch.
138            config_element.remove_switch(switch=switch)
139
140        elif isinstance(config_element, str):
141            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
142            config_element_names = self.get_config_element_names()
143            if config_element not in config_element_names:
144                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
145
146            matched_config_elements = [c for c in self.config_elements if c.name == config_element]
147
148            if isinstance(switch, Switch):
149                matched_config_elements_with_switch = [c for c in matched_config_elements if switch in c.switches]
150            elif isinstance(switch, str):
151                matched_config_elements_with_switch = [c for c in matched_config_elements if switch in c.get_switch_names()]
152            else:
153                raise TypeError(switch)
154
155            # If no matching ConfigElement contains arg "switch" in .switches, raise error.
156            if not matched_config_elements_with_switch:
157                available_switches = sorted(set(utils.flatten_list([c.switches for c in matched_config_elements])))
158                switch_name = switch.name if isinstance(switch, Switch) else switch
159                raise SwitchNotFound(f"'{switch_name}' not in {available_switches}")
160
161            # Remove arg "switch" from .switches of all ConfigElement instances that contain arg "switch" in .switches.
162            for c in matched_config_elements_with_switch:
163                c.remove_switch(switch=switch)
164
165        return self
166
167    def add_switch(self, config_element: Union[ConfigElement, str], switch: Switch, replace: bool = True):
168        """ Adds a Switch to any ConfigElement in self.config_elements that matches arg "config_element".
169
170        Args:
171            config_element (Union[ConfigElement, str]): A ConfigElement instance or str to match ConfigElement.name.
172            switch (Switch): A Switch instance.
173            replace (bool, optional): Default True. Deletes any pre-existing Switch with the same .name attribute as arg "switch" within a ConfigElement that matches arg "config_element".
174
175        Returns:
176            self
177
178        Raises:
179            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
180        """
181        if isinstance(config_element, ConfigElement):
182            # If config_element is not in self.config_elements, raise error.
183            if config_element not in self.config_elements:
184                raise ConfigElementNotFound(config_element)
185
186            # The ConfigElement instance exists in self.config_elements, add the switch.
187            config_element.add_switch(switch=switch, replace=replace)
188
189        elif isinstance(config_element, str):
190            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
191            config_element_names = self.get_config_element_names()
192            if config_element not in config_element_names:
193                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
194
195            # At least one ConfigElement instance with the same .name as arg "config_element" exists, add the switch.
196            for c in self.config_elements:
197                if c.name == config_element:
198                    c.add_switch(switch=switch, replace=replace)
199
200        else:
201            raise TypeError(config_element)
202
203        return self
204
205    def remove_config_element(self, config_element: Union[ConfigElement, str]):
206        """ Removes all ConfigElement instances from self.config_elements that match arg "config_element".
207
208        Args:
209            config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name attribute to match.
210
211        Returns:
212            self
213
214        Raises:
215            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
216        """
217        if isinstance(config_element, ConfigElement):
218            # If config_element is not in self.config_elements, raise error.
219            if config_element not in self.config_elements:
220                raise ConfigElementNotFound(config_element)
221
222            while config_element in self.config_elements:
223                # Remove all ConfigElement instances from self.config_elements using the builtin list .remove method
224                self.config_elements.remove(config_element)
225
226        elif isinstance(config_element, str):
227            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
228            config_element_names = self.get_config_element_names()
229            if config_element not in config_element_names:
230                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
231
232            # Remove all ConfigElement instances with the same .name as arg "config_element"
233            self.config_elements = [c for c in self.config_elements if c.name != config_element]
234
235        else:
236            raise TypeError(config_element)
237
238        return self
239
240    def add_config_element(self, config_element: ConfigElement, replace=True):
241        """ Adds a ConfigElement instance to self.config_elements.
242
243        Args:
244            config_element (ConfigElement): A ConfigElement instance.
245            replace (bool, optional): Default True. Deletes any pre-existing ConfigElement with the same .name attribute in self.config_elements.
246
247        Returns:
248            self
249        """
250        if not isinstance(config_element, ConfigElement):
251            raise TypeError(config_element)
252
253        if replace:
254            try:
255                # Remove all ConfigElement instances with the same .name as arg "config_element"
256                self.remove_config_element(config_element=config_element.name)
257            except ConfigElementNotFound:
258                # No ConfigElement exists with the same .name as arg "config_element"
259                pass
260
261        # Sort the .switches attribute of config_element
262        config_element.switches.sort()
263
264        # Append config_element to self.config_elements
265        self.config_elements.append(config_element)
266
267        # Sort self.config_elements by .name and .switches
268        self.config_elements.sort()
269
270        return self
271
272    @staticmethod
273    def get_attributes(dictionary: dict):
274        """ Returns attributes from arg "dictionary". Attributes are key value pairs that have a key starting with "@". The "@" is excluded in the returned keys. """
275        return {
276            k.replace("@", "", 1): v
277            for k, v in dictionary.items()
278            if k.startswith("@")
279        }
280
281    @staticmethod
282    def get_switches(dictionary: dict):
283        """ Returns switches from arg "dictionary". Switches are key value pairs that do not have a key starting with "@". """
284        return {
285            k: v
286            for k, v in dictionary.items()
287            if not k.startswith("@")
288        }
289
290    @staticmethod
291    def from_string(string: str):
292        """ Create Policy object from string.
293
294        Args:
295            string (str): A string representation of an xml content management policy, or a file path.
296
297        Returns:
298            new_policy (glasswall.content_management.policies.Policy): A Policy object.
299        """
300        try:
301            string = glasswall.utils.validate_xml(string)
302        except ValueError:
303            raise glasswall.content_management.errors.policies.ContentManagementPolicyError(string)
304
305        config = etree.fromstring(string.encode("utf-8"))
306
307        if config.tag != "config":
308            raise glasswall.content_management.errors.policies.ContentManagementPolicyError(string)
309
310        new_policy = glasswall.content_management.policies.Policy()
311
312        for config_element in config:
313            if hasattr(glasswall.content_management.config_elements, config_element.tag):
314                # Known config element exists, e.g. pdfConfig
315                new_config_element = getattr(glasswall.content_management.config_elements, config_element.tag)(attributes=config_element.attrib)
316            else:
317                # Create custom config element
318                new_config_element = glasswall.content_management.config_elements.ConfigElement(name=config_element.tag, attributes=config_element.attrib)
319
320            for item in config_element:
321                # Add children, e.g. textList has child elements: textItem
322                if item.getchildren():
323                    # if getchildren() then item is a config element, such as textList
324                    textList = glasswall.content_management.config_elements.ConfigElement(name=item.tag, attributes=item.attrib)
325                    for textItem in item.getchildren():
326                        new_textItem = glasswall.content_management.config_elements.ConfigElement(name=textItem.tag, attributes=textItem.attrib)
327                        for switch in textItem:
328                            new_textItem.add_switch(glasswall.content_management.switches.Switch(name=switch.tag, value=switch.text, attributes=switch.attrib))
329                        textList.subelements.append(new_textItem)
330                    new_config_element.subelements.append(textList)
331                    continue
332
333                # if not getchildren() then item is a switch
334                if hasattr(new_config_element.switches_module, item.tag):
335                    # Known switch exists, e.g. pdf.internal_hyperlinks
336                    new_switch = getattr(new_config_element.switches_module, item.tag)(value=item.text)
337                else:
338                    new_switch = glasswall.content_management.switches.Switch(name=item.tag, value=item.text, attributes=item.attrib)
339                new_config_element.add_switch(new_switch)
340
341            new_policy.add_config_element(new_config_element)
342
343        return new_policy

A Content Management Policy made up of a list of ConfigElement instances.

Policy( config_elements: list = [], default: Optional[str] = None, default_config_elements: list = [], config: dict = {}, **kwargs)
18    def __init__(self,
19                 config_elements: list = [],
20                 default: Optional[str] = None,
21                 default_config_elements: list = [],
22                 config: dict = {},
23                 **kwargs
24                 ):
25        self.config_elements = config_elements or []
26        self.default = default
27        self.default_config_elements = default_config_elements or []
28        self.config = config or {}
29
30        # Add default config elements
31        for config_element in self.default_config_elements:
32            self.add_config_element(config_element)
33
34        # Add customised config elements provided in `config`
35        for config_element_name, switches in config.items():
36            # Create config element
37            config_element = getattr(
38                glasswall.content_management.config_elements,
39                config_element_name,
40                ConfigElement
41            )
42
43            if config_element == glasswall.content_management.config_elements.archiveConfig:
44                # ArchiveManager archiveConfig special case, use default_archive_manager (no_action, discard, process)
45                config_element = config_element(default=self.default_archive_manager)
46            elif config_element == glasswall.content_management.config_elements.textSearchConfig:
47                # WordSearch textSearchConfig special case, pass attributes and subelements
48                # construct directly within textSearchConfig as the format is very different
49                config_element = config_element(attributes=Policy.get_attributes(switches), textList_subelements=switches.get("textList", []))
50                self.add_config_element(config_element)
51                continue
52            elif config_element == glasswall.content_management.config_elements.ConfigElement:
53                # base ConfigElement class
54                config_element = config_element(name=config_element_name, default=self.default)
55            else:
56                # subclasses of ConfigElement that provide their own name
57                # if this config_element has a non-None `default` attribute, assign as `self.default`
58                if getattr(config_element(), "default", None):
59                    config_element = config_element(default=self.default)
60                else:
61                    # Don't set attribute `default`, e.g. `sysConfig`
62                    config_element = config_element()
63
64            for switch_name, switch_value in switches.items():
65                # If switch is an attribute, update attributes instead of adding switch
66                if switch_name.startswith("@"):
67                    config_element.attributes.update({switch_name.replace("@", "", 1): switch_value})
68                    continue
69
70                # If switch is in switches_module, add it to this config element
71                if hasattr(config_element.switches_module, switch_name):
72                    config_element.add_switch(getattr(
73                        config_element.switches_module,
74                        switch_name
75                    )(value=switch_value))
76
77                # Otherwise, create a new Switch and add it
78                else:
79                    config_element.add_switch(Switch(name=switch_name, value=switch_value))
80
81            self.add_config_element(config_element)
82
83        # Sort self.config_elements by .name and .switches
84        self.config_elements.sort()
config_elements
default
default_config_elements
config
text
 98    @property
 99    def text(self):
100        """ String representation of XML. """
101        string = '<?xml version="1.0" encoding="utf-8"?>'
102        string += "\n<config>"
103        for config_element in self.config_elements:
104            config_element._indent = 1
105            string += f"\n{config_element.text}"
106        string += '\n</config>'
107
108        return string

String representation of XML.

def encode(self, *args):
110    def encode(self, *args):
111        """ UTF-8 encoded string representation of XML. """
112        return str(self).encode(*args)

UTF-8 encoded string representation of XML.

def get_config_element_names(self):
114    def get_config_element_names(self):
115        """ Returns a sorted list of unique ConfigElement.name values from self.config_elements. """
116        return sorted(set(config_element.name for config_element in self.config_elements))

Returns a sorted list of unique ConfigElement.name values from self.config_elements.

def remove_switch( self, config_element: Union[glasswall.content_management.config_elements.config_element.ConfigElement, str], switch: Union[glasswall.content_management.switches.switch.Switch, str]):
118    def remove_switch(self, config_element: Union[ConfigElement, str], switch: Union[Switch, str]):
119        """ Removes all Switch instances from config_element.switches that match arg "switch" where the ConfigElement instance in self.config_elements matches arg "config_element".
120
121        Args:
122            config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name to match.
123            switch (Union[Switch, str]): A Switch instance or Switch.name to match.
124
125        Returns:
126            self
127
128        Raises:
129            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
130            glasswall.content_management.errors.switches.SwitchNotFound: The switch was not found.
131        """
132        if isinstance(config_element, ConfigElement):
133            # If config_element is not in self.config_elements, raise error.
134            if config_element not in self.config_elements:
135                raise ConfigElementNotFound(config_element)
136
137            # The ConfigElement instance exists in self.config_elements, remove the switch.
138            config_element.remove_switch(switch=switch)
139
140        elif isinstance(config_element, str):
141            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
142            config_element_names = self.get_config_element_names()
143            if config_element not in config_element_names:
144                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
145
146            matched_config_elements = [c for c in self.config_elements if c.name == config_element]
147
148            if isinstance(switch, Switch):
149                matched_config_elements_with_switch = [c for c in matched_config_elements if switch in c.switches]
150            elif isinstance(switch, str):
151                matched_config_elements_with_switch = [c for c in matched_config_elements if switch in c.get_switch_names()]
152            else:
153                raise TypeError(switch)
154
155            # If no matching ConfigElement contains arg "switch" in .switches, raise error.
156            if not matched_config_elements_with_switch:
157                available_switches = sorted(set(utils.flatten_list([c.switches for c in matched_config_elements])))
158                switch_name = switch.name if isinstance(switch, Switch) else switch
159                raise SwitchNotFound(f"'{switch_name}' not in {available_switches}")
160
161            # Remove arg "switch" from .switches of all ConfigElement instances that contain arg "switch" in .switches.
162            for c in matched_config_elements_with_switch:
163                c.remove_switch(switch=switch)
164
165        return self

Removes all Switch instances from config_element.switches that match arg "switch" where the ConfigElement instance in self.config_elements matches arg "config_element".

Args: config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name to match. switch (Union[Switch, str]): A Switch instance or Switch.name to match.

Returns: self

Raises: glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found. glasswall.content_management.errors.switches.SwitchNotFound: The switch was not found.

def add_switch( self, config_element: Union[glasswall.content_management.config_elements.config_element.ConfigElement, str], switch: glasswall.content_management.switches.switch.Switch, replace: bool = True):
167    def add_switch(self, config_element: Union[ConfigElement, str], switch: Switch, replace: bool = True):
168        """ Adds a Switch to any ConfigElement in self.config_elements that matches arg "config_element".
169
170        Args:
171            config_element (Union[ConfigElement, str]): A ConfigElement instance or str to match ConfigElement.name.
172            switch (Switch): A Switch instance.
173            replace (bool, optional): Default True. Deletes any pre-existing Switch with the same .name attribute as arg "switch" within a ConfigElement that matches arg "config_element".
174
175        Returns:
176            self
177
178        Raises:
179            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
180        """
181        if isinstance(config_element, ConfigElement):
182            # If config_element is not in self.config_elements, raise error.
183            if config_element not in self.config_elements:
184                raise ConfigElementNotFound(config_element)
185
186            # The ConfigElement instance exists in self.config_elements, add the switch.
187            config_element.add_switch(switch=switch, replace=replace)
188
189        elif isinstance(config_element, str):
190            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
191            config_element_names = self.get_config_element_names()
192            if config_element not in config_element_names:
193                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
194
195            # At least one ConfigElement instance with the same .name as arg "config_element" exists, add the switch.
196            for c in self.config_elements:
197                if c.name == config_element:
198                    c.add_switch(switch=switch, replace=replace)
199
200        else:
201            raise TypeError(config_element)
202
203        return self

Adds a Switch to any ConfigElement in self.config_elements that matches arg "config_element".

Args: config_element (Union[ConfigElement, str]): A ConfigElement instance or str to match ConfigElement.name. switch (Switch): A Switch instance. replace (bool, optional): Default True. Deletes any pre-existing Switch with the same .name attribute as arg "switch" within a ConfigElement that matches arg "config_element".

Returns: self

Raises: glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.

def remove_config_element( self, config_element: Union[glasswall.content_management.config_elements.config_element.ConfigElement, str]):
205    def remove_config_element(self, config_element: Union[ConfigElement, str]):
206        """ Removes all ConfigElement instances from self.config_elements that match arg "config_element".
207
208        Args:
209            config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name attribute to match.
210
211        Returns:
212            self
213
214        Raises:
215            glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.
216        """
217        if isinstance(config_element, ConfigElement):
218            # If config_element is not in self.config_elements, raise error.
219            if config_element not in self.config_elements:
220                raise ConfigElementNotFound(config_element)
221
222            while config_element in self.config_elements:
223                # Remove all ConfigElement instances from self.config_elements using the builtin list .remove method
224                self.config_elements.remove(config_element)
225
226        elif isinstance(config_element, str):
227            # If no ConfigElement in self.config_elements has a .name matching arg "config_element", raise error.
228            config_element_names = self.get_config_element_names()
229            if config_element not in config_element_names:
230                raise ConfigElementNotFound(f"'{config_element}' not in {config_element_names}")
231
232            # Remove all ConfigElement instances with the same .name as arg "config_element"
233            self.config_elements = [c for c in self.config_elements if c.name != config_element]
234
235        else:
236            raise TypeError(config_element)
237
238        return self

Removes all ConfigElement instances from self.config_elements that match arg "config_element".

Args: config_element (Union[ConfigElement, str]): A ConfigElement instance or ConfigElement.name attribute to match.

Returns: self

Raises: glasswall.content_management.errors.config_elements.ConfigElementNotFound: The config_element was not found.

def add_config_element( self, config_element: glasswall.content_management.config_elements.config_element.ConfigElement, replace=True):
240    def add_config_element(self, config_element: ConfigElement, replace=True):
241        """ Adds a ConfigElement instance to self.config_elements.
242
243        Args:
244            config_element (ConfigElement): A ConfigElement instance.
245            replace (bool, optional): Default True. Deletes any pre-existing ConfigElement with the same .name attribute in self.config_elements.
246
247        Returns:
248            self
249        """
250        if not isinstance(config_element, ConfigElement):
251            raise TypeError(config_element)
252
253        if replace:
254            try:
255                # Remove all ConfigElement instances with the same .name as arg "config_element"
256                self.remove_config_element(config_element=config_element.name)
257            except ConfigElementNotFound:
258                # No ConfigElement exists with the same .name as arg "config_element"
259                pass
260
261        # Sort the .switches attribute of config_element
262        config_element.switches.sort()
263
264        # Append config_element to self.config_elements
265        self.config_elements.append(config_element)
266
267        # Sort self.config_elements by .name and .switches
268        self.config_elements.sort()
269
270        return self

Adds a ConfigElement instance to self.config_elements.

Args: config_element (ConfigElement): A ConfigElement instance. replace (bool, optional): Default True. Deletes any pre-existing ConfigElement with the same .name attribute in self.config_elements.

Returns: self

@staticmethod
def get_attributes(dictionary: dict):
272    @staticmethod
273    def get_attributes(dictionary: dict):
274        """ Returns attributes from arg "dictionary". Attributes are key value pairs that have a key starting with "@". The "@" is excluded in the returned keys. """
275        return {
276            k.replace("@", "", 1): v
277            for k, v in dictionary.items()
278            if k.startswith("@")
279        }

Returns attributes from arg "dictionary". Attributes are key value pairs that have a key starting with "@". The "@" is excluded in the returned keys.

@staticmethod
def get_switches(dictionary: dict):
281    @staticmethod
282    def get_switches(dictionary: dict):
283        """ Returns switches from arg "dictionary". Switches are key value pairs that do not have a key starting with "@". """
284        return {
285            k: v
286            for k, v in dictionary.items()
287            if not k.startswith("@")
288        }

Returns switches from arg "dictionary". Switches are key value pairs that do not have a key starting with "@".

@staticmethod
def from_string(string: str):
290    @staticmethod
291    def from_string(string: str):
292        """ Create Policy object from string.
293
294        Args:
295            string (str): A string representation of an xml content management policy, or a file path.
296
297        Returns:
298            new_policy (glasswall.content_management.policies.Policy): A Policy object.
299        """
300        try:
301            string = glasswall.utils.validate_xml(string)
302        except ValueError:
303            raise glasswall.content_management.errors.policies.ContentManagementPolicyError(string)
304
305        config = etree.fromstring(string.encode("utf-8"))
306
307        if config.tag != "config":
308            raise glasswall.content_management.errors.policies.ContentManagementPolicyError(string)
309
310        new_policy = glasswall.content_management.policies.Policy()
311
312        for config_element in config:
313            if hasattr(glasswall.content_management.config_elements, config_element.tag):
314                # Known config element exists, e.g. pdfConfig
315                new_config_element = getattr(glasswall.content_management.config_elements, config_element.tag)(attributes=config_element.attrib)
316            else:
317                # Create custom config element
318                new_config_element = glasswall.content_management.config_elements.ConfigElement(name=config_element.tag, attributes=config_element.attrib)
319
320            for item in config_element:
321                # Add children, e.g. textList has child elements: textItem
322                if item.getchildren():
323                    # if getchildren() then item is a config element, such as textList
324                    textList = glasswall.content_management.config_elements.ConfigElement(name=item.tag, attributes=item.attrib)
325                    for textItem in item.getchildren():
326                        new_textItem = glasswall.content_management.config_elements.ConfigElement(name=textItem.tag, attributes=textItem.attrib)
327                        for switch in textItem:
328                            new_textItem.add_switch(glasswall.content_management.switches.Switch(name=switch.tag, value=switch.text, attributes=switch.attrib))
329                        textList.subelements.append(new_textItem)
330                    new_config_element.subelements.append(textList)
331                    continue
332
333                # if not getchildren() then item is a switch
334                if hasattr(new_config_element.switches_module, item.tag):
335                    # Known switch exists, e.g. pdf.internal_hyperlinks
336                    new_switch = getattr(new_config_element.switches_module, item.tag)(value=item.text)
337                else:
338                    new_switch = glasswall.content_management.switches.Switch(name=item.tag, value=item.text, attributes=item.attrib)
339                new_config_element.add_switch(new_switch)
340
341            new_policy.add_config_element(new_config_element)
342
343        return new_policy

Create Policy object from string.

Args: string (str): A string representation of an xml content management policy, or a file path.

Returns: new_policy (glasswall.content_management.policies.Policy): A Policy object.