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
16 from optparse
import OptionParser
23 CHROME_POLICY_KEY
= 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome'
24 CHROMIUM_POLICY_KEY
= 'SOFTWARE\\\\Policies\\\\Chromium'
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.
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'),
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()
69 for platform
, version
in [ p
.split(':') for p
in policy
['supported_on'] ]:
70 if not version
.endswith('-'):
73 if platform
.startswith('chrome.'):
74 platform_sub
= platform
[7:]
75 if platform_sub
== '*':
76 self
.platforms
.extend(['win', 'mac', 'linux'])
78 self
.platforms
.append(platform_sub
)
80 self
.platforms
.append(platform
)
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(
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')
102 self
.items
= [ PolicyDetails
.EnumItem(entry
) for entry
in items
]
104 PH_PATTERN
= re
.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>')
106 # Simplistic grit placeholder stripper.
108 def _RemovePlaceholders(text
):
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)
120 parser
= OptionParser(usage
=__doc__
)
121 parser
.add_option('--pch', '--policy-constants-header', dest
='header_path',
122 help='generate header file of policy constants',
124 parser
.add_option('--pcc', '--policy-constants-source', dest
='source_path',
125 help='generate source file of policy constants',
127 parser
.add_option('--cpp', '--cloud-policy-protobuf',
128 dest
='cloud_policy_proto_path',
129 help='generate cloud policy protobuf file',
131 parser
.add_option('--csp', '--chrome-settings-protobuf',
132 dest
='chrome_settings_proto_path',
133 help='generate chrome settings protobuf 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',
140 (opts
, args
) = parser
.parse_args()
143 print 'exactly platform, chromium_os flag and input file must be specified.'
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):
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
)
171 #------------------ shared helpers ---------------------------------#
173 def _OutputGeneratedWarningHeader(f
, template_file_path
):
175 '// DO NOT MODIFY THIS FILE DIRECTLY!\n'
176 '// IT IS GENERATED BY generate_policy_source.py\n'
177 '// FROM ' + template_file_path
+ '\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():
194 f
.write(COMMENT_WRAPPER
.fill(line
))
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']:
208 def _LoadJSONFile(json_file
):
209 with
open(json_file
, 'r') as f
:
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'
220 '#include <string>\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'
227 'namespace policy {\n'
229 'namespace internal {\n'
230 'struct SchemaData;\n'
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'
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'
248 '// Returns the schema data of the Chrome policy schema.\n'
249 'const internal::SchemaData* GetChromeSchemaData();\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
284 self
.shared_strings
= shared_strings
285 self
.schema_nodes
= []
286 self
.property_nodes
= []
287 self
.properties_nodes
= []
288 self
.restriction_nodes
= []
290 self
.string_enums
= []
291 self
.simple_types
= {
298 self
.stringlist_type
= None
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.
309 def AppendSchema(self
, type, extra
, comment
=''):
310 index
= len(self
.schema_nodes
)
311 self
.schema_nodes
.append((type, extra
, comment
))
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(
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
)
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.
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'])
400 if 'maximum' in schema
:
401 max_value
= int(schema
['minimum'])
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',
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
)
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
445 index
= self
.AppendSchema('TYPE_DICTIONARY', -1)
447 if 'additionalProperties' in schema
:
448 additionalProperties
= self
.GenerateAndCollectID(
449 schema
['additionalProperties'],
450 'additionalProperties of ' + name
)
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())
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
)
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
)
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
493 index
= self
.Generate(schema
, name
)
494 if not schema
.has_key('id'):
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
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'
508 for type, extra
, comment
in self
.schema_nodes
:
510 f
.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra
, comment
))
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
:
518 f
.write(' { %-50s %6d },\n' % (key
, schema
))
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
)
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
))
536 f
.write('const int kIntegerEnumerations[] = {\n')
537 for possible_values
in self
.int_enums
:
538 f
.write(' %d,\n' % possible_values
)
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
))
547 f
.write('const internal::SchemaData kChromeSchemaData = {\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')
556 def GetByID(self
, id_str
):
557 if not isinstance(id_str
, types
.StringTypes
):
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'
583 '#include <algorithm>\n'
584 '#include <climits>\n'
586 '#include "base/logging.h"\n'
587 '#include "components/policy/core/common/schema_internal.h"\n'
589 'namespace policy {\n'
594 # Generate the Chrome schema.
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,',
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'
630 f
.write('} // namespace\n\n')
633 f
.write('#if defined(GOOGLE_CHROME_BUILD)\n'
634 'const wchar_t kRegistryChromePolicyKey[] = '
635 'L"' + CHROME_POLICY_KEY
+ '";\n'
637 'const wchar_t kRegistryChromePolicyKey[] = '
638 'L"' + CHROMIUM_POLICY_KEY
+ '";\n'
641 f
.write('const internal::SchemaData* GetChromeSchemaData() {\n'
642 ' return &kChromeSchemaData;\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
660 raise RuntimeError('Type %s of policy %s is not supported at '
661 'enterprise defaults' % (policy
.policy_type
,
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'
669 ' }\n' % (policy
.name
, policy
.name
, creation_expression
))
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'
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'
699 ' size_t index = it - begin;\n'
700 ' CHECK_LT(index, arraysize(kChromePolicyDetails));\n'
701 ' return kChromePolicyDetails + index;\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
= '''
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
= '''
733 option optimize_for = LITE_RUNTIME;
735 package enterprise_management;
738 repeated string entries = 1;
741 message PolicyOptions {
743 // The given settings are applied regardless of user choice.
745 // The user may choose to override the given settings.
747 // No policy value is present and the policy should be ignored.
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.
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
))
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
)
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
))
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
))
831 #------------------ protobuf decoder -------------------------------#
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;
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.";
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);
877 base::Value* DecodeJson(const std::string& json) {
878 scoped_ptr<base::Value> root(
879 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS));
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,
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
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
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'
933 ' switch(policy_proto.policy_options().mode()) {\n'
934 ' case em::PolicyOptions::MANDATORY:\n'
936 ' level = POLICY_LEVEL_MANDATORY;\n'
938 ' case em::PolicyOptions::RECOMMENDED:\n'
940 ' level = POLICY_LEVEL_RECOMMENDED;\n'
942 ' case em::PolicyOptions::UNSET:\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' %
956 f
.write(' value, external_data_fetcher);\n'
963 def _WriteCloudPolicyDecoder(policies
, os
, f
):
965 for policy
in policies
:
966 if policy
.is_supported
and not policy
.is_device_only
:
967 _WritePolicyCode(f
, policy
)
971 if __name__
== '__main__':