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
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.
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()
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.
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.
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.
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.
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.
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.
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
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.
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 "@".
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.