• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from xml.dom import minidom
7from grit.format.policy_templates.writers import xml_formatted_writer
8
9
10def GetWriter(config):
11  '''Factory method for instanciating the ADMXWriter. Every Writer needs a
12  GetWriter method because the TemplateFormatter uses this method to
13  instantiate a Writer.
14  '''
15  return ADMXWriter(['win'], config)
16
17
18class ADMXWriter(xml_formatted_writer.XMLFormattedWriter):
19  '''Class for generating an ADMX policy template. It is used by the
20  PolicyTemplateGenerator to write the admx file.
21  '''
22
23  # DOM root node of the generated ADMX document.
24  _doc = None
25
26  # The ADMX "policies" element that contains the ADMX "policy" elements that
27  # are generated.
28  _active_policies_elem = None
29
30  def _AdmlString(self, name):
31    '''Creates a reference to the named string in an ADML file.
32    Args:
33      name: Name of the referenced ADML string.
34    '''
35    return '$(string.' + name + ')'
36
37  def _AdmlStringExplain(self, name):
38    '''Creates a reference to the named explanation string in an ADML file.
39    Args:
40      name: Name of the referenced ADML explanation.
41    '''
42    return '$(string.' + name + '_Explain)'
43
44  def _AdmlPresentation(self, name):
45    '''Creates a reference to the named presentation element in an ADML file.
46    Args:
47      name: Name of the referenced ADML presentation element.
48    '''
49    return '$(presentation.' + name + ')'
50
51  def _AddPolicyNamespaces(self, parent, prefix, namespace):
52    '''Generates the ADMX "policyNamespace" element and adds the elements to the
53    passed parent element. The namespace of the generated ADMX document is
54    define via the ADMX "target" element. Used namespaces are declared with an
55    ADMX "using" element. ADMX "target" and "using" elements are children of the
56    ADMX "policyNamespace" element.
57
58    Args:
59      parent: The parent node to which all generated elements are added.
60      prefix: A logical name that can be used in the generated ADMX document to
61        refere to this namespace.
62      namespace: Namespace of the generated ADMX document.
63    '''
64    policy_namespaces_elem = self.AddElement(parent, 'policyNamespaces')
65    attributes = {
66      'prefix': prefix,
67      'namespace': namespace,
68    }
69    self.AddElement(policy_namespaces_elem, 'target', attributes)
70    attributes = {
71      'prefix': 'windows',
72      'namespace': 'Microsoft.Policies.Windows',
73    }
74    self.AddElement(policy_namespaces_elem, 'using', attributes)
75
76  def _AddCategory(self, parent, name, display_name,
77                   parent_category_name=None):
78    '''Adds an ADMX category element to the passed parent node. The following
79    snippet shows an example of a category element where "chromium" is the value
80    of the parameter name:
81
82    <category displayName="$(string.chromium)" name="chromium"/>
83
84    Each parent node can have only one category with a given name. Adding the
85    same category again with the same attributes is ignored, but adding it
86    again with different attributes is an error.
87
88    Args:
89      parent: The parent node to which all generated elements are added.
90      name: Name of the category.
91      display_name: Display name of the category.
92      parent_category_name: Name of the parent category. Defaults to None.
93    '''
94    existing = filter(lambda e: e.getAttribute('name') == name,
95                      parent.getElementsByTagName('category'))
96    if existing:
97      assert len(existing) == 1
98      assert existing[0].getAttribute('name') == name
99      assert existing[0].getAttribute('displayName') == display_name
100      return
101    attributes = {
102      'name': name,
103      'displayName': display_name,
104    }
105    category_elem = self.AddElement(parent, 'category', attributes)
106    if parent_category_name:
107      attributes = {'ref': parent_category_name}
108      self.AddElement(category_elem, 'parentCategory', attributes)
109
110  def _AddCategories(self, categories):
111    '''Generates the ADMX "categories" element and adds it to the categories
112    main node. The "categories" element defines the category for the policies
113    defined in this ADMX document. Here is an example of an ADMX "categories"
114    element:
115
116    <categories>
117      <category displayName="$(string.google)" name="google"/>
118      <category displayName="$(string.googlechrome)" name="googlechrome">
119        <parentCategory ref="google"/>
120      </category>
121    </categories>
122
123    Args:
124      categories_path: The categories path e.g. ['google', 'googlechrome']. For
125        each level in the path a "category" element will be generated. Except
126        for the root level, each level refers to its parent. Since the root
127        level category has no parent it does not require a parent reference.
128    '''
129    category_name = None
130    for category in categories:
131      parent_category_name = category_name
132      category_name = category
133      self._AddCategory(self._categories_elem, category_name,
134                        self._AdmlString(category_name), parent_category_name)
135
136  def _AddSupportedOn(self, parent, supported_os):
137    '''Generates the "supportedOn" ADMX element and adds it to the passed
138    parent node. The "supportedOn" element contains information about supported
139    Windows OS versions. The following code snippet contains an example of a
140    "supportedOn" element:
141
142    <supportedOn>
143      <definitions>
144        <definition name="SUPPORTED_WINXPSP2"
145                    displayName="$(string.SUPPORTED_WINXPSP2)"/>
146        </definitions>
147        ...
148    </supportedOn>
149
150    Args:
151      parent: The parent element to which all generated elements are added.
152      supported_os: List with all supported Win OSes.
153    '''
154    supported_on_elem = self.AddElement(parent, 'supportedOn')
155    definitions_elem = self.AddElement(supported_on_elem, 'definitions')
156    attributes = {
157      'name': supported_os,
158      'displayName': self._AdmlString(supported_os)
159    }
160    self.AddElement(definitions_elem, 'definition', attributes)
161
162  def _AddStringPolicy(self, parent, name):
163    '''Generates ADMX elements for a String-Policy and adds them to the
164    passed parent node.
165    '''
166    attributes = {
167      'id': name,
168      'valueName': name,
169    }
170    self.AddElement(parent, 'text', attributes)
171
172  def _AddIntPolicy(self, parent, name):
173    '''Generates ADMX elements for an Int-Policy and adds them to the passed
174    parent node.
175    '''
176    attributes = {
177      'id': name,
178      'valueName': name,
179      'maxValue': '2000000000',
180    }
181    self.AddElement(parent, 'decimal', attributes)
182
183  def _AddEnumPolicy(self, parent, policy):
184    '''Generates ADMX elements for an Enum-Policy and adds them to the
185    passed parent element.
186    '''
187    name = policy['name']
188    items = policy['items']
189    attributes = {
190      'id': name,
191      'valueName': name,
192    }
193    enum_elem = self.AddElement(parent, 'enum', attributes)
194    for item in items:
195      attributes = {'displayName': self._AdmlString(item['name'])}
196      item_elem = self.AddElement(enum_elem, 'item', attributes)
197      value_elem = self.AddElement(item_elem, 'value')
198      value_string = str(item['value'])
199      if policy['type'] == 'int-enum':
200        self.AddElement(value_elem, 'decimal', {'value': value_string})
201      else:
202        self.AddElement(value_elem, 'string', {}, value_string)
203
204  def _AddListPolicy(self, parent, key, name):
205    '''Generates ADMX XML elements for a List-Policy and adds them to the
206    passed parent element.
207    '''
208    attributes = {
209      # The ID must be in sync with ID of the corresponding element in the ADML
210      # file.
211      'id': name + 'Desc',
212      'valuePrefix': '',
213      'key': key + '\\' + name,
214    }
215    self.AddElement(parent, 'list', attributes)
216
217  def _AddMainPolicy(self, parent):
218    '''Generates ADMX elements for a Main-Policy amd adds them to the
219    passed parent element.
220    '''
221    enabled_value_elem = self.AddElement(parent, 'enabledValue');
222    self.AddElement(enabled_value_elem, 'decimal', {'value': '1'})
223    disabled_value_elem = self.AddElement(parent, 'disabledValue');
224    self.AddElement(disabled_value_elem, 'decimal', {'value': '0'})
225
226  def _GetElements(self, policy_group_elem):
227    '''Returns the ADMX "elements" child from an ADMX "policy" element. If the
228    "policy" element has no "elements" child yet, a new child is created.
229
230    Args:
231      policy_group_elem: The ADMX "policy" element from which the child element
232        "elements" is returned.
233
234    Raises:
235      Exception: The policy_group_elem does not contain a ADMX "policy" element.
236    '''
237    if policy_group_elem.tagName != 'policy':
238      raise Exception('Expected a "policy" element but got a "%s" element'
239                      % policy_group_elem.tagName)
240    elements_list = policy_group_elem.getElementsByTagName('elements');
241    if len(elements_list) == 0:
242      return self.AddElement(policy_group_elem, 'elements')
243    elif len(elements_list) == 1:
244      return elements_list[0]
245    else:
246      raise Exception('There is supposed to be only one "elements" node but'
247                      ' there are %s.' % str(len(elements_list)))
248
249  def _WritePolicy(self, policy, name, key, parent):
250    '''Generates AMDX elements for a Policy. There are four different policy
251    types: Main-Policy, String-Policy, Enum-Policy and List-Policy.
252    '''
253    policies_elem = self._active_policies_elem
254    policy_type = policy['type']
255    policy_name = policy['name']
256    if policy_type == 'external':
257      # This type can only be set through cloud policy.
258      return
259
260    attributes = {
261      'name': name,
262      'class': self.config['win_group_policy_class'],
263      'displayName': self._AdmlString(policy_name),
264      'explainText': self._AdmlStringExplain(policy_name),
265      'presentation': self._AdmlPresentation(policy_name),
266      'key': key,
267    }
268    # Store the current "policy" AMDX element in self for later use by the
269    # WritePolicy method.
270    policy_elem = self.AddElement(policies_elem, 'policy',
271                                  attributes)
272    self.AddElement(policy_elem, 'parentCategory',
273                    {'ref': parent})
274    self.AddElement(policy_elem, 'supportedOn',
275                    {'ref': self.config['win_supported_os']})
276    if policy_type == 'main':
277      self.AddAttribute(policy_elem, 'valueName', policy_name)
278      self._AddMainPolicy(policy_elem)
279    elif policy_type in ('string', 'dict'):
280      # 'dict' policies are configured as JSON-encoded strings on Windows.
281      parent = self._GetElements(policy_elem)
282      self._AddStringPolicy(parent, policy_name)
283    elif policy_type == 'int':
284      parent = self._GetElements(policy_elem)
285      self._AddIntPolicy(parent, policy_name)
286    elif policy_type in ('int-enum', 'string-enum'):
287      parent = self._GetElements(policy_elem)
288      self._AddEnumPolicy(parent, policy)
289    elif policy_type in ('list', 'string-enum-list'):
290      parent = self._GetElements(policy_elem)
291      self._AddListPolicy(parent, key, policy_name)
292    elif policy_type == 'group':
293      pass
294    else:
295      raise Exception('Unknown policy type %s.' % policy_type)
296
297  def WritePolicy(self, policy):
298    if self.CanBeMandatory(policy):
299      self._WritePolicy(policy,
300                        policy['name'],
301                        self.config['win_reg_mandatory_key_name'],
302                        self._active_mandatory_policy_group_name)
303
304  def WriteRecommendedPolicy(self, policy):
305    self._WritePolicy(policy,
306                      policy['name'] + '_recommended',
307                      self.config['win_reg_recommended_key_name'],
308                      self._active_recommended_policy_group_name)
309
310  def _BeginPolicyGroup(self, group, name, parent):
311    '''Generates ADMX elements for a Policy-Group.
312    '''
313    attributes = {
314      'name': name,
315      'displayName': self._AdmlString(group['name'] + '_group'),
316    }
317    category_elem = self.AddElement(self._categories_elem,
318                                    'category',
319                                    attributes)
320    attributes = {
321      'ref': parent
322    }
323    self.AddElement(category_elem, 'parentCategory', attributes)
324
325  def BeginPolicyGroup(self, group):
326    self._BeginPolicyGroup(group,
327                           group['name'],
328                           self.config['win_mandatory_category_path'][-1])
329    self._active_mandatory_policy_group_name = group['name']
330
331  def EndPolicyGroup(self):
332    self._active_mandatory_policy_group_name = \
333        self.config['win_mandatory_category_path'][-1]
334
335  def BeginRecommendedPolicyGroup(self, group):
336    self._BeginPolicyGroup(group,
337                           group['name'] + '_recommended',
338                           self.config['win_recommended_category_path'][-1])
339    self._active_recommended_policy_group_name = group['name'] + '_recommended'
340
341  def EndRecommendedPolicyGroup(self):
342    self._active_recommended_policy_group_name = \
343        self.config['win_recommended_category_path'][-1]
344
345  def BeginTemplate(self):
346    '''Generates the skeleton of the ADMX template. An ADMX template contains
347    an ADMX "PolicyDefinitions" element with four child nodes: "policies"
348    "policyNamspaces", "resources", "supportedOn" and "categories"
349    '''
350    dom_impl = minidom.getDOMImplementation('')
351    self._doc = dom_impl.createDocument(None, 'policyDefinitions', None)
352    policy_definitions_elem = self._doc.documentElement
353
354    policy_definitions_elem.attributes['revision'] = '1.0'
355    policy_definitions_elem.attributes['schemaVersion'] = '1.0'
356
357    self._AddPolicyNamespaces(policy_definitions_elem,
358                              self.config['admx_prefix'],
359                              self.config['admx_namespace'])
360    self.AddElement(policy_definitions_elem, 'resources',
361                    {'minRequiredRevision' : '1.0'})
362    self._AddSupportedOn(policy_definitions_elem,
363                         self.config['win_supported_os'])
364    self._categories_elem = self.AddElement(policy_definitions_elem,
365                                            'categories')
366    self._AddCategories(self.config['win_mandatory_category_path'])
367    self._AddCategories(self.config['win_recommended_category_path'])
368    self._active_policies_elem = self.AddElement(policy_definitions_elem,
369                                                 'policies')
370    self._active_mandatory_policy_group_name = \
371        self.config['win_mandatory_category_path'][-1]
372    self._active_recommended_policy_group_name = \
373        self.config['win_recommended_category_path'][-1]
374
375  def GetTemplateText(self):
376    return self.ToPrettyXml(self._doc)
377