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
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'
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.
40 'dict': ('TYPE_DICTIONARY', 'string', 'String',
42 'external': ('TYPE_EXTERNAL', 'string', 'String',
44 'int': ('TYPE_INTEGER', 'int64', 'Integer',
46 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer',
48 'list': ('TYPE_LIST', 'StringList', 'StringList',
50 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean',
52 'string': ('TYPE_STRING', 'string', 'String',
54 'string-enum': ('TYPE_STRING', 'string', 'String',
56 'string-enum-list': ('TYPE_LIST', 'StringList', 'StringList',
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()
80 for platform
, version
in [ p
.split(':') for p
in policy
['supported_on'] ]:
81 if not version
.endswith('-'):
84 if platform
.startswith('chrome.'):
85 platform_sub
= platform
[7:]
86 if platform_sub
== '*':
87 self
.platforms
.extend(['win', 'mac', 'linux'])
89 self
.platforms
.append(platform_sub
)
91 self
.platforms
.append(platform
)
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(
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')
113 self
.items
= [ PolicyDetails
.EnumItem(entry
) for entry
in items
]
115 PH_PATTERN
= re
.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>')
117 # Simplistic grit placeholder stripper.
119 def _RemovePlaceholders(text
):
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)
131 parser
= OptionParser(usage
=__doc__
)
132 parser
.add_option('--pch', '--policy-constants-header', dest
='header_path',
133 help='generate header file of policy constants',
135 parser
.add_option('--pcc', '--policy-constants-source', dest
='source_path',
136 help='generate source file of policy constants',
138 parser
.add_option('--cpp', '--cloud-policy-protobuf',
139 dest
='cloud_policy_proto_path',
140 help='generate cloud policy protobuf file',
142 parser
.add_option('--csp', '--chrome-settings-protobuf',
143 dest
='chrome_settings_proto_path',
144 help='generate chrome settings protobuf 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',
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',
156 (opts
, args
) = parser
.parse_args()
159 print 'exactly platform, chromium_os flag and input file must be specified.'
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):
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
)
185 GenerateFile(opts
.app_restrictions_path
, _WriteAppRestrictions
, xml
=True)
190 #------------------ shared helpers ---------------------------------#
192 def _OutputGeneratedWarningHeader(f
, template_file_path
, xml_style
):
196 f
.write('<?xml version="1.0" encoding="utf-8"?>\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')
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():
224 f
.write(COMMENT_WRAPPER
.fill(line
))
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']:
238 def _LoadJSONFile(json_file
):
239 with
open(json_file
, 'r') as f
:
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'
250 '#include <string>\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'
257 'namespace policy {\n'
259 'namespace internal {\n'
260 'struct SchemaData;\n'
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'
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'
278 '// Returns the schema data of the Chrome policy schema.\n'
279 'const internal::SchemaData* GetChromeSchemaData();\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
314 self
.shared_strings
= shared_strings
315 self
.schema_nodes
= []
316 self
.property_nodes
= []
317 self
.properties_nodes
= []
318 self
.restriction_nodes
= []
320 self
.string_enums
= []
321 self
.simple_types
= {
328 self
.stringlist_type
= None
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.
339 def AppendSchema(self
, type, extra
, comment
=''):
340 index
= len(self
.schema_nodes
)
341 self
.schema_nodes
.append((type, extra
, comment
))
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(
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
)
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.
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'])
430 if 'maximum' in schema
:
431 max_value
= int(schema
['minimum'])
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',
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
)
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
475 index
= self
.AppendSchema('TYPE_DICTIONARY', -1)
477 if 'additionalProperties' in schema
:
478 additionalProperties
= self
.GenerateAndCollectID(
479 schema
['additionalProperties'],
480 'additionalProperties of ' + name
)
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())
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
)
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
)
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
523 index
= self
.Generate(schema
, name
)
524 if not schema
.has_key('id'):
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
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'
538 for type, extra
, comment
in self
.schema_nodes
:
540 f
.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra
, comment
))
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
:
548 f
.write(' { %-50s %6d },\n' % (key
, schema
))
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
)
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
))
566 f
.write('const int kIntegerEnumerations[] = {\n')
567 for possible_values
in self
.int_enums
:
568 f
.write(' %d,\n' % possible_values
)
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
))
577 f
.write('const internal::SchemaData kChromeSchemaData = {\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')
586 def GetByID(self
, id_str
):
587 if not isinstance(id_str
, types
.StringTypes
):
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'
613 '#include <algorithm>\n'
614 '#include <climits>\n'
616 '#include "base/logging.h"\n'
617 '#include "components/policy/core/common/schema_internal.h"\n'
619 'namespace policy {\n'
624 # Generate the Chrome schema.
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,',
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'
660 f
.write('} // namespace\n\n')
663 f
.write('#if defined(GOOGLE_CHROME_BUILD)\n'
664 'const wchar_t kRegistryChromePolicyKey[] = '
665 'L"' + CHROME_POLICY_KEY
+ '";\n'
667 'const wchar_t kRegistryChromePolicyKey[] = '
668 'L"' + CHROMIUM_POLICY_KEY
+ '";\n'
671 f
.write('const internal::SchemaData* GetChromeSchemaData() {\n'
672 ' return &kChromeSchemaData;\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
690 raise RuntimeError('Type %s of policy %s is not supported at '
691 'enterprise defaults' % (policy
.policy_type
,
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'
699 ' }\n' % (policy
.name
, policy
.name
, creation_expression
))
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'
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'
729 ' size_t index = it - begin;\n'
730 ' CHECK_LT(index, arraysize(kChromePolicyDetails));\n'
731 ' return kChromePolicyDetails + index;\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
= '''
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
= '''
763 option optimize_for = LITE_RUNTIME;
765 package enterprise_management;
768 repeated string entries = 1;
771 message PolicyOptions {
773 // The given settings are applied regardless of user choice.
775 // The user may choose to override the given settings.
777 // No policy value is present and the policy should be ignored.
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.
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
))
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
)
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
))
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
))
861 #------------------ protobuf decoder -------------------------------#
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;
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.";
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);
907 base::Value* DecodeJson(const std::string& json) {
908 scoped_ptr<base::Value> root(
909 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS));
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,
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
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
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'
963 ' switch(policy_proto.policy_options().mode()) {\n'
964 ' case em::PolicyOptions::MANDATORY:\n'
966 ' level = POLICY_LEVEL_MANDATORY;\n'
968 ' case em::PolicyOptions::RECOMMENDED:\n'
970 ' level = POLICY_LEVEL_RECOMMENDED;\n'
972 ' case em::PolicyOptions::UNSET:\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' %
986 f
.write(' value, external_data_fetcher);\n'
993 def _WriteCloudPolicyDecoder(policies
, os
, f
):
995 for policy
in policies
:
996 if policy
.is_supported
and not policy
.is_device_only
:
997 _WritePolicyCode(f
, policy
)
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
)
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__':