blob: f15c13f98e55a5538dc0e1bc211a3fec325309c7 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from writers import gpo_editor_writer
import re
NEWLINE = '\r\n'
POLICY_LIST_URL = '''https://cloud.google.com/docs/chrome-enterprise/policies/?policy='''
def GetWriter(config):
'''Factory method for creating AdmWriter objects.
See the constructor of TemplateWriter for description of
arguments.
'''
return AdmWriter(['win', 'win7'], config)
class IndentedStringBuilder:
'''Utility class for building text with indented lines.'''
def __init__(self):
self.lines = []
self.indent = ''
def AddLine(self, string='', indent_diff=0):
'''Appends a string with indentation and a linebreak to |self.lines|.
Args:
string: The string to print.
indent_diff: the difference of indentation of the printed line,
compared to the next/previous printed line. Increment occurs
after printing the line, while decrement occurs before that.
'''
indent_diff *= 2
if indent_diff < 0:
self.indent = self.indent[(-indent_diff):]
if string != '':
self.lines.append(self.indent + string)
else:
self.lines.append('')
if indent_diff > 0:
self.indent += ''.ljust(indent_diff)
def AddLines(self, other):
'''Appends the content of another |IndentedStringBuilder| to |self.lines|.
Indentation of the added lines will be the sum of |self.indent| and
their original indentation.
Args:
other: The buffer from which lines are copied.
'''
for line in other.lines:
self.AddLine(line)
def ToString(self):
'''Returns |self.lines| as text string.'''
return NEWLINE.join(self.lines)
class AdmWriter(gpo_editor_writer.GpoEditorWriter):
'''Class for generating policy templates in Windows ADM format.
It is used by PolicyTemplateGenerator to write ADM files.
'''
TYPE_TO_INPUT = {
'string': 'EDITTEXT',
'int': 'NUMERIC',
'string-enum': 'DROPDOWNLIST',
'int-enum': 'DROPDOWNLIST',
'list': 'LISTBOX',
'string-enum-list': 'LISTBOX',
'dict': 'EDITTEXT',
'external': 'EDITTEXT'
}
def _Escape(self, string):
return string.replace('.', '_')
def _AddGuiString(self, name, value):
# The |name| must be escaped.
assert name == self._Escape(name)
# Escape newlines in the value.
value = value.replace('\n', '\\n')
if name in self.strings_seen:
err = ('%s was added as "%s" and now added again as "%s"' %
(name, self.strings_seen[name], value))
assert value == self.strings_seen[name], err
else:
self.strings_seen[name] = value
line = '%s="%s"' % (name, value)
self.strings.AddLine(line)
def _WriteSupported(self, builder, is_win7_only):
builder.AddLine('#if version >= 4', 1)
key = 'win_supported_os_win7' if is_win7_only else 'win_supported_os'
supported_on_text = self.config[key]
builder.AddLine('SUPPORTED !!' + supported_on_text)
builder.AddLine('#endif', -1)
def _WritePart(self, policy, key_name, builder):
'''Writes the PART ... END PART section of a policy.
Args:
policy: The policy to write to the output.
key_name: The registry key backing the policy.
builder: Builder to append lines to.
'''
policy_part_name = self._Escape(policy['name'] + '_Part')
self._AddGuiString(policy_part_name, policy['label'])
# Print the PART ... END PART section:
builder.AddLine()
adm_type = self.TYPE_TO_INPUT[policy['type']]
builder.AddLine('PART !!%s %s' % (policy_part_name, adm_type), 1)
if policy['type'] in ('list', 'string-enum-list'):
# Note that the following line causes FullArmor ADMX Migrator to create
# corrupt ADMX files. Please use admx_writer to get ADMX files.
builder.AddLine('KEYNAME "%s\\%s"' % (key_name, policy['name']))
builder.AddLine('VALUEPREFIX ""')
else:
builder.AddLine('VALUENAME "%s"' % policy['name'])
if policy['type'] == 'int':
# The default max for NUMERIC values is 9999 which is too small for us.
max = '2000000000'
min = '0'
if self.PolicyHasRestrictions(policy):
schema = policy['schema']
if 'minimum' in schema:
min = schema['minimum']
if 'maximum' in schema:
max = schema['maximum']
builder.AddLine('MIN ' + str(min) + ' MAX ' + max)
if policy['type'] in ('string', 'dict', 'external'):
# The default max for EDITTEXT values is 1023, which is too small for
# big JSON blobs and other string policies.
builder.AddLine('MAXLEN 1000000')
if policy['type'] in ('int-enum', 'string-enum'):
builder.AddLine('ITEMLIST', 1)
for item in policy['items']:
if policy['type'] == 'int-enum':
value_text = 'NUMERIC ' + str(item['value'])
else:
value_text = '"' + item['value'] + '"'
string_id = self._Escape(item['name'] + '_DropDown')
builder.AddLine('NAME !!%s VALUE %s' % (string_id, value_text))
self._AddGuiString(string_id, item['caption'])
builder.AddLine('END ITEMLIST', -1)
builder.AddLine('END PART', -1)
def PolicyHasRestrictions(self, policy):
if 'schema' in policy:
return any(keyword in policy['schema'] \
for keyword in ['minimum', 'maximum'])
return False
def _WritePolicy(self, policy, key_name, builder):
policy_name = self._Escape(policy['name'] + '_Policy')
self._AddGuiString(policy_name, policy['caption'])
builder.AddLine('POLICY !!%s' % policy_name, 1)
self._WriteSupported(builder, self.IsPolicyOnWin7Only(policy))
policy_explain_name = self._Escape(policy['name'] + '_Explain')
policy_explain = self._GetPolicyExplanation(policy)
self._AddGuiString(policy_explain_name, policy_explain)
builder.AddLine('EXPLAIN !!' + policy_explain_name)
if policy['type'] == 'main':
builder.AddLine('VALUENAME "%s"' % policy['name'])
builder.AddLine('VALUEON NUMERIC 1')
builder.AddLine('VALUEOFF NUMERIC 0')
else:
self._WritePart(policy, key_name, builder)
builder.AddLine('END POLICY', -1)
builder.AddLine()
def _GetPolicyExplanation(self, policy):
'''Returns the explanation for a given policy.
Includes a link to the relevant documentation on chromium.org.
'''
policy_desc = policy.get('desc')
reference_url = POLICY_LIST_URL + policy['name']
reference_link_text = self.GetLocalizedMessage('reference_link')
reference_link_text = reference_link_text.replace('$6', reference_url)
if policy_desc is not None:
policy_desc += '\n\n'
if not policy.get('deprecated', False):
policy_desc += reference_link_text
return policy_desc
else:
return reference_link_text
def WriteComment(self, comment):
self.lines.AddLine('; ' + comment)
def WritePolicy(self, policy):
if self.CanBeMandatory(policy):
self._WritePolicy(policy, self.winconfig['reg_mandatory_key_name'],
self.policies)
def WriteRecommendedPolicy(self, policy):
self._WritePolicy(policy, self.winconfig['reg_recommended_key_name'],
self.recommended_policies)
def BeginPolicyGroup(self, group):
category_name = self._Escape(group['name'] + '_Category')
self._AddGuiString(category_name, group['caption'])
self.policies.AddLine('CATEGORY !!' + category_name, 1)
def EndPolicyGroup(self):
self.policies.AddLine('END CATEGORY', -1)
self.policies.AddLine('')
def BeginRecommendedPolicyGroup(self, group):
category_name = self._Escape(group['name'] + '_Category')
self._AddGuiString(category_name, group['caption'])
self.recommended_policies.AddLine('CATEGORY !!' + category_name, 1)
def EndRecommendedPolicyGroup(self):
self.recommended_policies.AddLine('END CATEGORY', -1)
self.recommended_policies.AddLine('')
def _CreateTemplate(self, category_path, key_name, policies):
'''Creates the whole ADM template except for the [Strings] section, and
returns it as an |IndentedStringBuilder|.
Args:
category_path: List of strings representing the category path.
key_name: Main registry key backing the policies.
policies: ADM code for all the policies in an |IndentedStringBuilder|.
'''
lines = IndentedStringBuilder()
for part in category_path:
lines.AddLine('CATEGORY !!' + part, 1)
lines.AddLine('KEYNAME "%s"' % key_name)
lines.AddLine()
lines.AddLines(policies)
for part in category_path:
lines.AddLine('END CATEGORY', -1)
lines.AddLine()
return lines
def BeginTemplate(self):
if self._GetChromiumVersionString() is not None:
self.WriteComment(self.config['build'] + ' version: ' + \
self._GetChromiumVersionString())
self._AddGuiString(self.config['win_supported_os'],
self.messages['win_supported_all']['text'])
self._AddGuiString(self.config['win_supported_os_win7'],
self.messages['win_supported_win7']['text'])
categories = self.winconfig['mandatory_category_path'] + \
self.winconfig['recommended_category_path']
strings = self.winconfig['category_path_strings'].copy()
if 'adm_category_path_strings' in self.config:
strings.update(self.config['adm_category_path_strings'])
for category in categories:
if (category in strings):
# Replace {...} by localized messages.
string = re.sub(r"\{(\w+)\}", \
lambda m: self.messages[m.group(1)]['text'], \
strings[category])
self._AddGuiString(category, string)
# All the policies will be written into self.policies.
# The final template text will be assembled into self.lines by
# self.EndTemplate().
def EndTemplate(self):
# Copy policies into self.lines.
policy_class = self.GetClass().upper()
for class_name in ['MACHINE', 'USER']:
if policy_class != 'BOTH' and policy_class != class_name:
continue
self.lines.AddLine('CLASS ' + class_name, 1)
self.lines.AddLines(
self._CreateTemplate(self.winconfig['mandatory_category_path'],
self.winconfig['reg_mandatory_key_name'],
self.policies))
self.lines.AddLines(
self._CreateTemplate(self.winconfig['recommended_category_path'],
self.winconfig['reg_recommended_key_name'],
self.recommended_policies))
self.lines.AddLine('', -1)
# Copy user strings into self.lines.
self.lines.AddLine('[Strings]')
self.lines.AddLines(self.strings)
def Init(self):
# String buffer for building the whole ADM file.
self.lines = IndentedStringBuilder()
# String buffer for building the strings section of the ADM file.
self.strings = IndentedStringBuilder()
# Map of strings seen, to avoid duplicates.
self.strings_seen = {}
# String buffer for building the policies of the ADM file.
self.policies = IndentedStringBuilder()
# String buffer for building the recommended policies of the ADM file.
self.recommended_policies = IndentedStringBuilder()
# Shortcut to platform-specific ADMX/ADM specific configuration.
assert len(self.platforms) == 2
self.winconfig = self.config['win_config'][self.platforms[0]]
def GetTemplateText(self):
return self.lines.ToString()
def GetClass(self):
return 'Both'