Deprecate chrome.usb.requestAccess.
[chromium-blink-merge.git] / components / policy / tools / generate_policy_source.py
blobc3096c74928007307bce487fd07e907939153616
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
23 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome'
24 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium'
27 class PolicyDetails:
28 """Parses a policy template and caches all its details."""
30 # Maps policy types to a tuple with 3 other types:
31 # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy
32 # references external data
33 # - the equivalent Protobuf field type
34 # - the name of one of the protobufs for shared policy types
35 # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type
36 # that can also be used to represent lists of other JSON objects.
37 TYPE_MAP = {
38 'dict': ('TYPE_DICTIONARY', 'string', 'String'),
39 'external': ('TYPE_EXTERNAL', 'string', 'String'),
40 'int': ('TYPE_INTEGER', 'int64', 'Integer'),
41 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer'),
42 'list': ('TYPE_LIST', 'StringList', 'StringList'),
43 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean'),
44 'string': ('TYPE_STRING', 'string', 'String'),
45 'string-enum': ('TYPE_STRING', 'string', 'String'),
46 'string-enum-list': ('TYPE_LIST', 'StringList', 'StringList'),
49 class EnumItem:
50 def __init__(self, item):
51 self.caption = PolicyDetails._RemovePlaceholders(item['caption'])
52 self.value = item['value']
54 def __init__(self, policy, os, is_chromium_os):
55 self.id = policy['id']
56 self.name = policy['name']
57 features = policy.get('features', {})
58 self.can_be_recommended = features.get('can_be_recommended', False)
59 self.can_be_mandatory = features.get('can_be_mandatory', True)
60 self.is_deprecated = policy.get('deprecated', False)
61 self.is_device_only = policy.get('device_only', False)
62 self.schema = policy.get('schema', {})
63 self.has_enterprise_default = 'default_for_enterprise_users' in policy
64 if self.has_enterprise_default:
65 self.enterprise_default = policy['default_for_enterprise_users']
67 expected_platform = 'chrome_os' if is_chromium_os else os.lower()
68 self.platforms = []
69 for platform, version in [ p.split(':') for p in policy['supported_on'] ]:
70 if not version.endswith('-'):
71 continue
73 if platform.startswith('chrome.'):
74 platform_sub = platform[7:]
75 if platform_sub == '*':
76 self.platforms.extend(['win', 'mac', 'linux'])
77 else:
78 self.platforms.append(platform_sub)
79 else:
80 self.platforms.append(platform)
82 self.platforms.sort()
83 self.is_supported = expected_platform in self.platforms
85 if not PolicyDetails.TYPE_MAP.has_key(policy['type']):
86 raise NotImplementedError('Unknown policy type for %s: %s' %
87 (policy['name'], policy['type']))
88 self.policy_type, self.protobuf_type, self.policy_protobuf_type = \
89 PolicyDetails.TYPE_MAP[policy['type']]
90 self.schema = policy['schema']
92 self.desc = '\n'.join(
93 map(str.strip,
94 PolicyDetails._RemovePlaceholders(policy['desc']).splitlines()))
95 self.caption = PolicyDetails._RemovePlaceholders(policy['caption'])
96 self.max_size = policy.get('max_size', 0)
98 items = policy.get('items')
99 if items is None:
100 self.items = None
101 else:
102 self.items = [ PolicyDetails.EnumItem(entry) for entry in items ]
104 PH_PATTERN = re.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>')
106 # Simplistic grit placeholder stripper.
107 @staticmethod
108 def _RemovePlaceholders(text):
109 result = ''
110 pos = 0
111 for m in PolicyDetails.PH_PATTERN.finditer(text):
112 result += text[pos:m.start(0)]
113 result += m.group(2) or m.group(1)
114 pos = m.end(0)
115 result += text[pos:]
116 return result
119 def main():
120 parser = OptionParser(usage=__doc__)
121 parser.add_option('--pch', '--policy-constants-header', dest='header_path',
122 help='generate header file of policy constants',
123 metavar='FILE')
124 parser.add_option('--pcc', '--policy-constants-source', dest='source_path',
125 help='generate source file of policy constants',
126 metavar='FILE')
127 parser.add_option('--cpp', '--cloud-policy-protobuf',
128 dest='cloud_policy_proto_path',
129 help='generate cloud policy protobuf file',
130 metavar='FILE')
131 parser.add_option('--csp', '--chrome-settings-protobuf',
132 dest='chrome_settings_proto_path',
133 help='generate chrome settings protobuf file',
134 metavar='FILE')
135 parser.add_option('--cpd', '--cloud-policy-decoder',
136 dest='cloud_policy_decoder_path',
137 help='generate C++ code decoding the cloud policy protobuf',
138 metavar='FILE')
140 (opts, args) = parser.parse_args()
142 if len(args) != 3:
143 print 'exactly platform, chromium_os flag and input file must be specified.'
144 parser.print_help()
145 return 2
147 os = args[0]
148 is_chromium_os = args[1] == '1'
149 template_file_name = args[2]
151 template_file_contents = _LoadJSONFile(template_file_name)
152 policy_details = [ PolicyDetails(policy, os, is_chromium_os)
153 for policy in _Flatten(template_file_contents) ]
154 sorted_policy_details = sorted(policy_details, key=lambda policy: policy.name)
156 def GenerateFile(path, writer, sorted=False):
157 if path:
158 with open(path, 'w') as f:
159 _OutputGeneratedWarningHeader(f, template_file_name)
160 writer(sorted and sorted_policy_details or policy_details, os, f)
162 GenerateFile(opts.header_path, _WritePolicyConstantHeader, sorted=True)
163 GenerateFile(opts.source_path, _WritePolicyConstantSource, sorted=True)
164 GenerateFile(opts.cloud_policy_proto_path, _WriteCloudPolicyProtobuf)
165 GenerateFile(opts.chrome_settings_proto_path, _WriteChromeSettingsProtobuf)
166 GenerateFile(opts.cloud_policy_decoder_path, _WriteCloudPolicyDecoder)
168 return 0
171 #------------------ shared helpers ---------------------------------#
173 def _OutputGeneratedWarningHeader(f, template_file_path):
174 f.write('//\n'
175 '// DO NOT MODIFY THIS FILE DIRECTLY!\n'
176 '// IT IS GENERATED BY generate_policy_source.py\n'
177 '// FROM ' + template_file_path + '\n'
178 '//\n\n')
181 COMMENT_WRAPPER = textwrap.TextWrapper()
182 COMMENT_WRAPPER.width = 80
183 COMMENT_WRAPPER.initial_indent = '// '
184 COMMENT_WRAPPER.subsequent_indent = '// '
185 COMMENT_WRAPPER.replace_whitespace = False
188 # Writes a comment, each line prefixed by // and wrapped to 80 spaces.
189 def _OutputComment(f, comment):
190 for line in comment.splitlines():
191 if len(line) == 0:
192 f.write('//')
193 else:
194 f.write(COMMENT_WRAPPER.fill(line))
195 f.write('\n')
198 # Returns an iterator over all the policies in |template_file_contents|.
199 def _Flatten(template_file_contents):
200 for policy in template_file_contents['policy_definitions']:
201 if policy['type'] == 'group':
202 for sub_policy in policy['policies']:
203 yield sub_policy
204 else:
205 yield policy
208 def _LoadJSONFile(json_file):
209 with open(json_file, 'r') as f:
210 text = f.read()
211 return eval(text)
214 #------------------ policy constants header ------------------------#
216 def _WritePolicyConstantHeader(policies, os, f):
217 f.write('#ifndef CHROME_COMMON_POLICY_CONSTANTS_H_\n'
218 '#define CHROME_COMMON_POLICY_CONSTANTS_H_\n'
219 '\n'
220 '#include <string>\n'
221 '\n'
222 '#include "base/basictypes.h"\n'
223 '#include "base/values.h"\n'
224 '#include "components/policy/core/common/policy_details.h"\n'
225 '#include "components/policy/core/common/policy_map.h"\n'
226 '\n'
227 'namespace policy {\n'
228 '\n'
229 'namespace internal {\n'
230 'struct SchemaData;\n'
231 '}\n\n')
233 if os == 'win':
234 f.write('// The windows registry path where Chrome policy '
235 'configuration resides.\n'
236 'extern const wchar_t kRegistryChromePolicyKey[];\n')
238 f.write('#if defined (OS_CHROMEOS)\n'
239 '// Sets default values for enterprise users.\n'
240 'void SetEnterpriseUsersDefaults(PolicyMap* policy_map);\n'
241 '#endif\n'
242 '\n'
243 '// Returns the PolicyDetails for |policy| if |policy| is a known\n'
244 '// Chrome policy, otherwise returns NULL.\n'
245 'const PolicyDetails* GetChromePolicyDetails('
246 'const std::string& policy);\n'
247 '\n'
248 '// Returns the schema data of the Chrome policy schema.\n'
249 'const internal::SchemaData* GetChromeSchemaData();\n'
250 '\n')
251 f.write('// Key names for the policy settings.\n'
252 'namespace key {\n\n')
253 for policy in policies:
254 # TODO(joaodasilva): Include only supported policies in
255 # configuration_policy_handler.cc and configuration_policy_handler_list.cc
256 # so that these names can be conditional on 'policy.is_supported'.
257 # http://crbug.com/223616
258 f.write('extern const char k' + policy.name + '[];\n')
259 f.write('\n} // namespace key\n\n'
260 '} // namespace policy\n\n'
261 '#endif // CHROME_COMMON_POLICY_CONSTANTS_H_\n')
264 #------------------ policy constants source ------------------------#
266 # A mapping of the simple schema types to base::Value::Types.
267 SIMPLE_SCHEMA_NAME_MAP = {
268 'boolean': 'TYPE_BOOLEAN',
269 'integer': 'TYPE_INTEGER',
270 'null' : 'TYPE_NULL',
271 'number' : 'TYPE_DOUBLE',
272 'string' : 'TYPE_STRING',
275 class SchemaNodesGenerator:
276 """Builds the internal structs to represent a JSON schema."""
278 def __init__(self, shared_strings):
279 """Creates a new generator.
281 |shared_strings| is a map of strings to a C expression that evaluates to
282 that string at runtime. This mapping can be used to reuse existing string
283 constants."""
284 self.shared_strings = shared_strings
285 self.schema_nodes = []
286 self.property_nodes = []
287 self.properties_nodes = []
288 self.restriction_nodes = []
289 self.int_enums = []
290 self.string_enums = []
291 self.simple_types = {
292 'boolean': None,
293 'integer': None,
294 'null': None,
295 'number': None,
296 'string': None,
298 self.stringlist_type = None
299 self.ranges = {}
300 self.id_map = {}
302 def GetString(self, s):
303 if s in self.shared_strings:
304 return self.shared_strings[s]
305 # Generate JSON escaped string, which is slightly different from desired
306 # C/C++ escaped string. Known differences includes unicode escaping format.
307 return json.dumps(s)
309 def AppendSchema(self, type, extra, comment=''):
310 index = len(self.schema_nodes)
311 self.schema_nodes.append((type, extra, comment))
312 return index
314 def AppendRestriction(self, first, second):
315 r = (str(first), str(second))
316 if not r in self.ranges:
317 self.ranges[r] = len(self.restriction_nodes)
318 self.restriction_nodes.append(r)
319 return self.ranges[r]
321 def GetSimpleType(self, name):
322 if self.simple_types[name] == None:
323 self.simple_types[name] = self.AppendSchema(
324 SIMPLE_SCHEMA_NAME_MAP[name],
326 'simple type: ' + name)
327 return self.simple_types[name]
329 def GetStringList(self):
330 if self.stringlist_type == None:
331 self.stringlist_type = self.AppendSchema(
332 'TYPE_LIST',
333 self.GetSimpleType('string'),
334 'simple type: stringlist')
335 return self.stringlist_type
337 def SchemaHaveRestriction(self, schema):
338 return any(keyword in schema for keyword in
339 ['minimum', 'maximum', 'enum', 'pattern'])
341 def IsConsecutiveInterval(self, seq):
342 sortedSeq = sorted(seq)
343 return all(sortedSeq[i] + 1 == sortedSeq[i + 1]
344 for i in xrange(len(sortedSeq) - 1))
346 def GetEnumIntegerType(self, schema, name):
347 assert all(type(x) == int for x in schema['enum'])
348 possible_values = schema['enum']
349 if self.IsConsecutiveInterval(possible_values):
350 index = self.AppendRestriction(max(possible_values), min(possible_values))
351 return self.AppendSchema('TYPE_INTEGER', index,
352 'integer with enumeration restriction (use range instead): %s' % name)
353 offset_begin = len(self.int_enums)
354 self.int_enums += possible_values
355 offset_end = len(self.int_enums)
356 return self.AppendSchema('TYPE_INTEGER',
357 self.AppendRestriction(offset_begin, offset_end),
358 'integer with enumeration restriction: %s' % name)
360 def GetEnumStringType(self, schema, name):
361 assert all(type(x) == str for x in schema['enum'])
362 offset_begin = len(self.string_enums)
363 self.string_enums += schema['enum']
364 offset_end = len(self.string_enums)
365 return self.AppendSchema('TYPE_STRING',
366 self.AppendRestriction(offset_begin, offset_end),
367 'string with enumeration restriction: %s' % name)
369 def GetEnumType(self, schema, name):
370 if len(schema['enum']) == 0:
371 raise RuntimeError('Empty enumeration in %s' % name)
372 elif schema['type'] == 'integer':
373 return self.GetEnumIntegerType(schema, name)
374 elif schema['type'] == 'string':
375 return self.GetEnumStringType(schema, name)
376 else:
377 raise RuntimeError('Unknown enumeration type in %s' % name)
379 def GetPatternType(self, schema, name):
380 if schema['type'] != 'string':
381 raise RuntimeError('Unknown pattern type in %s' % name)
382 pattern = schema['pattern']
383 # Try to compile the pattern to validate it, note that the syntax used
384 # here might be slightly different from re2.
385 # TODO(binjin): Add a python wrapper of re2 and use it here.
386 re.compile(pattern)
387 index = len(self.string_enums);
388 self.string_enums.append(pattern);
389 return self.AppendSchema('TYPE_STRING',
390 self.AppendRestriction(index, index),
391 'string with pattern restriction: %s' % name);
393 def GetRangedType(self, schema, name):
394 if schema['type'] != 'integer':
395 raise RuntimeError('Unknown ranged type in %s' % name)
396 min_value_set, max_value_set = False, False
397 if 'minimum' in schema:
398 min_value = int(schema['minimum'])
399 min_value_set = True
400 if 'maximum' in schema:
401 max_value = int(schema['minimum'])
402 max_value_set = True
403 if min_value_set and max_value_set and min_value > max_value:
404 raise RuntimeError('Invalid ranged type in %s' % name)
405 index = self.AppendRestriction(
406 str(max_value) if max_value_set else 'INT_MAX',
407 str(min_value) if min_value_set else 'INT_MIN')
408 return self.AppendSchema('TYPE_INTEGER',
409 index,
410 'integer with ranged restriction: %s' % name)
412 def Generate(self, schema, name):
413 """Generates the structs for the given schema.
415 |schema|: a valid JSON schema in a dictionary.
416 |name|: the name of the current node, for the generated comments."""
417 if schema.has_key('$ref'):
418 if schema.has_key('id'):
419 raise RuntimeError("Schemas with a $ref can't have an id")
420 if not isinstance(schema['$ref'], types.StringTypes):
421 raise RuntimeError("$ref attribute must be a string")
422 return schema['$ref']
423 if schema['type'] in self.simple_types:
424 if not self.SchemaHaveRestriction(schema):
425 # Simple types use shared nodes.
426 return self.GetSimpleType(schema['type'])
427 elif 'enum' in schema:
428 return self.GetEnumType(schema, name)
429 elif 'pattern' in schema:
430 return self.GetPatternType(schema, name)
431 else:
432 return self.GetRangedType(schema, name)
434 if schema['type'] == 'array':
435 # Special case for lists of strings, which is a common policy type.
436 # The 'type' may be missing if the schema has a '$ref' attribute.
437 if schema['items'].get('type', '') == 'string':
438 return self.GetStringList()
439 return self.AppendSchema('TYPE_LIST',
440 self.GenerateAndCollectID(schema['items'], 'items of ' + name))
441 elif schema['type'] == 'object':
442 # Reserve an index first, so that dictionaries come before their
443 # properties. This makes sure that the root node is the first in the
444 # SchemaNodes array.
445 index = self.AppendSchema('TYPE_DICTIONARY', -1)
447 if 'additionalProperties' in schema:
448 additionalProperties = self.GenerateAndCollectID(
449 schema['additionalProperties'],
450 'additionalProperties of ' + name)
451 else:
452 additionalProperties = -1
454 # Properties must be sorted by name, for the binary search lookup.
455 # Note that |properties| must be evaluated immediately, so that all the
456 # recursive calls to Generate() append the necessary child nodes; if
457 # |properties| were a generator then this wouldn't work.
458 sorted_properties = sorted(schema.get('properties', {}).items())
459 properties = [
460 (self.GetString(key), self.GenerateAndCollectID(subschema, key))
461 for key, subschema in sorted_properties ]
463 pattern_properties = []
464 for pattern, subschema in schema.get('patternProperties', {}).items():
465 pattern_properties.append((self.GetString(pattern),
466 self.GenerateAndCollectID(subschema, pattern)));
468 begin = len(self.property_nodes)
469 self.property_nodes += properties
470 end = len(self.property_nodes)
471 self.property_nodes += pattern_properties
472 pattern_end = len(self.property_nodes)
474 if index == 0:
475 self.root_properties_begin = begin
476 self.root_properties_end = end
478 extra = len(self.properties_nodes)
479 self.properties_nodes.append((begin, end, pattern_end,
480 additionalProperties, name))
482 # Set the right data at |index| now.
483 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name)
484 return index
485 else:
486 assert False
488 def GenerateAndCollectID(self, schema, name):
489 """A wrapper of Generate(), will take the return value, check and add 'id'
490 attribute to self.id_map. The wrapper needs to be used for every call to
491 Generate().
493 index = self.Generate(schema, name)
494 if not schema.has_key('id'):
495 return index
496 id_str = schema['id']
497 if self.id_map.has_key(id_str):
498 raise RuntimeError('Duplicated id: ' + id_str)
499 self.id_map[id_str] = index
500 return index
502 def Write(self, f):
503 """Writes the generated structs to the given file.
505 |f| an open file to write to."""
506 f.write('const internal::SchemaNode kSchemas[] = {\n'
507 '// Type Extra\n')
508 for type, extra, comment in self.schema_nodes:
509 type += ','
510 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment))
511 f.write('};\n\n')
513 if self.property_nodes:
514 f.write('const internal::PropertyNode kPropertyNodes[] = {\n'
515 '// Property #Schema\n')
516 for key, schema in self.property_nodes:
517 key += ','
518 f.write(' { %-50s %6d },\n' % (key, schema))
519 f.write('};\n\n')
521 if self.properties_nodes:
522 f.write('const internal::PropertiesNode kProperties[] = {\n'
523 '// Begin End PatternEnd Additional Properties\n')
524 for node in self.properties_nodes:
525 f.write(' { %5d, %5d, %10d, %5d }, // %s\n' % node)
526 f.write('};\n\n')
528 if self.restriction_nodes:
529 f.write('const internal::RestrictionNode kRestrictionNodes[] = {\n')
530 f.write('// FIRST, SECOND\n')
531 for first, second in self.restriction_nodes:
532 f.write(' {{ %-8s %4s}},\n' % (first + ',', second))
533 f.write('};\n\n')
535 if self.int_enums:
536 f.write('const int kIntegerEnumerations[] = {\n')
537 for possible_values in self.int_enums:
538 f.write(' %d,\n' % possible_values)
539 f.write('};\n\n')
541 if self.string_enums:
542 f.write('const char* kStringEnumerations[] = {\n')
543 for possible_values in self.string_enums:
544 f.write(' %s,\n' % self.GetString(possible_values))
545 f.write('};\n\n')
547 f.write('const internal::SchemaData kChromeSchemaData = {\n'
548 ' kSchemas,\n')
549 f.write(' kPropertyNodes,\n' if self.property_nodes else ' NULL,\n')
550 f.write(' kProperties,\n' if self.properties_nodes else ' NULL,\n')
551 f.write(' kRestrictionNodes,\n' if self.restriction_nodes else ' NULL,\n')
552 f.write(' kIntegerEnumerations,\n' if self.int_enums else ' NULL,\n')
553 f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n')
554 f.write('};\n\n')
556 def GetByID(self, id_str):
557 if not isinstance(id_str, types.StringTypes):
558 return id_str
559 if not self.id_map.has_key(id_str):
560 raise RuntimeError('Invalid $ref: ' + id_str)
561 return self.id_map[id_str]
563 def ResolveID(self, index, params):
564 return params[:index] + (self.GetByID(params[index]),) + params[index+1:]
566 def ResolveReferences(self):
567 """Resolve reference mapping, required to be called after Generate()
569 After calling Generate(), the type of indices used in schema structures
570 might be either int or string. An int type suggests that it's a resolved
571 index, but for string type it's unresolved. Resolving a reference is as
572 simple as looking up for corresponding ID in self.id_map, and replace the
573 old index with the mapped index.
575 self.schema_nodes = map(partial(self.ResolveID, 1), self.schema_nodes)
576 self.property_nodes = map(partial(self.ResolveID, 1), self.property_nodes)
577 self.properties_nodes = map(partial(self.ResolveID, 3),
578 self.properties_nodes)
580 def _WritePolicyConstantSource(policies, os, f):
581 f.write('#include "policy/policy_constants.h"\n'
582 '\n'
583 '#include <algorithm>\n'
584 '#include <climits>\n'
585 '\n'
586 '#include "base/logging.h"\n'
587 '#include "components/policy/core/common/schema_internal.h"\n'
588 '\n'
589 'namespace policy {\n'
590 '\n'
591 'namespace {\n'
592 '\n')
594 # Generate the Chrome schema.
595 chrome_schema = {
596 'type': 'object',
597 'properties': {},
599 shared_strings = {}
600 for policy in policies:
601 shared_strings[policy.name] = "key::k%s" % policy.name
602 if policy.is_supported:
603 chrome_schema['properties'][policy.name] = policy.schema
605 # Note: this list must be kept in sync with the known property list of the
606 # Chrome schema, so that binary seaching in the PropertyNode array gets the
607 # right index on this array as well. See the implementation of
608 # GetChromePolicyDetails() below.
609 f.write('const PolicyDetails kChromePolicyDetails[] = {\n'
610 '// is_deprecated is_device_policy id max_external_data_size\n')
611 for policy in policies:
612 if policy.is_supported:
613 f.write(' { %-14s %-16s %3s, %24s },\n' % (
614 'true,' if policy.is_deprecated else 'false,',
615 'true,' if policy.is_device_only else 'false,',
616 policy.id,
617 policy.max_size))
618 f.write('};\n\n')
620 schema_generator = SchemaNodesGenerator(shared_strings)
621 schema_generator.GenerateAndCollectID(chrome_schema, 'root node')
622 schema_generator.ResolveReferences()
623 schema_generator.Write(f)
625 f.write('bool CompareKeys(const internal::PropertyNode& node,\n'
626 ' const std::string& key) {\n'
627 ' return node.key < key;\n'
628 '}\n\n')
630 f.write('} // namespace\n\n')
632 if os == 'win':
633 f.write('#if defined(GOOGLE_CHROME_BUILD)\n'
634 'const wchar_t kRegistryChromePolicyKey[] = '
635 'L"' + CHROME_POLICY_KEY + '";\n'
636 '#else\n'
637 'const wchar_t kRegistryChromePolicyKey[] = '
638 'L"' + CHROMIUM_POLICY_KEY + '";\n'
639 '#endif\n\n')
641 f.write('const internal::SchemaData* GetChromeSchemaData() {\n'
642 ' return &kChromeSchemaData;\n'
643 '}\n\n')
645 f.write('#if defined (OS_CHROMEOS)\n'
646 'void SetEnterpriseUsersDefaults(PolicyMap* policy_map) {\n')
648 for policy in policies:
649 if policy.has_enterprise_default:
650 if policy.policy_type == 'TYPE_BOOLEAN':
651 creation_expression = 'new base::FundamentalValue(%s)' %\
652 ('true' if policy.enterprise_default else 'false')
653 elif policy.policy_type == 'TYPE_INTEGER':
654 creation_expression = 'new base::FundamentalValue(%s)' %\
655 policy.enterprise_default
656 elif policy.policy_type == 'TYPE_STRING':
657 creation_expression = 'new base::StringValue("%s")' %\
658 policy.enterprise_default
659 else:
660 raise RuntimeError('Type %s of policy %s is not supported at '
661 'enterprise defaults' % (policy.policy_type,
662 policy.name))
663 f.write(' if (!policy_map->Get(key::k%s)) {\n'
664 ' policy_map->Set(key::k%s,\n'
665 ' POLICY_LEVEL_MANDATORY,\n'
666 ' POLICY_SCOPE_USER,\n'
667 ' %s,\n'
668 ' NULL);\n'
669 ' }\n' % (policy.name, policy.name, creation_expression))
671 f.write('}\n'
672 '#endif\n\n')
674 f.write('const PolicyDetails* GetChromePolicyDetails('
675 'const std::string& policy) {\n'
676 ' // First index in kPropertyNodes of the Chrome policies.\n'
677 ' static const int begin_index = %s;\n'
678 ' // One-past-the-end of the Chrome policies in kPropertyNodes.\n'
679 ' static const int end_index = %s;\n' %
680 (schema_generator.root_properties_begin,
681 schema_generator.root_properties_end))
682 f.write(' const internal::PropertyNode* begin =\n'
683 ' kPropertyNodes + begin_index;\n'
684 ' const internal::PropertyNode* end = kPropertyNodes + end_index;\n'
685 ' const internal::PropertyNode* it =\n'
686 ' std::lower_bound(begin, end, policy, CompareKeys);\n'
687 ' if (it == end || it->key != policy)\n'
688 ' return NULL;\n'
689 ' // This relies on kPropertyNodes from begin_index to end_index\n'
690 ' // having exactly the same policies (and in the same order) as\n'
691 ' // kChromePolicyDetails, so that binary searching on the first\n'
692 ' // gets the same results as a binary search on the second would.\n'
693 ' // However, kPropertyNodes has the policy names and\n'
694 ' // kChromePolicyDetails doesn\'t, so we obtain the index into\n'
695 ' // the second array by searching the first to avoid duplicating\n'
696 ' // the policy name pointers.\n'
697 ' // Offsetting |it| from |begin| here obtains the index we\'re\n'
698 ' // looking for.\n'
699 ' size_t index = it - begin;\n'
700 ' CHECK_LT(index, arraysize(kChromePolicyDetails));\n'
701 ' return kChromePolicyDetails + index;\n'
702 '}\n\n')
704 f.write('namespace key {\n\n')
705 for policy in policies:
706 # TODO(joaodasilva): Include only supported policies in
707 # configuration_policy_handler.cc and configuration_policy_handler_list.cc
708 # so that these names can be conditional on 'policy.is_supported'.
709 # http://crbug.com/223616
710 f.write('const char k{name}[] = "{name}";\n'.format(name=policy.name))
711 f.write('\n} // namespace key\n\n'
712 '} // namespace policy\n')
715 #------------------ policy protobufs --------------------------------#
717 CHROME_SETTINGS_PROTO_HEAD = '''
718 syntax = "proto2";
720 option optimize_for = LITE_RUNTIME;
722 package enterprise_management;
724 // For StringList and PolicyOptions.
725 import "cloud_policy.proto";
730 CLOUD_POLICY_PROTO_HEAD = '''
731 syntax = "proto2";
733 option optimize_for = LITE_RUNTIME;
735 package enterprise_management;
737 message StringList {
738 repeated string entries = 1;
741 message PolicyOptions {
742 enum PolicyMode {
743 // The given settings are applied regardless of user choice.
744 MANDATORY = 0;
745 // The user may choose to override the given settings.
746 RECOMMENDED = 1;
747 // No policy value is present and the policy should be ignored.
748 UNSET = 2;
750 optional PolicyMode mode = 1 [default = MANDATORY];
753 message BooleanPolicyProto {
754 optional PolicyOptions policy_options = 1;
755 optional bool value = 2;
758 message IntegerPolicyProto {
759 optional PolicyOptions policy_options = 1;
760 optional int64 value = 2;
763 message StringPolicyProto {
764 optional PolicyOptions policy_options = 1;
765 optional string value = 2;
768 message StringListPolicyProto {
769 optional PolicyOptions policy_options = 1;
770 optional StringList value = 2;
776 # Field IDs [1..RESERVED_IDS] will not be used in the wrapping protobuf.
777 RESERVED_IDS = 2
780 def _WritePolicyProto(f, policy, fields):
781 _OutputComment(f, policy.caption + '\n\n' + policy.desc)
782 if policy.items is not None:
783 _OutputComment(f, '\nValid values:')
784 for item in policy.items:
785 _OutputComment(f, ' %s: %s' % (str(item.value), item.caption))
786 if policy.policy_type == 'TYPE_DICTIONARY':
787 _OutputComment(f, '\nValue schema:\n%s' %
788 json.dumps(policy.schema, sort_keys=True, indent=4,
789 separators=(',', ': ')))
790 _OutputComment(f, '\nSupported on: %s' % ', '.join(policy.platforms))
791 if policy.can_be_recommended and not policy.can_be_mandatory:
792 _OutputComment(f, '\nNote: this policy must have a RECOMMENDED ' +\
793 'PolicyMode set in PolicyOptions.')
794 f.write('message %sProto {\n' % policy.name)
795 f.write(' optional PolicyOptions policy_options = 1;\n')
796 f.write(' optional %s %s = 2;\n' % (policy.protobuf_type, policy.name))
797 f.write('}\n\n')
798 fields += [ ' optional %sProto %s = %s;\n' %
799 (policy.name, policy.name, policy.id + RESERVED_IDS) ]
802 def _WriteChromeSettingsProtobuf(policies, os, f):
803 f.write(CHROME_SETTINGS_PROTO_HEAD)
805 fields = []
806 f.write('// PBs for individual settings.\n\n')
807 for policy in policies:
808 # Note: this protobuf also gets the unsupported policies, since it's an
809 # exaustive list of all the supported user policies on any platform.
810 if not policy.is_device_only:
811 _WritePolicyProto(f, policy, fields)
813 f.write('// --------------------------------------------------\n'
814 '// Big wrapper PB containing the above groups.\n\n'
815 'message ChromeSettingsProto {\n')
816 f.write(''.join(fields))
817 f.write('}\n\n')
820 def _WriteCloudPolicyProtobuf(policies, os, f):
821 f.write(CLOUD_POLICY_PROTO_HEAD)
822 f.write('message CloudPolicySettings {\n')
823 for policy in policies:
824 if policy.is_supported and not policy.is_device_only:
825 f.write(' optional %sPolicyProto %s = %s;\n' %
826 (policy.policy_protobuf_type, policy.name,
827 policy.id + RESERVED_IDS))
828 f.write('}\n\n')
831 #------------------ protobuf decoder -------------------------------#
833 CPP_HEAD = '''
834 #include <limits>
835 #include <string>
837 #include "base/basictypes.h"
838 #include "base/callback.h"
839 #include "base/json/json_reader.h"
840 #include "base/logging.h"
841 #include "base/memory/scoped_ptr.h"
842 #include "base/memory/weak_ptr.h"
843 #include "base/values.h"
844 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
845 #include "components/policy/core/common/external_data_fetcher.h"
846 #include "components/policy/core/common/policy_map.h"
847 #include "policy/policy_constants.h"
848 #include "policy/proto/cloud_policy.pb.h"
850 using google::protobuf::RepeatedPtrField;
852 namespace policy {
854 namespace em = enterprise_management;
856 base::Value* DecodeIntegerValue(google::protobuf::int64 value) {
857 if (value < std::numeric_limits<int>::min() ||
858 value > std::numeric_limits<int>::max()) {
859 LOG(WARNING) << "Integer value " << value
860 << " out of numeric limits, ignoring.";
861 return NULL;
864 return new base::FundamentalValue(static_cast<int>(value));
867 base::ListValue* DecodeStringList(const em::StringList& string_list) {
868 base::ListValue* list_value = new base::ListValue;
869 RepeatedPtrField<std::string>::const_iterator entry;
870 for (entry = string_list.entries().begin();
871 entry != string_list.entries().end(); ++entry) {
872 list_value->AppendString(*entry);
874 return list_value;
877 base::Value* DecodeJson(const std::string& json) {
878 scoped_ptr<base::Value> root(
879 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS));
881 if (!root)
882 LOG(WARNING) << "Invalid JSON string, ignoring: " << json;
884 // Accept any Value type that parsed as JSON, and leave it to the handler to
885 // convert and check the concrete type.
886 return root.release();
889 void DecodePolicy(const em::CloudPolicySettings& policy,
890 base::WeakPtr<CloudExternalDataManager> external_data_manager,
891 PolicyMap* map) {
895 CPP_FOOT = '''}
897 } // namespace policy
901 def _CreateValue(type, arg):
902 if type == 'TYPE_BOOLEAN':
903 return 'new base::FundamentalValue(%s)' % arg
904 elif type == 'TYPE_INTEGER':
905 return 'DecodeIntegerValue(%s)' % arg
906 elif type == 'TYPE_STRING':
907 return 'new base::StringValue(%s)' % arg
908 elif type == 'TYPE_LIST':
909 return 'DecodeStringList(%s)' % arg
910 elif type == 'TYPE_DICTIONARY' or type == 'TYPE_EXTERNAL':
911 return 'DecodeJson(%s)' % arg
912 else:
913 raise NotImplementedError('Unknown type %s' % type)
916 def _CreateExternalDataFetcher(type, name):
917 if type == 'TYPE_EXTERNAL':
918 return 'new ExternalDataFetcher(external_data_manager, key::k%s)' % name
919 return 'NULL'
922 def _WritePolicyCode(f, policy):
923 membername = policy.name.lower()
924 proto_type = '%sPolicyProto' % policy.policy_protobuf_type
925 f.write(' if (policy.has_%s()) {\n' % membername)
926 f.write(' const em::%s& policy_proto = policy.%s();\n' %
927 (proto_type, membername))
928 f.write(' if (policy_proto.has_value()) {\n')
929 f.write(' PolicyLevel level = POLICY_LEVEL_MANDATORY;\n'
930 ' bool do_set = true;\n'
931 ' if (policy_proto.has_policy_options()) {\n'
932 ' do_set = false;\n'
933 ' switch(policy_proto.policy_options().mode()) {\n'
934 ' case em::PolicyOptions::MANDATORY:\n'
935 ' do_set = true;\n'
936 ' level = POLICY_LEVEL_MANDATORY;\n'
937 ' break;\n'
938 ' case em::PolicyOptions::RECOMMENDED:\n'
939 ' do_set = true;\n'
940 ' level = POLICY_LEVEL_RECOMMENDED;\n'
941 ' break;\n'
942 ' case em::PolicyOptions::UNSET:\n'
943 ' break;\n'
944 ' }\n'
945 ' }\n'
946 ' if (do_set) {\n')
947 f.write(' base::Value* value = %s;\n' %
948 (_CreateValue(policy.policy_type, 'policy_proto.value()')))
949 # TODO(bartfab): |value| == NULL indicates that the policy value could not be
950 # parsed successfully. Surface such errors in the UI.
951 f.write(' if (value) {\n')
952 f.write(' ExternalDataFetcher* external_data_fetcher = %s;\n' %
953 _CreateExternalDataFetcher(policy.policy_type, policy.name))
954 f.write(' map->Set(key::k%s, level, POLICY_SCOPE_USER,\n' %
955 policy.name)
956 f.write(' value, external_data_fetcher);\n'
957 ' }\n'
958 ' }\n'
959 ' }\n'
960 ' }\n')
963 def _WriteCloudPolicyDecoder(policies, os, f):
964 f.write(CPP_HEAD)
965 for policy in policies:
966 if policy.is_supported and not policy.is_device_only:
967 _WritePolicyCode(f, policy)
968 f.write(CPP_FOOT)
971 if __name__ == '__main__':
972 sys.exit(main())