| OLD | NEW |
| (Empty) |
| 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 | |
| 6 '''python %prog [options] platform chromium_os_flag template | |
| 7 | |
| 8 platform specifies which platform source is being generated for | |
| 9 and can be one of (win, mac, linux) | |
| 10 chromium_os_flag should be 1 if this is a Chromium OS build | |
| 11 template is the path to a .json policy template file.''' | |
| 12 | |
| 13 from __future__ import with_statement | |
| 14 import json | |
| 15 from optparse import OptionParser | |
| 16 import re | |
| 17 import sys | |
| 18 import textwrap | |
| 19 | |
| 20 | |
| 21 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' | |
| 22 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' | |
| 23 | |
| 24 | |
| 25 class PolicyDetails: | |
| 26 """Parses a policy template and caches all its details.""" | |
| 27 | |
| 28 # Maps policy types to a tuple with 3 other types: | |
| 29 # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy | |
| 30 # references external data | |
| 31 # - the equivalent Protobuf field type | |
| 32 # - the name of one of the protobufs for shared policy types | |
| 33 # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type | |
| 34 # that can also be used to represent lists of other JSON objects. | |
| 35 TYPE_MAP = { | |
| 36 'dict': ('TYPE_DICTIONARY', 'string', 'String'), | |
| 37 'external': ('TYPE_EXTERNAL', 'string', 'String'), | |
| 38 'int': ('TYPE_INTEGER', 'int64', 'Integer'), | |
| 39 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer'), | |
| 40 'list': ('TYPE_LIST', 'StringList', 'StringList'), | |
| 41 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean'), | |
| 42 'string': ('TYPE_STRING', 'string', 'String'), | |
| 43 'string-enum': ('TYPE_STRING', 'string', 'String'), | |
| 44 } | |
| 45 | |
| 46 class EnumItem: | |
| 47 def __init__(self, item): | |
| 48 self.caption = PolicyDetails._RemovePlaceholders(item['caption']) | |
| 49 self.value = item['value'] | |
| 50 | |
| 51 def __init__(self, policy, os, is_chromium_os): | |
| 52 self.id = policy['id'] | |
| 53 self.name = policy['name'] | |
| 54 self.is_deprecated = policy.get('deprecated', False) | |
| 55 self.is_device_only = policy.get('device_only', False) | |
| 56 self.schema = policy.get('schema', {}) | |
| 57 | |
| 58 expected_platform = 'chrome_os' if is_chromium_os else os.lower() | |
| 59 self.platforms = [] | |
| 60 for platform, version in [ p.split(':') for p in policy['supported_on'] ]: | |
| 61 if not version.endswith('-'): | |
| 62 continue | |
| 63 | |
| 64 if platform.startswith('chrome.'): | |
| 65 platform_sub = platform[7:] | |
| 66 if platform_sub == '*': | |
| 67 self.platforms.extend(['win', 'mac', 'linux']) | |
| 68 else: | |
| 69 self.platforms.append(platform_sub) | |
| 70 else: | |
| 71 self.platforms.append(platform) | |
| 72 | |
| 73 self.platforms.sort() | |
| 74 self.is_supported = expected_platform in self.platforms | |
| 75 | |
| 76 if not PolicyDetails.TYPE_MAP.has_key(policy['type']): | |
| 77 raise NotImplementedError('Unknown policy type for %s: %s' % | |
| 78 (policy['name'], policy['type'])) | |
| 79 self.policy_type, self.protobuf_type, self.policy_protobuf_type = \ | |
| 80 PolicyDetails.TYPE_MAP[policy['type']] | |
| 81 self.schema = policy['schema'] | |
| 82 | |
| 83 self.desc = '\n'.join( | |
| 84 map(str.strip, | |
| 85 PolicyDetails._RemovePlaceholders(policy['desc']).splitlines())) | |
| 86 self.caption = PolicyDetails._RemovePlaceholders(policy['caption']) | |
| 87 self.max_size = policy.get('max_size', 0) | |
| 88 | |
| 89 items = policy.get('items') | |
| 90 if items is None: | |
| 91 self.items = None | |
| 92 else: | |
| 93 self.items = [ PolicyDetails.EnumItem(entry) for entry in items ] | |
| 94 | |
| 95 PH_PATTERN = re.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>') | |
| 96 | |
| 97 # Simplistic grit placeholder stripper. | |
| 98 @staticmethod | |
| 99 def _RemovePlaceholders(text): | |
| 100 result = '' | |
| 101 pos = 0 | |
| 102 for m in PolicyDetails.PH_PATTERN.finditer(text): | |
| 103 result += text[pos:m.start(0)] | |
| 104 result += m.group(2) or m.group(1) | |
| 105 pos = m.end(0) | |
| 106 result += text[pos:] | |
| 107 return result | |
| 108 | |
| 109 | |
| 110 def main(): | |
| 111 parser = OptionParser(usage=__doc__) | |
| 112 parser.add_option('--pch', '--policy-constants-header', dest='header_path', | |
| 113 help='generate header file of policy constants', | |
| 114 metavar='FILE') | |
| 115 parser.add_option('--pcc', '--policy-constants-source', dest='source_path', | |
| 116 help='generate source file of policy constants', | |
| 117 metavar='FILE') | |
| 118 parser.add_option('--cpp', '--cloud-policy-protobuf', | |
| 119 dest='cloud_policy_proto_path', | |
| 120 help='generate cloud policy protobuf file', | |
| 121 metavar='FILE') | |
| 122 parser.add_option('--csp', '--chrome-settings-protobuf', | |
| 123 dest='chrome_settings_proto_path', | |
| 124 help='generate chrome settings protobuf file', | |
| 125 metavar='FILE') | |
| 126 parser.add_option('--cpd', '--cloud-policy-decoder', | |
| 127 dest='cloud_policy_decoder_path', | |
| 128 help='generate C++ code decoding the cloud policy protobuf', | |
| 129 metavar='FILE') | |
| 130 | |
| 131 (opts, args) = parser.parse_args() | |
| 132 | |
| 133 if len(args) != 3: | |
| 134 print 'exactly platform, chromium_os flag and input file must be specified.' | |
| 135 parser.print_help() | |
| 136 return 2 | |
| 137 | |
| 138 os = args[0] | |
| 139 is_chromium_os = args[1] == '1' | |
| 140 template_file_name = args[2] | |
| 141 | |
| 142 template_file_contents = _LoadJSONFile(template_file_name) | |
| 143 policy_details = [ PolicyDetails(policy, os, is_chromium_os) | |
| 144 for policy in _Flatten(template_file_contents) ] | |
| 145 sorted_policy_details = sorted(policy_details, key=lambda policy: policy.name) | |
| 146 | |
| 147 def GenerateFile(path, writer, sorted=False): | |
| 148 if path: | |
| 149 with open(path, 'w') as f: | |
| 150 _OutputGeneratedWarningHeader(f, template_file_name) | |
| 151 writer(sorted and sorted_policy_details or policy_details, os, f) | |
| 152 | |
| 153 GenerateFile(opts.header_path, _WritePolicyConstantHeader, sorted=True) | |
| 154 GenerateFile(opts.source_path, _WritePolicyConstantSource, sorted=True) | |
| 155 GenerateFile(opts.cloud_policy_proto_path, _WriteCloudPolicyProtobuf) | |
| 156 GenerateFile(opts.chrome_settings_proto_path, _WriteChromeSettingsProtobuf) | |
| 157 GenerateFile(opts.cloud_policy_decoder_path, _WriteCloudPolicyDecoder) | |
| 158 | |
| 159 return 0 | |
| 160 | |
| 161 | |
| 162 #------------------ shared helpers ---------------------------------# | |
| 163 | |
| 164 def _OutputGeneratedWarningHeader(f, template_file_path): | |
| 165 f.write('//\n' | |
| 166 '// DO NOT MODIFY THIS FILE DIRECTLY!\n' | |
| 167 '// IT IS GENERATED BY generate_policy_source.py\n' | |
| 168 '// FROM ' + template_file_path + '\n' | |
| 169 '//\n\n') | |
| 170 | |
| 171 | |
| 172 COMMENT_WRAPPER = textwrap.TextWrapper() | |
| 173 COMMENT_WRAPPER.width = 80 | |
| 174 COMMENT_WRAPPER.initial_indent = '// ' | |
| 175 COMMENT_WRAPPER.subsequent_indent = '// ' | |
| 176 COMMENT_WRAPPER.replace_whitespace = False | |
| 177 | |
| 178 | |
| 179 # Writes a comment, each line prefixed by // and wrapped to 80 spaces. | |
| 180 def _OutputComment(f, comment): | |
| 181 for line in comment.splitlines(): | |
| 182 if len(line) == 0: | |
| 183 f.write('//') | |
| 184 else: | |
| 185 f.write(COMMENT_WRAPPER.fill(line)) | |
| 186 f.write('\n') | |
| 187 | |
| 188 | |
| 189 # Returns an iterator over all the policies in |template_file_contents|. | |
| 190 def _Flatten(template_file_contents): | |
| 191 for policy in template_file_contents['policy_definitions']: | |
| 192 if policy['type'] == 'group': | |
| 193 for sub_policy in policy['policies']: | |
| 194 yield sub_policy | |
| 195 else: | |
| 196 yield policy | |
| 197 | |
| 198 | |
| 199 def _LoadJSONFile(json_file): | |
| 200 with open(json_file, 'r') as f: | |
| 201 text = f.read() | |
| 202 return eval(text) | |
| 203 | |
| 204 | |
| 205 #------------------ policy constants header ------------------------# | |
| 206 | |
| 207 def _WritePolicyConstantHeader(policies, os, f): | |
| 208 f.write('#ifndef CHROME_COMMON_POLICY_CONSTANTS_H_\n' | |
| 209 '#define CHROME_COMMON_POLICY_CONSTANTS_H_\n' | |
| 210 '\n' | |
| 211 '#include <string>\n' | |
| 212 '\n' | |
| 213 '#include "base/basictypes.h"\n' | |
| 214 '#include "base/values.h"\n' | |
| 215 '#include "components/policy/core/common/policy_details.h"\n' | |
| 216 '\n' | |
| 217 'namespace policy {\n' | |
| 218 '\n' | |
| 219 'namespace internal {\n' | |
| 220 'struct SchemaData;\n' | |
| 221 '}\n\n') | |
| 222 | |
| 223 if os == 'win': | |
| 224 f.write('// The windows registry path where Chrome policy ' | |
| 225 'configuration resides.\n' | |
| 226 'extern const wchar_t kRegistryChromePolicyKey[];\n') | |
| 227 | |
| 228 f.write('// Returns the PolicyDetails for |policy| if |policy| is a known\n' | |
| 229 '// Chrome policy, otherwise returns NULL.\n' | |
| 230 'const PolicyDetails* GetChromePolicyDetails(' | |
| 231 'const std::string& policy);\n' | |
| 232 '\n' | |
| 233 '// Returns the schema data of the Chrome policy schema.\n' | |
| 234 'const internal::SchemaData* GetChromeSchemaData();\n' | |
| 235 '\n') | |
| 236 f.write('// Key names for the policy settings.\n' | |
| 237 'namespace key {\n\n') | |
| 238 for policy in policies: | |
| 239 # TODO(joaodasilva): Include only supported policies in | |
| 240 # configuration_policy_handler.cc and configuration_policy_handler_list.cc | |
| 241 # so that these names can be conditional on 'policy.is_supported'. | |
| 242 # http://crbug.com/223616 | |
| 243 f.write('extern const char k' + policy.name + '[];\n') | |
| 244 f.write('\n} // namespace key\n\n' | |
| 245 '} // namespace policy\n\n' | |
| 246 '#endif // CHROME_COMMON_POLICY_CONSTANTS_H_\n') | |
| 247 | |
| 248 | |
| 249 #------------------ policy constants source ------------------------# | |
| 250 | |
| 251 # A mapping of the simple schema types to base::Value::Types. | |
| 252 SIMPLE_SCHEMA_NAME_MAP = { | |
| 253 'boolean': 'TYPE_BOOLEAN', | |
| 254 'integer': 'TYPE_INTEGER', | |
| 255 'null' : 'TYPE_NULL', | |
| 256 'number' : 'TYPE_DOUBLE', | |
| 257 'string' : 'TYPE_STRING', | |
| 258 } | |
| 259 | |
| 260 | |
| 261 class SchemaNodesGenerator: | |
| 262 """Builds the internal structs to represent a JSON schema.""" | |
| 263 | |
| 264 def __init__(self, shared_strings): | |
| 265 """Creates a new generator. | |
| 266 | |
| 267 |shared_strings| is a map of strings to a C expression that evaluates to | |
| 268 that string at runtime. This mapping can be used to reuse existing string | |
| 269 constants.""" | |
| 270 self.shared_strings = shared_strings | |
| 271 self.schema_nodes = [] | |
| 272 self.property_nodes = [] | |
| 273 self.properties_nodes = [] | |
| 274 self.simple_types = { | |
| 275 'boolean': None, | |
| 276 'integer': None, | |
| 277 'null': None, | |
| 278 'number': None, | |
| 279 'string': None, | |
| 280 } | |
| 281 self.stringlist_type = None | |
| 282 | |
| 283 def GetString(self, s): | |
| 284 return self.shared_strings[s] if s in self.shared_strings else '"%s"' % s | |
| 285 | |
| 286 def AppendSchema(self, type, extra, comment=''): | |
| 287 index = len(self.schema_nodes) | |
| 288 self.schema_nodes.append((type, extra, comment)) | |
| 289 return index | |
| 290 | |
| 291 def GetSimpleType(self, name): | |
| 292 if self.simple_types[name] == None: | |
| 293 self.simple_types[name] = self.AppendSchema( | |
| 294 SIMPLE_SCHEMA_NAME_MAP[name], | |
| 295 -1, | |
| 296 'simple type: ' + name) | |
| 297 return self.simple_types[name] | |
| 298 | |
| 299 def GetStringList(self): | |
| 300 if self.stringlist_type == None: | |
| 301 self.stringlist_type = self.AppendSchema( | |
| 302 'TYPE_LIST', | |
| 303 self.GetSimpleType('string'), | |
| 304 'simple type: stringlist') | |
| 305 return self.stringlist_type | |
| 306 | |
| 307 def Generate(self, schema, name): | |
| 308 """Generates the structs for the given schema. | |
| 309 | |
| 310 |schema|: a valid JSON schema in a dictionary. | |
| 311 |name|: the name of the current node, for the generated comments.""" | |
| 312 # Simple types use shared nodes. | |
| 313 if schema['type'] in self.simple_types: | |
| 314 return self.GetSimpleType(schema['type']) | |
| 315 | |
| 316 if schema['type'] == 'array': | |
| 317 # Special case for lists of strings, which is a common policy type. | |
| 318 if schema['items']['type'] == 'string': | |
| 319 return self.GetStringList() | |
| 320 return self.AppendSchema( | |
| 321 'TYPE_LIST', | |
| 322 self.Generate(schema['items'], 'items of ' + name)) | |
| 323 elif schema['type'] == 'object': | |
| 324 # Reserve an index first, so that dictionaries come before their | |
| 325 # properties. This makes sure that the root node is the first in the | |
| 326 # SchemaNodes array. | |
| 327 index = self.AppendSchema('TYPE_DICTIONARY', -1) | |
| 328 | |
| 329 if 'additionalProperties' in schema: | |
| 330 additionalProperties = self.Generate( | |
| 331 schema['additionalProperties'], | |
| 332 'additionalProperties of ' + name) | |
| 333 else: | |
| 334 additionalProperties = -1 | |
| 335 | |
| 336 # Properties must be sorted by name, for the binary search lookup. | |
| 337 # Note that |properties| must be evaluated immediately, so that all the | |
| 338 # recursive calls to Generate() append the necessary child nodes; if | |
| 339 # |properties| were a generator then this wouldn't work. | |
| 340 sorted_properties = sorted(schema.get('properties', {}).items()) | |
| 341 properties = [ (self.GetString(key), self.Generate(schema, key)) | |
| 342 for key, schema in sorted_properties ] | |
| 343 begin = len(self.property_nodes) | |
| 344 self.property_nodes += properties | |
| 345 end = len(self.property_nodes) | |
| 346 if index == 0: | |
| 347 self.root_properties_begin = begin | |
| 348 self.root_properties_end = end | |
| 349 | |
| 350 extra = len(self.properties_nodes) | |
| 351 self.properties_nodes.append((begin, end, additionalProperties, name)) | |
| 352 | |
| 353 # Set the right data at |index| now. | |
| 354 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name) | |
| 355 return index | |
| 356 else: | |
| 357 assert False | |
| 358 | |
| 359 def Write(self, f): | |
| 360 """Writes the generated structs to the given file. | |
| 361 | |
| 362 |f| an open file to write to.""" | |
| 363 f.write('const internal::SchemaNode kSchemas[] = {\n' | |
| 364 '// Type Extra\n') | |
| 365 for type, extra, comment in self.schema_nodes: | |
| 366 type += ',' | |
| 367 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment)) | |
| 368 f.write('};\n\n') | |
| 369 | |
| 370 f.write('const internal::PropertyNode kPropertyNodes[] = {\n' | |
| 371 '// Property #Schema\n') | |
| 372 for key, schema in self.property_nodes: | |
| 373 key += ',' | |
| 374 f.write(' { %-50s %7d },\n' % (key, schema)) | |
| 375 f.write('};\n\n') | |
| 376 | |
| 377 f.write('const internal::PropertiesNode kProperties[] = {\n' | |
| 378 '// Begin End Additional Properties\n') | |
| 379 for node in self.properties_nodes: | |
| 380 f.write(' { %5d, %5d, %5d }, // %s\n' % node) | |
| 381 f.write('};\n\n') | |
| 382 | |
| 383 f.write('const internal::SchemaData kChromeSchemaData = {\n' | |
| 384 ' kSchemas,\n' | |
| 385 ' kPropertyNodes,\n' | |
| 386 ' kProperties,\n' | |
| 387 '};\n\n') | |
| 388 | |
| 389 | |
| 390 def _WritePolicyConstantSource(policies, os, f): | |
| 391 f.write('#include "policy/policy_constants.h"\n' | |
| 392 '\n' | |
| 393 '#include <algorithm>\n' | |
| 394 '\n' | |
| 395 '#include "base/logging.h"\n' | |
| 396 '#include "components/policy/core/common/schema_internal.h"\n' | |
| 397 '\n' | |
| 398 'namespace policy {\n' | |
| 399 '\n' | |
| 400 'namespace {\n' | |
| 401 '\n') | |
| 402 | |
| 403 # Generate the Chrome schema. | |
| 404 chrome_schema = { | |
| 405 'type': 'object', | |
| 406 'properties': {}, | |
| 407 } | |
| 408 shared_strings = {} | |
| 409 for policy in policies: | |
| 410 shared_strings[policy.name] = "key::k%s" % policy.name | |
| 411 if policy.is_supported: | |
| 412 chrome_schema['properties'][policy.name] = policy.schema | |
| 413 | |
| 414 # Note: this list must be kept in sync with the known property list of the | |
| 415 # Chrome schema, so that binary seaching in the PropertyNode array gets the | |
| 416 # right index on this array as well. See the implementation of | |
| 417 # GetChromePolicyDetails() below. | |
| 418 f.write('const PolicyDetails kChromePolicyDetails[] = {\n' | |
| 419 '// is_deprecated is_device_policy id max_external_data_size\n') | |
| 420 for policy in policies: | |
| 421 if policy.is_supported: | |
| 422 f.write(' { %-14s %-16s %3s, %24s },\n' % ( | |
| 423 'true,' if policy.is_deprecated else 'false,', | |
| 424 'true,' if policy.is_device_only else 'false,', | |
| 425 policy.id, | |
| 426 policy.max_size)) | |
| 427 f.write('};\n\n') | |
| 428 | |
| 429 schema_generator = SchemaNodesGenerator(shared_strings) | |
| 430 schema_generator.Generate(chrome_schema, 'root node') | |
| 431 schema_generator.Write(f) | |
| 432 | |
| 433 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' | |
| 434 ' const std::string& key) {\n' | |
| 435 ' return node.key < key;\n' | |
| 436 '}\n\n') | |
| 437 | |
| 438 f.write('} // namespace\n\n') | |
| 439 | |
| 440 if os == 'win': | |
| 441 f.write('#if defined(GOOGLE_CHROME_BUILD)\n' | |
| 442 'const wchar_t kRegistryChromePolicyKey[] = ' | |
| 443 'L"' + CHROME_POLICY_KEY + '";\n' | |
| 444 '#else\n' | |
| 445 'const wchar_t kRegistryChromePolicyKey[] = ' | |
| 446 'L"' + CHROMIUM_POLICY_KEY + '";\n' | |
| 447 '#endif\n\n') | |
| 448 | |
| 449 f.write('const internal::SchemaData* GetChromeSchemaData() {\n' | |
| 450 ' return &kChromeSchemaData;\n' | |
| 451 '}\n\n') | |
| 452 | |
| 453 f.write('const PolicyDetails* GetChromePolicyDetails(' | |
| 454 'const std::string& policy) {\n' | |
| 455 ' // First index in kPropertyNodes of the Chrome policies.\n' | |
| 456 ' static const int begin_index = %s;\n' | |
| 457 ' // One-past-the-end of the Chrome policies in kPropertyNodes.\n' | |
| 458 ' static const int end_index = %s;\n' % | |
| 459 (schema_generator.root_properties_begin, | |
| 460 schema_generator.root_properties_end)) | |
| 461 f.write(' const internal::PropertyNode* begin =\n' | |
| 462 ' kPropertyNodes + begin_index;\n' | |
| 463 ' const internal::PropertyNode* end = kPropertyNodes + end_index;\n' | |
| 464 ' const internal::PropertyNode* it =\n' | |
| 465 ' std::lower_bound(begin, end, policy, CompareKeys);\n' | |
| 466 ' if (it == end || it->key != policy)\n' | |
| 467 ' return NULL;\n' | |
| 468 ' // This relies on kPropertyNodes from begin_index to end_index\n' | |
| 469 ' // having exactly the same policies (and in the same order) as\n' | |
| 470 ' // kChromePolicyDetails, so that binary searching on the first\n' | |
| 471 ' // gets the same results as a binary search on the second would.\n' | |
| 472 ' // However, kPropertyNodes has the policy names and\n' | |
| 473 ' // kChromePolicyDetails doesn\'t, so we obtain the index into\n' | |
| 474 ' // the second array by searching the first to avoid duplicating\n' | |
| 475 ' // the policy name pointers.\n' | |
| 476 ' // Offsetting |it| from |begin| here obtains the index we\'re\n' | |
| 477 ' // looking for.\n' | |
| 478 ' size_t index = it - begin;\n' | |
| 479 ' CHECK_LT(index, arraysize(kChromePolicyDetails));\n' | |
| 480 ' return kChromePolicyDetails + index;\n' | |
| 481 '}\n\n') | |
| 482 | |
| 483 f.write('namespace key {\n\n') | |
| 484 for policy in policies: | |
| 485 # TODO(joaodasilva): Include only supported policies in | |
| 486 # configuration_policy_handler.cc and configuration_policy_handler_list.cc | |
| 487 # so that these names can be conditional on 'policy.is_supported'. | |
| 488 # http://crbug.com/223616 | |
| 489 f.write('const char k{name}[] = "{name}";\n'.format(name=policy.name)) | |
| 490 f.write('\n} // namespace key\n\n' | |
| 491 '} // namespace policy\n') | |
| 492 | |
| 493 | |
| 494 #------------------ policy protobufs --------------------------------# | |
| 495 | |
| 496 CHROME_SETTINGS_PROTO_HEAD = ''' | |
| 497 syntax = "proto2"; | |
| 498 | |
| 499 option optimize_for = LITE_RUNTIME; | |
| 500 | |
| 501 package enterprise_management; | |
| 502 | |
| 503 // For StringList and PolicyOptions. | |
| 504 import "cloud_policy.proto"; | |
| 505 | |
| 506 ''' | |
| 507 | |
| 508 | |
| 509 CLOUD_POLICY_PROTO_HEAD = ''' | |
| 510 syntax = "proto2"; | |
| 511 | |
| 512 option optimize_for = LITE_RUNTIME; | |
| 513 | |
| 514 package enterprise_management; | |
| 515 | |
| 516 message StringList { | |
| 517 repeated string entries = 1; | |
| 518 } | |
| 519 | |
| 520 message PolicyOptions { | |
| 521 enum PolicyMode { | |
| 522 // The given settings are applied regardless of user choice. | |
| 523 MANDATORY = 0; | |
| 524 // The user may choose to override the given settings. | |
| 525 RECOMMENDED = 1; | |
| 526 // No policy value is present and the policy should be ignored. | |
| 527 UNSET = 2; | |
| 528 } | |
| 529 optional PolicyMode mode = 1 [default = MANDATORY]; | |
| 530 } | |
| 531 | |
| 532 message BooleanPolicyProto { | |
| 533 optional PolicyOptions policy_options = 1; | |
| 534 optional bool value = 2; | |
| 535 } | |
| 536 | |
| 537 message IntegerPolicyProto { | |
| 538 optional PolicyOptions policy_options = 1; | |
| 539 optional int64 value = 2; | |
| 540 } | |
| 541 | |
| 542 message StringPolicyProto { | |
| 543 optional PolicyOptions policy_options = 1; | |
| 544 optional string value = 2; | |
| 545 } | |
| 546 | |
| 547 message StringListPolicyProto { | |
| 548 optional PolicyOptions policy_options = 1; | |
| 549 optional StringList value = 2; | |
| 550 } | |
| 551 | |
| 552 ''' | |
| 553 | |
| 554 | |
| 555 # Field IDs [1..RESERVED_IDS] will not be used in the wrapping protobuf. | |
| 556 RESERVED_IDS = 2 | |
| 557 | |
| 558 | |
| 559 def _WritePolicyProto(f, policy, fields): | |
| 560 _OutputComment(f, policy.caption + '\n\n' + policy.desc) | |
| 561 if policy.items is not None: | |
| 562 _OutputComment(f, '\nValid values:') | |
| 563 for item in policy.items: | |
| 564 _OutputComment(f, ' %s: %s' % (str(item.value), item.caption)) | |
| 565 if policy.policy_type == 'TYPE_DICTIONARY': | |
| 566 _OutputComment(f, '\nValue schema:\n%s' % | |
| 567 json.dumps(policy.schema, sort_keys=True, indent=4, | |
| 568 separators=(',', ': '))) | |
| 569 _OutputComment(f, '\nSupported on: %s' % ', '.join(policy.platforms)) | |
| 570 f.write('message %sProto {\n' % policy.name) | |
| 571 f.write(' optional PolicyOptions policy_options = 1;\n') | |
| 572 f.write(' optional %s %s = 2;\n' % (policy.protobuf_type, policy.name)) | |
| 573 f.write('}\n\n') | |
| 574 fields += [ ' optional %sProto %s = %s;\n' % | |
| 575 (policy.name, policy.name, policy.id + RESERVED_IDS) ] | |
| 576 | |
| 577 | |
| 578 def _WriteChromeSettingsProtobuf(policies, os, f): | |
| 579 f.write(CHROME_SETTINGS_PROTO_HEAD) | |
| 580 | |
| 581 fields = [] | |
| 582 f.write('// PBs for individual settings.\n\n') | |
| 583 for policy in policies: | |
| 584 # Note: this protobuf also gets the unsupported policies, since it's an | |
| 585 # exaustive list of all the supported user policies on any platform. | |
| 586 if not policy.is_device_only: | |
| 587 _WritePolicyProto(f, policy, fields) | |
| 588 | |
| 589 f.write('// --------------------------------------------------\n' | |
| 590 '// Big wrapper PB containing the above groups.\n\n' | |
| 591 'message ChromeSettingsProto {\n') | |
| 592 f.write(''.join(fields)) | |
| 593 f.write('}\n\n') | |
| 594 | |
| 595 | |
| 596 def _WriteCloudPolicyProtobuf(policies, os, f): | |
| 597 f.write(CLOUD_POLICY_PROTO_HEAD) | |
| 598 f.write('message CloudPolicySettings {\n') | |
| 599 for policy in policies: | |
| 600 if policy.is_supported and not policy.is_device_only: | |
| 601 f.write(' optional %sPolicyProto %s = %s;\n' % | |
| 602 (policy.policy_protobuf_type, policy.name, | |
| 603 policy.id + RESERVED_IDS)) | |
| 604 f.write('}\n\n') | |
| 605 | |
| 606 | |
| 607 #------------------ protobuf decoder -------------------------------# | |
| 608 | |
| 609 CPP_HEAD = ''' | |
| 610 #include <limits> | |
| 611 #include <string> | |
| 612 | |
| 613 #include "base/basictypes.h" | |
| 614 #include "base/callback.h" | |
| 615 #include "base/json/json_reader.h" | |
| 616 #include "base/logging.h" | |
| 617 #include "base/memory/scoped_ptr.h" | |
| 618 #include "base/memory/weak_ptr.h" | |
| 619 #include "base/values.h" | |
| 620 #include "components/policy/core/common/cloud/cloud_external_data_manager.h" | |
| 621 #include "components/policy/core/common/external_data_fetcher.h" | |
| 622 #include "components/policy/core/common/policy_map.h" | |
| 623 #include "policy/policy_constants.h" | |
| 624 #include "policy/proto/cloud_policy.pb.h" | |
| 625 | |
| 626 using google::protobuf::RepeatedPtrField; | |
| 627 | |
| 628 namespace policy { | |
| 629 | |
| 630 namespace em = enterprise_management; | |
| 631 | |
| 632 base::Value* DecodeIntegerValue(google::protobuf::int64 value) { | |
| 633 if (value < std::numeric_limits<int>::min() || | |
| 634 value > std::numeric_limits<int>::max()) { | |
| 635 LOG(WARNING) << "Integer value " << value | |
| 636 << " out of numeric limits, ignoring."; | |
| 637 return NULL; | |
| 638 } | |
| 639 | |
| 640 return base::Value::CreateIntegerValue(static_cast<int>(value)); | |
| 641 } | |
| 642 | |
| 643 base::ListValue* DecodeStringList(const em::StringList& string_list) { | |
| 644 base::ListValue* list_value = new base::ListValue; | |
| 645 RepeatedPtrField<std::string>::const_iterator entry; | |
| 646 for (entry = string_list.entries().begin(); | |
| 647 entry != string_list.entries().end(); ++entry) { | |
| 648 list_value->Append(base::Value::CreateStringValue(*entry)); | |
| 649 } | |
| 650 return list_value; | |
| 651 } | |
| 652 | |
| 653 base::Value* DecodeJson(const std::string& json) { | |
| 654 scoped_ptr<base::Value> root( | |
| 655 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS)); | |
| 656 | |
| 657 if (!root) | |
| 658 LOG(WARNING) << "Invalid JSON string, ignoring: " << json; | |
| 659 | |
| 660 // Accept any Value type that parsed as JSON, and leave it to the handler to | |
| 661 // convert and check the concrete type. | |
| 662 return root.release(); | |
| 663 } | |
| 664 | |
| 665 void DecodePolicy(const em::CloudPolicySettings& policy, | |
| 666 base::WeakPtr<CloudExternalDataManager> external_data_manager, | |
| 667 PolicyMap* map) { | |
| 668 ''' | |
| 669 | |
| 670 | |
| 671 CPP_FOOT = '''} | |
| 672 | |
| 673 } // namespace policy | |
| 674 ''' | |
| 675 | |
| 676 | |
| 677 def _CreateValue(type, arg): | |
| 678 if type == 'TYPE_BOOLEAN': | |
| 679 return 'base::Value::CreateBooleanValue(%s)' % arg | |
| 680 elif type == 'TYPE_INTEGER': | |
| 681 return 'DecodeIntegerValue(%s)' % arg | |
| 682 elif type == 'TYPE_STRING': | |
| 683 return 'base::Value::CreateStringValue(%s)' % arg | |
| 684 elif type == 'TYPE_LIST': | |
| 685 return 'DecodeStringList(%s)' % arg | |
| 686 elif type == 'TYPE_DICTIONARY' or type == 'TYPE_EXTERNAL': | |
| 687 return 'DecodeJson(%s)' % arg | |
| 688 else: | |
| 689 raise NotImplementedError('Unknown type %s' % type) | |
| 690 | |
| 691 | |
| 692 def _CreateExternalDataFetcher(type, name): | |
| 693 if type == 'TYPE_EXTERNAL': | |
| 694 return 'new ExternalDataFetcher(external_data_manager, key::k%s)' % name | |
| 695 return 'NULL' | |
| 696 | |
| 697 | |
| 698 def _WritePolicyCode(f, policy): | |
| 699 membername = policy.name.lower() | |
| 700 proto_type = '%sPolicyProto' % policy.policy_protobuf_type | |
| 701 f.write(' if (policy.has_%s()) {\n' % membername) | |
| 702 f.write(' const em::%s& policy_proto = policy.%s();\n' % | |
| 703 (proto_type, membername)) | |
| 704 f.write(' if (policy_proto.has_value()) {\n') | |
| 705 f.write(' PolicyLevel level = POLICY_LEVEL_MANDATORY;\n' | |
| 706 ' bool do_set = true;\n' | |
| 707 ' if (policy_proto.has_policy_options()) {\n' | |
| 708 ' do_set = false;\n' | |
| 709 ' switch(policy_proto.policy_options().mode()) {\n' | |
| 710 ' case em::PolicyOptions::MANDATORY:\n' | |
| 711 ' do_set = true;\n' | |
| 712 ' level = POLICY_LEVEL_MANDATORY;\n' | |
| 713 ' break;\n' | |
| 714 ' case em::PolicyOptions::RECOMMENDED:\n' | |
| 715 ' do_set = true;\n' | |
| 716 ' level = POLICY_LEVEL_RECOMMENDED;\n' | |
| 717 ' break;\n' | |
| 718 ' case em::PolicyOptions::UNSET:\n' | |
| 719 ' break;\n' | |
| 720 ' }\n' | |
| 721 ' }\n' | |
| 722 ' if (do_set) {\n') | |
| 723 f.write(' base::Value* value = %s;\n' % | |
| 724 (_CreateValue(policy.policy_type, 'policy_proto.value()'))) | |
| 725 # TODO(bartfab): |value| == NULL indicates that the policy value could not be | |
| 726 # parsed successfully. Surface such errors in the UI. | |
| 727 f.write(' if (value) {\n') | |
| 728 f.write(' ExternalDataFetcher* external_data_fetcher = %s;\n' % | |
| 729 _CreateExternalDataFetcher(policy.policy_type, policy.name)) | |
| 730 f.write(' map->Set(key::k%s, level, POLICY_SCOPE_USER,\n' % | |
| 731 policy.name) | |
| 732 f.write(' value, external_data_fetcher);\n' | |
| 733 ' }\n' | |
| 734 ' }\n' | |
| 735 ' }\n' | |
| 736 ' }\n') | |
| 737 | |
| 738 | |
| 739 def _WriteCloudPolicyDecoder(policies, os, f): | |
| 740 f.write(CPP_HEAD) | |
| 741 for policy in policies: | |
| 742 if policy.is_supported and not policy.is_device_only: | |
| 743 _WritePolicyCode(f, policy) | |
| 744 f.write(CPP_FOOT) | |
| 745 | |
| 746 | |
| 747 if __name__ == '__main__': | |
| 748 sys.exit(main()) | |
| OLD | NEW |