~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to tools/server-side/svnpubsub/svnpubsub/server.py

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
# Instead of using a complicated XMPP/AMPQ/JMS/super messaging service,
26
26
# we have simple HTTP GETs and PUTs to get data in and out.
27
27
#
28
 
# Currently supports both XML and JSON serialization.
 
28
# Currently supports JSON serialization.
29
29
#
30
30
# Example Sub clients:
31
 
#   curl -sN http://127.0.0.1:2069/commits
32
 
#   curl -sN http://127.0.0.1:2069/commits/svn/*
33
 
#   curl -sN http://127.0.0.1:2069/commits/svn
34
 
#   curl -sN http://127.0.0.1:2069/commits/*/13f79535-47bb-0310-9956-ffa450edef68
35
 
#   curl -sN http://127.0.0.1:2069/commits/svn/13f79535-47bb-0310-9956-ffa450edef68
36
 
#
37
 
#   URL is built into 2 parts:
38
 
#       /commits/${optional_type}/${optional_repository}
39
 
#
40
 
#   If the type is included in the URL, you will only get commits of that type.
41
 
#   The type can be * and then you will receive commits of any type.
 
31
#   curl -sN  http://127.0.0.1:2069/commits
 
32
#   curl -sN 'http://127.0.0.1:2069/commits/svn/*'
 
33
#   curl -sN  http://127.0.0.1:2069/commits/svn
 
34
#   curl -sN 'http://127.0.0.1:2069/commits/*/13f79535-47bb-0310-9956-ffa450edef68'
 
35
#   curl -sN  http://127.0.0.1:2069/commits/svn/13f79535-47bb-0310-9956-ffa450edef68
 
36
#
 
37
#   curl -sN  http://127.0.0.1:2069/metadata
 
38
#   curl -sN 'http://127.0.0.1:2069/metadata/svn/*'
 
39
#   curl -sN  http://127.0.0.1:2069/metadata/svn
 
40
#   curl -sN 'http://127.0.0.1:2069/metadata/*/13f79535-47bb-0310-9956-ffa450edef68'
 
41
#   curl -sN  http://127.0.0.1:2069/metadata/svn/13f79535-47bb-0310-9956-ffa450edef68
 
42
#
 
43
#   URLs are constructed from 3 parts:
 
44
#       /${notification}/${optional_type}/${optional_repository}
 
45
#
 
46
#   Notifications can be sent for commits or metadata (e.g., revprop) changes.
 
47
#   If the type is included in the URL, you will only get notifications of that type.
 
48
#   The type can be * and then you will receive notifications of any type.
42
49
#
43
50
#   If the repository is included in the URL, you will only receive
44
51
#   messages about that repository.  The repository can be * and then you
71
78
 
72
79
import time
73
80
 
74
 
class Commit:
 
81
class Notification(object):
75
82
    def __init__(self, r):
76
83
        self.__dict__.update(r)
77
84
        if not self.check_value('repository'):
86
93
    def check_value(self, k):
87
94
        return hasattr(self, k) and self.__dict__[k]
88
95
 
89
 
    def render_commit(self):
 
96
    def render(self):
 
97
        raise NotImplementedError
 
98
 
 
99
    def render_log(self):
 
100
        raise NotImplementedError
 
101
 
 
102
class Commit(Notification):
 
103
    KIND = 'COMMIT'
 
104
 
 
105
    def render(self):
90
106
        obj = {'commit': {}}
91
107
        obj['commit'].update(self.__dict__)
92
108
        return json.dumps(obj)
96
112
            paths_changed = " %d paths changed" % len(self.changed)
97
113
        except:
98
114
            paths_changed = ""
99
 
        return "%s:%s repo '%s' id '%s'%s" % (self.type,
100
 
                                  self.format,
101
 
                                  self.repository,
102
 
                                  self.id,
103
 
                                  paths_changed)
 
115
        return "commit %s:%s repo '%s' id '%s'%s" % (
 
116
            self.type, self.format, self.repository, self.id,
 
117
            paths_changed)
 
118
 
 
119
class Metadata(Notification):
 
120
    KIND = 'METADATA'
 
121
 
 
122
    def render(self):
 
123
        obj = {'metadata': {}}
 
124
        obj['metadata'].update(self.__dict__)
 
125
        return json.dumps(obj)
 
