3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Directly processes text of web.xml.
19 WebXmlParser is called with Xml string to produce a WebXml object containing
20 the data from that string.
22 WebXmlParser: Converts xml to AppEngineWebXml object.
23 WebXml: Contains relevant information from web.xml.
24 SecurityConstraint: Contains information about specified security constraints.
28 from xml
.etree
import ElementTree
30 from google
.appengine
.tools
import xml_parser_utils
31 from google
.appengine
.tools
.app_engine_config_exception
import AppEngineConfigException
32 from google
.appengine
.tools
.value_mixin
import ValueMixin
35 class WebXmlParser(object):
36 """Provides logic for walking down XML tree and pulling data."""
38 def ProcessXml(self
, xml_str
):
39 """Parses XML string and returns object representation of relevant info.
41 Uses ElementTree parser to return a tree representation of XML.
42 Then walks down that tree and extracts important info and adds it to the
46 xml_str: The XML string itself.
49 If there is well-formed but illegal XML, returns a list of
50 errors. Otherwise, returns an AppEngineWebXml object containing
54 AppEngineConfigException: In case of malformed XML or illegal inputs.
57 self
.web_xml
= WebXml()
59 xml_root
= ElementTree
.fromstring(xml_str
)
60 for node
in xml_root
.getchildren():
61 self
.ProcessSecondLevelNode(node
)
64 raise AppEngineConfigException('\n'.join(self
.errors
))
68 except ElementTree
.ParseError
:
69 raise AppEngineConfigException('Bad input -- not valid XML')
71 def ProcessSecondLevelNode(self
, node
):
72 element_name
= xml_parser_utils
.GetTag(node
)
73 if element_name
in ['servlet', 'filter', 'display-name', 'taglib']:
77 camel_case_name
= ''.join(part
.title() for part
in element_name
.split('-'))
78 method_name
= 'Process%sNode' % camel_case_name
79 if (hasattr(self
, method_name
) and
80 method_name
is not 'ProcessSecondLevelNode'):
81 getattr(self
, method_name
)(node
)
83 self
.errors
.append('Second-level tag not recognized: %s' % element_name
)
85 def ProcessServletMappingNode(self
, node
):
86 self
._ProcessUrlMappingNode
(node
)
88 def ProcessFilterMappingNode(self
, node
):
89 self
._ProcessUrlMappingNode
(node
)
91 def _ProcessUrlMappingNode(self
, node
):
92 """Parses out URL and possible ID for filter-mapping and servlet-mapping.
94 Pulls url-pattern text out of node and adds to WebXml object. If url-pattern
95 has an id attribute, adds that as well. This is done for <servlet-mapping>
96 and <filter-mapping> nodes.
99 node: An ElementTreeNode which looks something like the following:
102 <servlet-name>redteam</servlet-name>
103 <url-pattern>/red/*</url-pattern>
106 url_pattern_node
= xml_parser_utils
.GetChild(node
, 'url-pattern')
107 if url_pattern_node
is not None:
108 self
.web_xml
.patterns
.append(url_pattern_node
.text
)
109 id_attr
= xml_parser_utils
.GetAttribute(url_pattern_node
, 'id')
111 self
.web_xml
.pattern_to_id
[url_pattern_node
.text
] = id_attr
113 def ProcessErrorPageNode(self
, node
):
114 """Process error page specifications.
116 If one of the supplied error codes is 404, allow fall through to runtime.
119 node: An ElementTreeNode which looks something like the following.
121 <error-code>500</error-code>
122 <location>/errors/servererror.jsp</location>
126 error_code
= xml_parser_utils
.GetChildNodeText(node
, 'error-code')
127 if error_code
== '404':
128 self
.web_xml
.fall_through_to_runtime
= True
130 def ProcessWelcomeFileListNode(self
, node
):
131 for welcome_node
in xml_parser_utils
.GetNodes(node
, 'welcome-file'):
132 welcome_file
= welcome_node
.text
133 if welcome_file
and welcome_file
[0] == '/':
134 self
.errors
.append('Welcome files must be relative paths: %s' %
137 self
.web_xml
.welcome_files
.append(welcome_file
)
139 def ProcessMimeMappingNode(self
, node
):
140 extension
= xml_parser_utils
.GetChildNodeText(node
, 'extension')
141 mime_type
= xml_parser_utils
.GetChildNodeText(node
, 'mime-type')
144 self
.errors
.append('<mime-type> without extension')
146 self
.web_xml
.mime_mappings
[extension
] = mime_type
148 def ProcessSecurityConstraintNode(self
, node
):
149 """Pulls data from the security constraint node and adds to WebXml object.
152 node: An ElementTree Xml node that looks something like the following:
154 <security-constraint>
155 <web-resource-collection>
156 <url-pattern>/profile/*</url-pattern>
157 </web-resource-collection>
158 <user-data-constraint>
159 <transport-guarantee>CONFIDENTIAL</transport-guarantee>
160 </user-data-constraint>
161 </security-constraint>
163 security_constraint
= SecurityConstraint()
164 resources_node
= xml_parser_utils
.GetChild(node
, 'web-resource-collection')
165 security_constraint
.patterns
= [
166 sub_node
.text
for sub_node
in xml_parser_utils
.GetNodes(
167 resources_node
, 'url-pattern')]
168 constraint
= xml_parser_utils
.GetChild(node
, 'auth-constraint')
169 if constraint
is not None:
170 role_name
= xml_parser_utils
.GetChildNodeText(
171 constraint
, 'role-name').lower()
172 if role_name
not in ('none', '*', 'admin'):
173 self
.errors
.append('Bad value for <role-name> (%s), must be none, '
174 '*, or admin' % role_name
)
175 security_constraint
.required_role
= role_name
177 user_constraint
= xml_parser_utils
.GetChild(node
, 'user-data-constraint')
178 if user_constraint
is not None:
179 guarantee
= xml_parser_utils
.GetChildNodeText(
180 user_constraint
, 'transport-guarantee').lower()
181 if guarantee
not in ('none', 'integral', 'confidential'):
182 self
.errors
.append('Bad value for <transport-guarantee> (%s), must be'
183 ' none, integral, or confidential' % guarantee
)
184 security_constraint
.transport_guarantee
= guarantee
186 self
.web_xml
.security_constraints
.append(security_constraint
)
189 class WebXml(ValueMixin
):
190 """Contains information about web.xml relevant for translation to app.yaml."""
194 self
.security_constraints
= []
195 self
.welcome_files
= []
196 self
.mime_mappings
= {}
197 self
.pattern_to_id
= {}
198 self
.fall_through_to_runtime
= False
200 def GetMimeTypeForPath(self
, path
):
203 return self
.mime_mappings
.get(path
.split('.')[-1], None)
206 class SecurityConstraint(ValueMixin
):
207 """Contains information about security constraints in web.xml."""
211 self
.transport_guarantee
= 'none'
212 self
.required_role
= 'none'