~ubuntu-branches/ubuntu/trusty/python-boto/trusty

« back to all changes in this revision

Viewing changes to boto/gs/cors.py

  • Committer: Package Import Robot
  • Author(s): Eric Evans
  • Date: 2013-05-10 23:38:14 UTC
  • mfrom: (1.1.10) (14.1.2 experimental)
  • Revision ID: package-import@ubuntu.com-20130510233814-701dvlop7xfh88i7
Tags: 2.9.2-1
New upstream release (Closes: #700743).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2012 Google Inc.
 
2
#
 
3
# Permission is hereby granted, free of charge, to any person obtaining a
 
4
# copy of this software and associated documentation files (the
 
5
# "Software"), to deal in the Software without restriction, including
 
6
# without limitation the rights to use, copy, modify, merge, publish, dis-
 
7
# tribute, sublicense, and/or sell copies of the Software, and to permit
 
8
# persons to whom the Software is furnished to do so, subject to the fol-
 
9
# lowing conditions:
 
10
#
 
11
# The above copyright notice and this permission notice shall be included
 
12
# in all copies or substantial portions of the Software.
 
13
#
 
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 
15
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 
16
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 
17
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 
18
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 
20
# IN THE SOFTWARE.
 
21
 
 
22
import types
 
23
from boto.gs.user import User
 
24
from boto.exception import InvalidCorsError
 
25
from xml.sax import handler
 
26
 
 
27
# Relevant tags for the CORS XML document.
 
28
CORS_CONFIG = 'CorsConfig'
 
29
CORS        = 'Cors'
 
30
ORIGINS     = 'Origins'
 
31
ORIGIN      = 'Origin'
 
32
METHODS     = 'Methods'
 
33
METHOD      = 'Method'
 
34
HEADERS     = 'ResponseHeaders'
 
35
HEADER      = 'ResponseHeader'
 
36
MAXAGESEC   = 'MaxAgeSec'
 
37
 
 
38
class Cors(handler.ContentHandler):
 
39
    """Encapsulates the CORS configuration XML document"""
 
40
    def __init__(self):
 
41
        # List of CORS elements found within a CorsConfig element.
 
42
        self.cors = []
 
43
        # List of collections (e.g. Methods, ResponseHeaders, Origins)
 
44
        # found within a CORS element. We use a list of lists here
 
45
        # instead of a dictionary because the collections need to be
 
46
        # preserved in the order in which they appear in the input XML
 
47
        # document (and Python dictionary keys are inherently unordered).
 
48
        # The elements on this list are two element tuples of the form
 
49
        # (collection name, [list of collection contents]).
 
50
        self.collections = []
 
51
        # Lists of elements within a collection. Again a list is needed to
 
52
        # preserve ordering but also because the same element may appear
 
53
        # multiple times within a collection.
 
54
        self.elements = []
 
55
        # Dictionary mapping supported collection names to element types
 
56
        # which may be contained within each.
 
57
        self.legal_collections = {
 
58
            ORIGINS : [ORIGIN],
 
59
            METHODS : [METHOD],
 
60
            HEADERS : [HEADER],
 
61
            MAXAGESEC: []
 
62
        }
 
63
        # List of supported element types within any collection, used for
 
64
        # checking validadity of a parsed element name.
 
65
        self.legal_elements = [ORIGIN, METHOD, HEADER]
 
66
 
 
67
        self.parse_level = 0
 
68
        self.collection = None
 
69
        self.element = None
 
70
 
 
71
    def validateParseLevel(self, tag, level):
 
72
        """Verify parse level for a given tag."""
 
73
        if self.parse_level != level:
 
74
            raise InvalidCorsError('Invalid tag %s at parse level %d: ' %
 
75
                                   (tag, self.parse_level))
 
76
 
 
77
    def startElement(self, name, attrs, connection):
 
78
        """SAX XML logic for parsing new element found."""
 
79
        if name == CORS_CONFIG:
 
80
            self.validateParseLevel(name, 0)
 
81
            self.parse_level += 1;
 
82
        elif name == CORS:
 
83
            self.validateParseLevel(name, 1)
 
84
            self.parse_level += 1;
 
85
        elif name in self.legal_collections:
 
86
            self.validateParseLevel(name, 2)
 
87
            self.parse_level += 1;
 
88
            self.collection = name
 
89
        elif name in self.legal_elements:
 
90
            self.validateParseLevel(name, 3)
 
91
            # Make sure this tag is found inside a collection tag.
 
92
            if self.collection is None:
 
93
                raise InvalidCorsError('Tag %s found outside collection' % name)
 
94
            # Make sure this tag is allowed for the current collection tag.
 
95
            if name not in self.legal_collections[self.collection]:
 
96
                raise InvalidCorsError('Tag %s not allowed in %s collection' %
 
97
                                       (name, self.collection))
 
98
            self.element = name
 
99
        else:
 
100
            raise InvalidCorsError('Unsupported tag ' + name)
 
101
 
 
102
    def endElement(self, name, value, connection):
 
103
        """SAX XML logic for parsing new element found."""
 
104
        if name == CORS_CONFIG:
 
105
            self.validateParseLevel(name, 1)
 
106
            self.parse_level -= 1;
 
107
        elif name == CORS:
 
108
            self.validateParseLevel(name, 2)
 
109
            self.parse_level -= 1;
 
110
            # Terminating a CORS element, save any collections we found
 
111
            # and re-initialize collections list.
 
112
            self.cors.append(self.collections)
 
113
            self.collections = []
 
114
        elif name in self.legal_collections:
 
115
            self.validateParseLevel(name, 3)
 
116
            if name != self.collection:
 
117
              raise InvalidCorsError('Mismatched start and end tags (%s/%s)' %
 
118
                                     (self.collection, name))
 
119
            self.parse_level -= 1;
 
120
            if not self.legal_collections[name]:
 
121
              # If this collection doesn't contain any sub-elements, store
 
122
              # a tuple of name and this tag's element value.
 
123
              self.collections.append((name, value.strip()))
 
124
            else:
 
125
              # Otherwise, we're terminating a collection of sub-elements,
 
126
              # so store a tuple of name and list of contained elements.
 
127
              self.collections.append((name, self.elements))
 
128
            self.elements = []
 
129
            self.collection = None
 
130
        elif name in self.legal_elements:
 
131
            self.validateParseLevel(name, 3)
 
132
            # Make sure this tag is found inside a collection tag.
 
133
            if self.collection is None:
 
134
                raise InvalidCorsError('Tag %s found outside collection' % name)
 
135
            # Make sure this end tag is allowed for the current collection tag.
 
136
            if name not in self.legal_collections[self.collection]:
 
137
                raise InvalidCorsError('Tag %s not allowed in %s collection' %
 
138
                                       (name, self.collection))
 
139
            if name != self.element:
 
140
              raise InvalidCorsError('Mismatched start and end tags (%s/%s)' %
 
141
                                     (self.element, name))
 
142
            # Terminating an element tag, add it to the list of elements
 
143
            # for the current collection.
 
144
            self.elements.append((name, value.strip()))
 
145
            self.element = None
 
146
        else:
 
147
            raise InvalidCorsError('Unsupported end tag ' + name)
 
148
 
 
149
    def to_xml(self):
 
150
        """Convert CORS object into XML string representation."""
 
151
        s = '<' + CORS_CONFIG + '>'
 
152
        for collections in self.cors:
 
153
          s += '<' + CORS + '>'
 
154
          for (collection, elements_or_value) in collections:
 
155
            assert collection is not None
 
156
            s += '<' + collection + '>'
 
157
            # If collection elements has type string, append atomic value,
 
158
            # otherwise, append sequence of values in named tags.
 
159
            if isinstance(elements_or_value, types.StringTypes):
 
160
              s += elements_or_value
 
161
            else:
 
162
              for (name, value) in elements_or_value:
 
163
                assert name is not None
 
164
                assert value is not None
 
165
                s += '<' + name + '>' + value + '</' + name + '>'
 
166
            s += '</' + collection + '>'
 
167
          s += '</' + CORS + '>'
 
168
        s += '</' + CORS_CONFIG + '>'
 
169
        return s