126
 
 
127
    def render_log(self):
 
128
        return "metadata %s:%s repo '%s' id '%s' revprop '%s'" % (
 
129
            self.type, self.format, self.repository, self.id,
 
130
            self.revprop['name'])
104
131
 
105
132
 
106
133
HEARTBEAT_TIME = 15
107
134
 
108
135
class Client(object):
109
 
    def __init__(self, pubsub, r, type, repository):
 
136
    def __init__(self, pubsub, r, kind, type, repository):
110
137
        self.pubsub = pubsub
111
138
        r.notifyFinish().addErrback(self.finished)
112
139
        self.r = r
 
140
        self.kind = kind
113
141
        self.type = type
114
142
        self.repository = repository
115
143
        self.alive = True
123
151
        except ValueError:
124
152
            pass
125
153
 
126
 
    def interested_in(self, commit):
127
 
        if self.type and self.type != commit.type:
128
 
            return False
129
 
 
130
 
        if self.repository and self.repository != commit.repository:
 
154
    def interested_in(self, notification):
 
155
        if self.kind != notification.KIND:
 
156
            return False
 
157
 
 
158
        if self.type and self.type != notification.type:
 
159
            return False
 
160
 
 
161
        if self.repository and self.repository != notification.repository:
131
162
            return False
132
163
 
133
164
        return True
164
195
    isLeaf = True
165
196
    clients = []
166
197
 
 
198
    __notification_uri_map = {'commits': Commit.KIND,
 
199
                              'metadata': Metadata.KIND}
 
200
 
 
201
    def __init__(self, notification_class):
 
202
        resource.Resource.__init__(self)
 
203
        self.__notification_class = notification_class
 
204
 
167
205
    def cc(self):
168
206
        return len(self.clients)
169
207
 
183
221
            request.setResponseCode(400)
184
222
            return "Invalid path\n"
185
223
 
 
224
        kind = self.__notification_uri_map.get(uri[1], None)
 
225
        if kind is None:
 
226
            request.setResponseCode(400)
 
227
            return "Invalid path\n"
 
228
 
186
229
        if uri_len >= 3:
187
230
          type = uri[2]
188
231
 
195
238
        if repository == '*':
196
239
          repository = None
197
240
 
198
 
        c = Client(self, request, type, repository)
 
241
        c = Client(self, request, kind, type, repository)
199
242
        self.clients.append(c)
200
243
        c.start()
201
244
        return twisted.web.server.NOT_DONE_YET
202
245
 
203
 
    def notifyAll(self, commit):
204
 
        data = commit.render_commit()
 
246
    def notifyAll(self, notification):
 
247
        data = notification.render()
205
248
 
206
 
        log.msg("COMMIT: %s (%d clients)" % (commit.render_log(), self.cc()))
 
249
        log.msg("%s: %s (%d clients)"
 
250
                % (notification.KIND, notification.render_log(), self.cc()))
207
251
        for client in self.clients:
208
 
            if client.interested_in(commit):
 
252
            if client.interested_in(notification):
209
253
                client.write_data(data)
210
254
 
211
255
    def render_PUT(self, request):
218
262
        #import pdb;pdb.set_trace()
219
263
        #print "input: %s" % (input)
220
264
        try:
221
 
            c = json.loads(input)
222
 
            commit = Commit(c)
 
265
            data = json.loads(input)
 
266
            notification = self.__notification_class(data)
223
267
        except ValueError as e:
224
268
            request.setResponseCode(400)
225
 
            log.msg("COMMIT: failed due to: %s" % str(e))
226
 
            return str(e)
227
 
        self.notifyAll(commit)
 
269
            errstr = str(e)
 
270
            log.msg("%s: failed due to: %s" % (notification.KIND, errstr))
 
271
            return errstr
 
272
        self.notifyAll(notification)
228
273
        return "Ok"
229
274
 
 
275
 
230
276
def svnpubsub_server():
231
277
    root = resource.Resource()
232
 
    s = SvnPubSub()
233
 
    root.putChild("commits", s)
 
278
    c = SvnPubSub(Commit)
 
279
    m = SvnPubSub(Metadata)
 
280
    root.putChild('commits', c)
 
281
    root.putChild('metadata', m)
234
282
    return server.Site(root)
235
283
 
236
284
if __name__ == "__main__":