Roll libvpx 861f35:1fff3e
[chromium-blink-merge.git] / components / policy / tools / generate_policy_source.py
blobb3a723b72e6da0c8dea5acb2698afd9028d6c1f3
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.
6 '''python %prog [options] platform chromium_os_flag template
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.'''
13 from __future__ import with_statement
14 from functools import partial
15 import json
16 from optparse import OptionParser
17 import re
18 import sys
19 import textwrap
20 import types
21 from xml.sax.saxutils import escape as xml_escape
24 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome'
25 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium'
28 class PolicyDetails:
29 """Parses a policy template and caches all its details."""
31 # Maps policy types to a tuple with 4 other types:
32 # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy
33 # references external data
34 # - the equivalent Protobuf field type
35 # - the name of one of the protobufs for shared policy types
36 # - the equivalent type in Android's App Restriction Schema
37 # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type
38 # that can also be used to represent lists of other JSON objects.
39 TYPE_MAP = {
40 'dict': ('TYPE_DICTIONARY', 'string', 'String',
41 'string'),
42 'external': ('TYPE_EXTERNAL', 'string', 'String',
43 'invalid'),
44 'int': ('TYPE_INTEGER', 'int64', 'Integer',
45 'integer'),
46 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer',
47 'choice'),
48 'list': ('TYPE_LIST', 'StringList', 'StringList',
49 'string'),
50 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean',
51 'bool'),
52 'string': ('TYPE_STRING', 'string', 'String',
53 'string'),
54 'string-enum': ('TYPE_STRING', 'string', 'String',
55 'choice'),
56 'string-enum-list': ('TYPE_LIST', 'StringList', 'StringList',
57 'multi-select'),
60 class EnumItem:
61 def __init__(self, item):
62 self.caption = PolicyDetails._RemovePlaceholders(item['caption'])
63 self.value = item['value']
65 def __init__(self, policy, os, is_chromium_os):
66 self.id = policy['id']
67 self.name = policy['name']
68 features = policy.get('features', {})
69 self.can_be_recommended = features.get('can_be_recommended', False)
70 self.can_be_mandatory = features.get('can_be_mandatory', True)
71 self.is_deprecated = policy.get('deprecated', False)
72 self.is_device_only = policy.get('device_only', False)
73 self.schema = policy.get('schema', {})
74 self.has_enterprise_default = 'default_for_enterprise_users' in policy
75 if self.has_enterprise_default:
76 self.enterprise_default = policy['default_for_enterprise_users']
78 expected_platform = 'chrome_os' if is_chromium_os else os.lower()
79 self.platforms = []
80 for platform, version in [ p.split(':') for p in policy['supported_on'] ]:
81 if not version.endswith('-'):
82 continue
84 if platform.startswith('chrome.'):
85 platform_sub = platform[7:]
86 if platform_sub == '*':
87 self.platforms.extend(['win', 'mac', 'linux'])
88 else:
89 self.platforms.append(platform_sub)
90 else:
91 self.platforms.append(platform)
93 self.platforms.sort()
94 self.is_supported = expected_platform in self.platforms
96 if not PolicyDetails.TYPE_MAP.has_key(policy['type']):
97 raise NotImplementedError('Unknown policy type for %s: %s' %
98 (policy['name'], policy['type']))
99 self.policy_type, self.protobuf_type, self.policy_protobuf_type, \
100 self.restriction_type = PolicyDetails.TYPE_MAP[policy['type']]
101 self.schema = policy['schema']
103 self.desc = '\n'.join(
104 map(str.strip,
105 PolicyDetails._RemovePlaceholders(policy['desc']).splitlines()))
106 self.caption = PolicyDetails._RemovePlaceholders(policy['caption'])
107 self.max_size = policy.get('max_size', 0)
109 items = policy.get('items')
110 if items is None:
111 self.items = None
112 else:
113 self.items = [ PolicyDetails.EnumItem(entry) for entry in items ]
115 PH_PATTERN = re.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>')
117 # Simplistic grit placeholder stripper.
118 @staticmethod
119 def _RemovePlaceholders(text):
120 result = ''
121 pos = 0
122 for m in PolicyDetails.PH_PATTERN.finditer(text):
123 result += text[pos:m.start(0)]
124 result += m.group(2) or m.group(1)
125 pos = m.end(0)
126 result += text[pos:]
127 return result
130 def main():
131 parser = OptionParser(usage=__doc__)
132 parser.add_option('--pch', '--policy-constants-header', dest='header_path',
133 help='generate header file of policy constants',
134 metavar='FILE')
135 parser.add_option('--pcc', '--policy-constants-source', dest='source_path',
136 help='generate source file of policy constants',
137 metavar='FILE')
138 parser.add_option('--cpp', '--cloud-policy-protobuf',
139 dest='cloud_policy_proto_path',
140 help='generate cloud policy protobuf file',
141 metavar='FILE')
142 parser.add_option('--csp', '--chrome-settings-protobuf',
143 dest='chrome_settings_proto_path',
144 help='generate chrome settings protobuf file',
145 metavar='FILE')
146 parser.add_option('--cpd', '--cloud-policy-decoder',
147 dest='cloud_policy_decoder_path',
148 help='generate C++ code decoding the cloud policy protobuf',
149 metavar='FILE')
150 parser.add_option('--ard', '--app-restrictions-definition',
151 dest='app_restrictions_path',
152 help='generate an XML file as specified by '
153 'Android\'s App Restriction Schema',
154 metavar='FILE')
156 (opts, args) = parser.parse_args()
158 if len(args) != 3:
159 print 'exactly platform, chromium_os flag and input file must be specified.'
160 parser.print_help()
161 return 2
163 os = args[0]
164 is_chromium_os = args[1] == '1'
165 template_file_name = args[2]
167 template_file_contents = _LoadJSONFile(template_file_name)
168 policy_details = [ PolicyDetails(policy, os, is_chromium_os)
169 for policy in _Flatten(template_file_contents) ]
170 sorted_policy_details = sorted(policy_details, key=lambda policy: policy.name)
172 def GenerateFile(path, writer, sorted=False, xml=False):
173 if path:
174 with open(path, 'w') as f:
175 _OutputGeneratedWarningHeader(f, template_file_name, xml)
176 writer(sorted and sorted_policy_details or policy_details, os, f)
178 GenerateFile(opts.header_path, _WritePolicyConstantHeader, sorted=True)
179 GenerateFile(opts.source_path, _WritePolicyConstantSource, sorted=True)
180 GenerateFile(opts.cloud_policy_proto_path, _WriteCloudPolicyProtobuf)
181 GenerateFile(opts.chrome_settings_proto_path, _WriteChromeSettingsProtobuf)
182 GenerateFile(opts.cloud_policy_decoder_path, _WriteCloudPolicyDecoder)
184 if os == 'android':
185 GenerateFile(opts.app_restrictions_path, _WriteAppRestrictions, xml=True)
187 return 0
190 #------------------ shared helpers ---------------------------------#
192 def _OutputGeneratedWarningHeader(f, template_file_path, xml_style):
193 left_margin = '//'
194 if xml_style:
195 left_margin = ' '
196 f.write('<?xml version="1.0" encoding="utf-8"?>\n'
197 '<!--\n')
198 else:
199 f.write('//\n')
201 f.write(left_margin + ' DO NOT MODIFY THIS FILE DIRECTLY!\n')
202 f.write(left_margin + ' IT IS GENERATED BY generate_policy_source.py\n')
203 f.write(left_margin + ' FROM ' + template_file_path + '\n')
205 if xml_style:
206 f.write('-->\n\n')
207 else:
208 f.write(left_margin + '\n\n')
211 COMMENT_WRAPPER = textwrap.TextWrapper()
212 COMMENT_WRAPPER.width = 80
213 COMMENT_WRAPPER.initial_indent = '// '
214 COMMENT_WRAPPER.subsequent_indent = '// '
215 COMMENT_WRAPPER.replace_whitespace = False
218 # Writes a comment, each line prefixed by // and wrapped to 80 spaces.
219 def _OutputComment(f, comment):
220 for line in comment.splitlines():
221 if len(line) == 0:
222 f.write('//')
223 else:
224 f.write(COMMENT_WRAPPER.fill(line))
225 f.write('\n')
228 # Returns an iterator over all the policies in |template_file_contents|.
229 def _Flatten(template_file_contents):
230 for policy in template_file_contents['policy_definitions']:
231 if policy['type'] == 'group':
232 for sub_policy in policy['policies']:
233 yield sub_policy
234 else:
235 yield policy
238 def _LoadJSONFile(json_file):
239 with open(json_file, 'r') as f:
240 text = f.read()
241 return eval(text)
244 #------------------ policy constants header ------------------------#
246 def _WritePolicyConstantHeader(policies, os, f):
247 f.write('#ifndef CHROME_COMMON_POLICY_CONSTANTS_H_\n'
248 '#define CHROME_COMMON_POLICY_CONSTANTS_H_\n'
249 '\n'
250 '#include <string>\n'
251 '\n'
252 '#include "base/basictypes.h"\n'
253 '#include "base/values.h"\n'
254 '#include "components/policy/core/common/policy_details.h"\n'
255 '#include "components/policy/core/common/policy_map.h"\n'
256 '\n'
257 'namespace policy {\n'
258 '\n'
259 'namespace internal {\n'
260 'struct SchemaData;\n'
261 '}\n\n')
263 if os == 'win':
264 f.write('// The windows registry path where Chrome policy '
265 'configuration resides.\n'
266 'extern const wchar_t kRegistryChromePolicyKey[];\n')
268 f.write('#if defined (OS_CHROMEOS)\n'
269 '// Sets default values for enterprise users.\n'
270 'void SetEnterpriseUsersDefaults(PolicyMap* policy_map);\n'
271 '#endif\n'
272 '\n'
273 '// Returns the PolicyDetails for |policy| if |policy| is a known\n'
274 '// Chrome policy, otherwise returns NULL.\n'
275 'const PolicyDetails* GetChromePolicyDetails('
276 'const std::string& policy);\n'
277 '\n'
278 '// Returns the schema data of the Chrome policy schema.\n'
279 'const internal::SchemaData* GetChromeSchemaData();\n'
280 '\n')
281 f.write('// Key names for the policy settings.\n'
282 'namespace key {\n\n')
283 for policy in policies:
284 # TODO(joaodasilva): Include only supported policies in
285 # configuration_policy_handler.cc and configuration_policy_handler_list.cc
286 # so that these names can be conditional on 'policy.is_supported'.
287 # http://crbug.com/223616
288 f.write('extern const char k' + policy.name + '[];\n')
289 f.write('\n} // namespace key\n\n'
290 '} // namespace policy\n\n'
291 '#endif // CHROME_COMMON_POLICY_CONSTANTS_H_\n')
294 #------------------ policy constants source ------------------------#
296 # A mapping of the simple schema types to base::Value::Types.
297 SIMPLE_SCHEMA_NAME_MAP = {
298 'boolean': 'TYPE_BOOLEAN',
299 'integer': 'TYPE_INTEGER',
300 'null' : 'TYPE_NULL',
301 'number' : 'TYPE_DOUBLE',
302 'string' : 'TYPE_STRING',
305 class SchemaNodesGenerator:
306 """Builds the internal structs to represent a JSON schema."""
308 def __init__(self, shared_strings):
309 """Creates a new generator.
311 |shared_strings| is a map of strings to a C expression that evaluates to
312 that string at runtime. This mapping can be used to reuse existing string
313 constants."""
314 self.shared_strings = shared_strings
315 self.schema_nodes = []
316 self.property_nodes = []
317 self.properties_nodes = []
318 self.restriction_nodes = []
319 self.int_enums = []
320 self.string_enums = []
321 self.simple_types = {
322 'boolean': None,
323 'integer': None,
324 'null': None,
325 'number': None,
326 'string': None,
328 self.stringlist_type = None
329 self.ranges = {}
330 self.id_map = {}
332 def GetString(self, s):
333 if s in self.shared_strings:
334 return self.shared_strings[s]
335 # Generate JSON escaped string, which is slightly different from desired
336 # C/C++ escaped string. Known differences includes unicode escaping format.
337 return json.dumps(s)
339 def AppendSchema(self, type, extra, comment=''):
340 index = len(self.schema_nodes)
341 self.schema_nodes.append((type, extra, comment))
342 return index
344 def AppendRestriction(self, first, second):
345 r = (str(first), str(second))
346 if not r in self.ranges:
347 self.ranges[r] = len(self.restriction_nodes)
348 self.restriction_nodes.append(r)
349 return self.ranges[r]
351 def GetSimpleType(self, name):
352 if self.simple_types[name] == None:
353 self.simple_types[name] = self.AppendSchema(
354 SIMPLE_SCHEMA_NAME_MAP[name],
356 'simple type: ' + name)
357 return self.simple_types[name]
359 def GetStringList(self):
360 if self.stringlist_type == None:
361 self.stringlist_type = self.AppendSchema(
362 'TYPE_LIST',
363 self.GetSimpleType('string'),
364 'simple type: stringlist')
365 return self.stringlist_type
367 def SchemaHaveRestriction(self, schema):
368 return any(keyword in schema for keyword in
369 ['minimum', 'maximum', 'enum', 'pattern'])
371 def IsConsecutiveInterval(self, seq):
372 sortedSeq = sorted(seq)
373 return all(sortedSeq[i] + 1 == sortedSeq[i + 1]
374 for i in xrange(len(sortedSeq) - 1))
376 def GetEnumIntegerType(self, schema, name):
377 assert all(type(x) == int for x in schema['enum'])
378 possible_values = schema['enum']
379 if self.IsConsecutiveInterval(possible_values):
380 index = self.AppendRestriction(max(possible_values), min(possible_values))
381 return self.AppendSchema('TYPE_INTEGER', index,
382 'integer with enumeration restriction (use range instead): %s' % name)
383 offset_begin = len(self.int_enums)
384 self.int_enums += possible_values
385 offset_end = len(self.int_enums)
386 return self.AppendSchema('TYPE_INTEGER',
387 self.AppendRestriction(offset_begin, offset_end),
388 'integer with enumeration restriction: %s' % name)
390 def GetEnumStringType(self, schema, name):
391 assert all(type(x) == str for x in schema['enum'])
392 offset_begin = len(self.string_enums)
393 self.string_enums += schema['enum']
394 offset_end = len(self.string_enums)
395 return self.AppendSchema('TYPE_STRING',
396 self.AppendRestriction(offset_begin, offset_end),
397 'string with enumeration restriction: %s' % name)
399 def GetEnumType(self, schema, name):
400 if len(schema['enum']) == 0:
401 raise RuntimeError('Empty enumeration in %s' % name)
402 elif schema['type'] == 'integer':
403 return self.GetEnumIntegerType(schema, name)
404 elif schema['type'] == 'string':
405 return self.GetEnumStringType(schema, name)
406 else:
407 raise RuntimeError('Unknown enumeration type in %s' % name)
409 def GetPatternType(self, schema, name):
410 if schema['type'] != 'string':
411 raise RuntimeError('Unknown pattern type in %s' % name)
412 pattern = schema['pattern']
413 # Try to compile the pattern to validate it, note that the syntax used
414 # here might be slightly different from re2.
415 # TODO(binjin): Add a python wrapper of re2 and use it here.
416 re.compile(pattern)
417 index = len(self.string_enums);
418 self.string_enums.append(pattern);
419 return self.AppendSchema('TYPE_STRING',
420 self.AppendRestriction(index, index),
421 'string with pattern restriction: %s' % name);
423 def GetRangedType(self, schema, name):
424 if schema['type'] != 'integer':
425 raise RuntimeError('Unknown ranged type in %s' % name)
426 min_value_set, max_value_set = False, False
427 if 'minimum' in schema:
428 min_value = int(schema['minimum'])
429 min_value_set = True
430 if 'maximum' in schema:
431 max_value = int(schema['minimum'])
432 max_value_set = True
433 if min_value_set and max_value_set and min_value > max_value:
434 raise RuntimeError('Invalid ranged type in %s' % name)
435 index = self.AppendRestriction(
436 str(max_value) if max_value_set else 'INT_MAX',
437 str(min_value) if min_value_set else 'INT_MIN')
438 return self.AppendSchema('TYPE_INTEGER',
439 index,
440 'integer with ranged restriction: %s' % name)
442 def Generate(self, schema, name):
443 """Generates the structs for the given schema.
445 |schema|: a valid JSON schema in a dictionary.
446 |name|: the name of the current node, for the generated comments."""
447 if schema.has_key('$ref'):
448 if schema.has_key('id'):
449 raise RuntimeError("Schemas with a $ref can't have an id")
450 if not isinstance(schema['$ref'], types.StringTypes):
451 raise RuntimeError("$ref attribute must be a string")
452 return schema['$ref']
453 if schema['type'] in self.simple_types:
454 if not self.SchemaHaveRestriction(schema):
455 # Simple types use shared nodes.
456 return self.GetSimpleType(schema['type'])
457 elif 'enum' in schema:
458 return self.GetEnumType(schema, name)
459 elif 'pattern' in schema:
460 return self.GetPatternType(schema, name)
461 else:
462 return self.GetRangedType(schema, name)
464 if schema['type'] == 'array':
465 # Special case for lists of strings, which is a common policy type.
466 # The 'type' may be missing if the schema has a '$ref' attribute.
467 if schema['items'].get('type', '') == 'string':
468 return self.GetStringList()
469 return self.AppendSchema('TYPE_LIST',
470 self.GenerateAndCollectID(schema['items'], 'items of ' + name))
471 elif schema['type'] == 'object':
472 # Reserve an index first, so that dictionaries come before their
473 # properties. This makes sure that the root node is the first in the
474 # SchemaNodes array.
475 index = self.AppendSchema('TYPE_DICTIONARY', -1)
477 if 'additionalProperties' in schema:
478 additionalProperties = self.GenerateAndCollectID(
479 schema['additionalProperties'],
480 'additionalProperties of ' + name)
481 else:
482 additionalProperties = -1
484 # Properties must be sorted by name, for the binary search lookup.
485 # Note that |properties| must be evaluated immediately, so that all the
486 # recursive calls to Generate() append the necessary child nodes; if
487 # |properties| were a generator then this wouldn't work.
488 sorted_properties = sorted(schema.get('properties', {}).items())
489 properties = [
490 (self.GetString(key), self.GenerateAndCollectID(subschema, key))
491 for key, subschema in sorted_properties ]
493 pattern_properties = []
494 for pattern, subschema in schema.get('patternProperties', {}).items():
495 pattern_properties.append((self.GetString(pattern),
496 self.GenerateAndCollectID(subschema, pattern)));
498 begin = len(self.property_nodes)
499 self.property_nodes += properties
500 end = len(self.property_nodes)
501 self.property_nodes += pattern_properties
502 pattern_end = len(self.property_nodes)
504 if index == 0:
505 self.root_properties_begin = begin
506 self.root_properties_end = end
508 extra = len(self.properties_nodes)
509 self.properties_nodes.append((begin, end, pattern_end,
510 additionalProperties, name))
512 # Set the right data at |index| now.
513 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name)
514 return index
515 else:
516 assert False
518 def GenerateAndCollectID(self, schema, name):
519 """A wrapper of Generate(), will take the return value, check and add 'id'
520 attribute to self.id_map. The wrapper needs to be used for every call to
521 Generate().
523 index = self.Generate(schema, name)
524 if not schema.has_key('id'):
525 return index
526 id_str = schema['id']
527 if self.id_map.has_key(id_str):
528 raise RuntimeError('Duplicated id: ' + id_str)
529 self.id_map[id_str] = index
530 return index
532 def Write(self, f):
533 """Writes the generated structs to the given file.
535 |f| an open file to write to."""
536 f.write('const internal::SchemaNode kSchemas[] = {\n'
537 '// Type Extra\n')
538 for type, extra, comment in self.schema_nodes:
539 type += ','
540 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment))
541 f.write('};\n\n')
543 if self.property_nodes:
544 f.write('const internal::PropertyNode kPropertyNodes[] = {\n'
545 '// Property #Schema\n')
546 for key, schema in self.property_nodes:
547 key += ','
548 f.write(' { %-50s %6d },\n' % (key, schema))
549 f.write('};\n\n')
551 if self.properties_nodes:
552 f.write('const internal::PropertiesNode kProperties[] = {\n'
553 '// Begin End PatternEnd Additional Properties\n')
554 for node in self.properties_nodes:
555 f.write(' { %5d, %5d, %10d, %5d }, // %s\n' % node)
556 f.write('};\n\n')
558 if self.restriction_nodes:
559 f.write('const internal::RestrictionNode kRestrictionNodes[] = {\n')
560 f.write('// FIRST, SECOND\n')
561 for first, second in self.restriction_nodes:
562 f.write(' {{ %-8s %4s}},\n' % (first + ',', second))
563 f.write('};\n\n')
565 if self.int_enums:
566 f.write('const int kIntegerEnumerations[] = {\n')
567 for possible_values in self.int_enums:
568 f.write(' %d,\n' % possible_values)
569 f.write('};\n\n')
571 if self.string_enums:
572 f.write('const char* kStringEnumerations[] = {\n')
573 for possible_values in self.string_enums:
574 f.write(' %s,\n' % self.GetString(possible_values))
575 f.write('};\n\n')
577 f.write('const internal::SchemaData kChromeSchemaData = {\n'
578 ' kSchemas,\n')
579 f.write(' kPropertyNodes,\n' if self.property_nodes else ' NULL,\n')
580 f.write(' kProperties,\n' if self.properties_nodes else ' NULL,\n')
581 f.write(' kRestrictionNodes,\n' if self.restriction_nodes else ' NULL,\n')
582 f.write(' kIntegerEnumerations,\n' if self.int_enums else ' NULL,\n')
583 f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n')
584 f.write('};\n\n')
586 def GetByID(self, id_str):
587 if not isinstance(id_str, types.StringTypes):
588 return id_str
589 if not self.id_map.has_key(id_str):
590 raise RuntimeError('Invalid $ref: ' + id_str)
591 return self.id_map[id_str]
593 def ResolveID(self, index, params):
594 return params[:index] + (self.GetByID(params[index]),) + params[index + 1:]
596 def ResolveReferences(self):
597 """Resolve reference mapping, required to be called after Generate()
599 After calling Generate(), the type of indices used in schema structures
600 might be either int or string. An int type suggests that it's a resolved
601 index, but for string type it's unresolved. Resolving a reference is as
602 simple as looking up for corresponding ID in self.id_map, and replace the
603 old index with the mapped index.
605 self.schema_nodes = map(partial(self.ResolveID, 1), self.schema_nodes)
606 self.property_nodes = map(partial(self.ResolveID, 1), self.property_nodes)
607 self.properties_nodes = map(partial(self.ResolveID, 3),
608 self.properties_nodes)
610 def _WritePolicyConstantSource(policies, os, f):
611 f.write('#include "policy/policy_constants.h"\n'
612 '\n'
613 '#include <algorithm>\n'
614 '#include <climits>\n'
615 '\n'
616 '#include "base/logging.h"\n'
617 '#include "components/policy/core/common/schema_internal.h"\n'
618 '\n'
619 'namespace policy {\n'
620 '\n'
621 'namespace {\n'
622 '\n')
624 # Generate the Chrome schema.
625 chrome_schema = {
626 'type': 'object',
627 'properties': {},
629 shared_strings = {}
630 for policy in policies:
631 shared_strings[policy.name] = "key::k%s" % policy.name
632 if policy.is_supported:
633 chrome_schema['properties'][policy.name] = policy.schema
635 # Note: this list must be kept in sync with the known property list of the
636 # Chrome schema, so that binary seaching in the PropertyNode array gets the
637 # right index on this array as well. See the implementation of
638 # GetChromePolicyDetails() below.
639 f.write('const PolicyDetails kChromePolicyDetails[] = {\n'
640 '// is_deprecated is_device_policy id max_external_data_size\n')
641 for policy in policies:
642 if policy.is_supported:
643 f.write(' { %-14s %-16s %3s, %24s },\n' % (
644 'true,' if policy.is_deprecated else 'false,',
645 'true,' if policy.is_device_only else 'false,',
646 policy.id,
647 policy.max_size))
648 f.write('};\n\n')
650 schema_generator = SchemaNodesGenerator(shared_strings)
651 schema_generator.GenerateAndCollectID(chrome_schema, 'root node')
652 schema_generator.ResolveReferences()
653 schema_generator.Write(f)
655 f.write('bool CompareKeys(const internal::PropertyNode& node,\n'
656 ' const std::string& key) {\n'
657 ' return node.key < key;\n'
658 '}\n\n')
660 f.write('} // namespace\n\n')
662 if os == 'win':
663 f.write('#if defined(GOOGLE_CHROME_BUILD)\n'
664 'const wchar_t kRegistryChromePolicyKey[] = '
665 'L"' + CHROME_POLICY_KEY + '";\n'
666 '#else\n'
667 'const wchar_t kRegistryChromePolicyKey[] = '
668 'L"' + CHROMIUM_POLICY_KEY + '";\n'
669 '#endif\n\n')
671 f.write('const internal::SchemaData* GetChromeSchemaData() {\n'
672 ' return &kChromeSchemaData;\n'
673 '}\n\n')
675 f.write('#if defined (OS_CHROMEOS)\n'
676 'void SetEnterpriseUsersDefaults(PolicyMap* policy_map) {\n')
678 for policy in policies:
679 if policy.has_enterprise_default:
680 if policy.policy_type == 'TYPE_BOOLEAN':
681 creation_expression = 'new base::FundamentalValue(%s)' %\
682 ('true' if policy.enterprise_default else 'false')
683 elif policy.policy_type == 'TYPE_INTEGER':
684 creation_expression = 'new base::FundamentalValue(%s)' %\
685 policy.enterprise_default
686 elif policy.policy_type == 'TYPE_STRING':
687 creation_expression = 'new base::StringValue("%s")' %\
688 policy.enterprise_default
689 else:
690 raise RuntimeError('Type %s of policy %s is not supported at '
691 'enterprise defaults' % (policy.policy_type,
692 policy.name))
693 f.write(' if (!policy_map->Get(key::k%s)) {\n'
694 ' policy_map->Set(key::k%s,\n'
695 ' POLICY_LEVEL_MANDATORY,\n'
696 ' POLICY_SCOPE_USER,\n'
697 ' %s,\n'
698 ' NULL);\n'
699 ' }\n' % (policy.name, policy.name, creation_expression))
701 f.write('}\n'
702 '#endif\n\n')
704 f.write('const PolicyDetails* GetChromePolicyDetails('
705 'const std::string& policy) {\n'
706 ' // First index in kPropertyNodes of the Chrome policies.\n'
707 ' static const int begin_index = %s;\n'
708 ' // One-past-the-end of the Chrome policies in kPropertyNodes.\n'
709 ' static const int end_index = %s;\n' %
710 (schema_generator.root_properties_begin,
711 schema_generator.root_properties_end))
712 f.write(' const internal::PropertyNode* begin =\n'
713 ' kPropertyNodes + begin_index;\n'
714 ' const internal::PropertyNode* end = kPropertyNodes + end_index;\n'
715 ' const internal::PropertyNode* it =\n'
716 ' std::lower_bound(begin, end, policy, CompareKeys);\n'
717 ' if (it == end || it->key != policy)\n'
718 ' return NULL;\n'
719 ' // This relies on kPropertyNodes from begin_index to end_index\n'
720 ' // having exactly the same policies (and in the same order) as\n'
721 ' // kChromePolicyDetails, so that binary searching on the first\n'
722 ' // gets the same results as a binary search on the second would.\n'
723 ' // However, kPropertyNodes has the policy names and\n'
724 ' // kChromePolicyDetails doesn\'t, so we obtain the index into\n'
725 ' // the second array by searching the first to avoid duplicating\n'
726 ' // the policy name pointers.\n'
727 ' // Offsetting |it| from |begin| here obtains the index we\'re\n'
728 ' // looking for.\n'
729 ' size_t index = it - begin;\n'
730 ' CHECK_LT(index, arraysize(kChromePolicyDetails));\n'
731 ' return kChromePolicyDetails + index;\n'
732 '}\n\n')
734 f.write('namespace key {\n\n')
735 for policy in policies:
736 # TODO(joaodasilva): Include only supported policies in
737 # configuration_policy_handler.cc and configuration_policy_handler_list.cc
738 # so that these names can be conditional on 'policy.is_supported'.
739 # http://crbug.com/223616
740 f.write('const char k{name}[] = "{name}";\n'.format(name=policy.name))
741 f.write('\n} // namespace key\n\n'
742 '} // namespace policy\n')
745 #------------------ policy protobufs --------------------------------#
747 CHROME_SETTINGS_PROTO_HEAD = '''
748 syntax = "proto2";
750 option optimize_for = LITE_RUNTIME;
752 package enterprise_management;
754 // For StringList and PolicyOptions.
755 import "cloud_policy.proto";
760 CLOUD_POLICY_PROTO_HEAD = '''
761 syntax = "proto2";
763 option optimize_for = LITE_RUNTIME;
765 package enterprise_management;
767 message StringList {
768 repeated string entries = 1;
771 message PolicyOptions {
772 enum PolicyMode {
773 // The given settings are applied regardless of user choice.
774 MANDATORY = 0;
775 // The user may choose to override the given settings.
776 RECOMMENDED = 1;
777 // No policy value is present and the policy should be ignored.
778 UNSET = 2;
780 optional PolicyMode mode = 1 [default = MANDATORY];
783 message BooleanPolicyProto {
784 optional PolicyOptions policy_options = 1;
785 optional bool value = 2;
788 message IntegerPolicyProto {
789 optional PolicyOptions policy_options = 1;
790 optional int64 value = 2;
793 message StringPolicyProto {
794 optional PolicyOptions policy_options = 1;
795 optional string value = 2;
798 message StringListPolicyProto {
799 optional PolicyOptions policy_options = 1;
800 optional StringList value = 2;
806 # Field IDs [1..RESERVED_IDS] will not be used in the wrapping protobuf.
807 RESERVED_IDS = 2
810 def _WritePolicyProto(f, policy, fields):
811 _OutputComment(f, policy.caption + '\n\n' + policy.desc)
812 if policy.items is not None:
813 _OutputComment(f, '\nValid values:')
814 for item in policy.items:
815 _OutputComment(f, ' %s: %s' % (str(item.value), item.caption))
816 if policy.policy_type == 'TYPE_DICTIONARY':
817 _OutputComment(f, '\nValue schema:\n%s' %
818 json.dumps(policy.schema, sort_keys=True, indent=4,
819 separators=(',', ': ')))
820 _OutputComment(f, '\nSupported on: %s' % ', '.join(policy.platforms))
821 if policy.can_be_recommended and not policy.can_be_mandatory:
822 _OutputComment(f, '\nNote: this policy must have a RECOMMENDED ' +\
823 'PolicyMode set in PolicyOptions.')
824 f.write('message %sProto {\n' % policy.name)
825 f.write(' optional PolicyOptions policy_options = 1;\n')
826 f.write(' optional %s %s = 2;\n' % (policy.protobuf_type, policy.name))
827 f.write('}\n\n')
828 fields += [ ' optional %sProto %s = %s;\n' %
829 (policy.name, policy.name, policy.id + RESERVED_IDS) ]
832 def _WriteChromeSettingsProtobuf(policies, os, f):
833 f.write(CHROME_SETTINGS_PROTO_HEAD)
835 fields = []
836 f.write('// PBs for individual settings.\n\n')
837 for policy in policies:
838 # Note: this protobuf also gets the unsupported policies, since it's an
839 # exaustive list of all the supported user policies on any platform.
840 if not policy.is_device_only:
841 _WritePolicyProto(f, policy, fields)
843 f.write('// --------------------------------------------------\n'
844 '// Big wrapper PB containing the above groups.\n\n'
845 'message ChromeSettingsProto {\n')
846 f.write(''.join(fields))
847 f.write('}\n\n')
850 def _WriteCloudPolicyProtobuf(policies, os, f):
851 f.write(CLOUD_POLICY_PROTO_HEAD)
852 f.write('message CloudPolicySettings {\n')
853 for policy in policies:
854 if policy.is_supported and not policy.is_device_only:
855 f.write(' optional %sPolicyProto %s = %s;\n' %
856 (policy.policy_protobuf_type, policy.name,
857 policy.id + RESERVED_IDS))
858 f.write('}\n\n')
861 #------------------ protobuf decoder -------------------------------#
863 CPP_HEAD = '''
864 #include <limits>
865 #include <string>
867 #include "base/basictypes.h"
868 #include "base/callback.h"
869 #include "base/json/json_reader.h"
870 #include "base/logging.h"
871 #include "base/memory/scoped_ptr.h"
872 #include "base/memory/weak_ptr.h"
873 #include "base/values.h"
874 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
875 #include "components/policy/core/common/external_data_fetcher.h"
876 #include "components/policy/core/common/policy_map.h"
877 #include "policy/policy_constants.h"
878 #include "policy/proto/cloud_policy.pb.h"
880 using google::protobuf::RepeatedPtrField;
882 namespace policy {
884 namespace em = enterprise_management;
886 base::Value* DecodeIntegerValue(google::protobuf::int64 value) {
887 if (value < std::numeric_limits<int>::min() ||
888 value > std::numeric_limits<int>::max()) {
889 LOG(WARNING) << "Integer value " << value
890 << " out of numeric limits, ignoring.";
891 return NULL;
894 return new base::FundamentalValue(static_cast<int>(value));
897 base::ListValue* DecodeStringList(const em::StringList& string_list) {
898 base::ListValue* list_value = new base::ListValue;
899 RepeatedPtrField<std::string>::const_iterator entry;
900 for (entry = string_list.entries().begin();
901 entry != string_list.entries().end(); ++entry) {
902 list_value->AppendString(*entry);
904 return list_value;
907 base::Value* DecodeJson(const std::string& json) {
908 scoped_ptr<base::Value> root(
909 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS));
911 if (!root)
912 LOG(WARNING) << "Invalid JSON string, ignoring: " << json;
914 // Accept any Value type that parsed as JSON, and leave it to the handler to
915 // convert and check the concrete type.
916 return root.release();
919 void DecodePolicy(const em::CloudPolicySettings& policy,
920 base::WeakPtr<CloudExternalDataManager> external_data_manager,
921 PolicyMap* map) {
925 CPP_FOOT = '''}
927 } // namespace policy
931 def _CreateValue(type, arg):
932 if type == 'TYPE_BOOLEAN':
933 return 'new base::FundamentalValue(%s)' % arg
934 elif type == 'TYPE_INTEGER':
935 return 'DecodeIntegerValue(%s)' % arg
936 elif type == 'TYPE_STRING':
937 return 'new base::StringValue(%s)' % arg
938 elif type == 'TYPE_LIST':
939 return 'DecodeStringList(%s)' % arg
940 elif type == 'TYPE_DICTIONARY' or type == 'TYPE_EXTERNAL':
941 return 'DecodeJson(%s)' % arg
942 else:
943 raise NotImplementedError('Unknown type %s' % type)
946 def _CreateExternalDataFetcher(type, name):
947 if type == 'TYPE_EXTERNAL':
948 return 'new ExternalDataFetcher(external_data_manager, key::k%s)' % name
949 return 'NULL'
952 def _WritePolicyCode(f, policy):
953 membername = policy.name.lower()
954 proto_type = '%sPolicyProto' % policy.policy_protobuf_type
955 f.write(' if (policy.has_%s()) {\n' % membername)
956 f.write(' const em::%s& policy_proto = policy.%s();\n' %
957 (proto_type, membername))
958 f.write(' if (policy_proto.has_value()) {\n')
959 f.write(' PolicyLevel level = POLICY_LEVEL_MANDATORY;\n'
960 ' bool do_set = true;\n'
961 ' if (policy_proto.has_policy_options()) {\n'
962 ' do_set = false;\n'
963 ' switch(policy_proto.policy_options().mode()) {\n'
964 ' case em::PolicyOptions::MANDATORY:\n'
965 ' do_set = true;\n'
966 ' level = POLICY_LEVEL_MANDATORY;\n'
967 ' break;\n'
968 ' case em::PolicyOptions::RECOMMENDED:\n'
969 ' do_set = true;\n'
970 ' level = POLICY_LEVEL_RECOMMENDED;\n'
971 ' break;\n'
972 ' case em::PolicyOptions::UNSET:\n'
973 ' break;\n'
974 ' }\n'
975 ' }\n'
976 ' if (do_set) {\n')
977 f.write(' base::Value* value = %s;\n' %
978 (_CreateValue(policy.policy_type, 'policy_proto.value()')))
979 # TODO(bartfab): |value| == NULL indicates that the policy value could not be
980 # parsed successfully. Surface such errors in the UI.
981 f.write(' if (value) {\n')
982 f.write(' ExternalDataFetcher* external_data_fetcher = %s;\n' %
983 _CreateExternalDataFetcher(policy.policy_type, policy.name))
984 f.write(' map->Set(key::k%s, level, POLICY_SCOPE_USER,\n' %
985 policy.name)
986 f.write(' value, external_data_fetcher);\n'
987 ' }\n'
988 ' }\n'
989 ' }\n'
990 ' }\n')
993 def _WriteCloudPolicyDecoder(policies, os, f):
994 f.write(CPP_HEAD)
995 for policy in policies:
996 if policy.is_supported and not policy.is_device_only:
997 _WritePolicyCode(f, policy)
998 f.write(CPP_FOOT)
1001 def _WriteAppRestrictions(policies, os, f):
1003 def WriteRestrictionCommon(key):
1004 f.write(' <restriction\n'
1005 ' android:key="%s"\n' % key)
1006 f.write(' android:title="@string/%sTitle"\n' % key)
1007 f.write(' android:description="@string/%sDesc"\n' % key)
1009 def WriteItemsDefinition(key):
1010 f.write(' android:entries="@array/%sEntries"\n' % key)
1011 f.write(' android:entryValues="@array/%sValues"\n' % key)
1013 def WriteAppRestriction(policy):
1014 policy_name = policy.name
1015 WriteRestrictionCommon(policy_name)
1017 if policy.items is not None:
1018 WriteItemsDefinition(policy_name)
1020 f.write(' android:restrictionType="%s"/>' % policy.restriction_type)
1021 f.write('\n\n')
1023 # _WriteAppRestrictions body
1024 f.write('<restrictions xmlns:android="'
1025 'http://schemas.android.com/apk/res/android">\n\n')
1026 for policy in policies:
1027 if policy.is_supported and policy.restriction_type != 'invalid':
1028 WriteAppRestriction(policy)
1029 f.write('</restrictions>')
1031 if __name__ == '__main__':
1032 sys.exit(main())