32 from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS |
32 from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS |
33 from autobahn.wamp import types, auth |
33 from autobahn.wamp import types, auth |
34 from autobahn.wamp.serializer import MsgPackSerializer |
34 from autobahn.wamp.serializer import MsgPackSerializer |
35 from twisted.internet.defer import inlineCallbacks |
35 from twisted.internet.defer import inlineCallbacks |
36 from twisted.internet.protocol import ReconnectingClientFactory |
36 from twisted.internet.protocol import ReconnectingClientFactory |
|
37 from twisted.python.components import registerAdapter |
37 |
38 |
38 import runtime.NevowServer as NS |
39 import runtime.NevowServer as NS |
39 |
40 |
40 from formless import annotate |
41 from formless import annotate, webform |
|
42 import formless |
|
43 from nevow import tags, url, static |
41 |
44 |
42 mandatoryConfigItems = ["ID", "active", "realm", "url"] |
45 mandatoryConfigItems = ["ID", "active", "realm", "url"] |
43 |
46 |
44 _transportFactory = None |
47 _transportFactory = None |
45 _WampSession = None |
48 _WampSession = None |
46 _PySrv = None |
49 _PySrv = None |
|
50 WorkingDir = None |
|
51 |
|
52 # Find pre-existing project WAMP config file |
47 _WampConf = None |
53 _WampConf = None |
48 _WampSecret = None |
54 _WampSecret = None |
49 |
55 |
50 ExposedCalls = [ |
56 ExposedCalls = [ |
51 ("StartPLC", {}), |
57 ("StartPLC", {}), |
162 super(ReconnectingWampWebSocketClientFactory, self).clientConnectionFailed(connector, reason) |
176 super(ReconnectingWampWebSocketClientFactory, self).clientConnectionFailed(connector, reason) |
163 else: |
177 else: |
164 del connector |
178 del connector |
165 |
179 |
166 |
180 |
167 def CheckConfiguration(WSClientConf): |
181 def CheckConfiguration(WampClientConf): |
168 url = WSClientConf["url"] |
182 url = WampClientConf["url"] |
169 if not IsCorrectUri(url): |
183 if not IsCorrectUri(url): |
170 raise annotate.ValidateError( |
184 raise annotate.ValidateError( |
171 {"url":"Invalid URL: {}".format(url)}, |
185 {"url":"Invalid URL: {}".format(url)}, |
172 _("WAMP confiuration error:")) |
186 _("WAMP confiuration error:")) |
173 |
187 |
174 def GetConfiguration(): |
188 def GetConfiguration(): |
175 global lastKnownConfig |
189 global lastKnownConfig |
176 |
190 |
177 WSClientConf = json.load(open(_WampConf)) |
191 if os.path.exists(_WampConf): |
|
192 WampClientConf = json.load(open(_WampConf)) |
|
193 else: |
|
194 WampClientConf = defaultWampConfig |
|
195 |
178 for itemName in mandatoryConfigItems: |
196 for itemName in mandatoryConfigItems: |
179 if WSClientConf.get(itemName, None) is None : |
197 if WampClientConf.get(itemName, None) is None : |
180 raise Exception(_("WAMP configuration error : missing '{}' parameter.").format(itemName)) |
198 raise Exception(_("WAMP configuration error : missing '{}' parameter.").format(itemName)) |
181 |
199 |
182 CheckConfiguration(WSClientConf) |
200 CheckConfiguration(WampClientConf) |
183 |
201 |
184 lastKnownConfig = WSClientConf.copy() |
202 lastKnownConfig = WampClientConf.copy() |
185 return WSClientConf |
203 return WampClientConf |
186 |
204 |
187 |
205 |
188 def SetConfiguration(WSClientConf): |
206 def SetWampSecret(wampSecret): |
|
207 with open(os.path.realpath(_WampSecret), 'w') as f: |
|
208 f.write(wampSecret) |
|
209 |
|
210 def SetConfiguration(WampClientConf): |
189 global lastKnownConfig |
211 global lastKnownConfig |
190 |
212 |
191 CheckConfiguration(WSClientConf) |
213 CheckConfiguration(WampClientConf) |
192 |
214 |
193 lastKnownConfig = WSClientConf.copy() |
215 lastKnownConfig = WampClientConf.copy() |
194 |
216 |
195 with open(os.path.realpath(_WampConf), 'w') as f: |
217 with open(os.path.realpath(_WampConf), 'w') as f: |
196 json.dump(WSClientConf, f, sort_keys=True, indent=4) |
218 json.dump(WampClientConf, f, sort_keys=True, indent=4) |
197 if 'active' in WSClientConf and WSClientConf['active']: |
219 if 'active' in WampClientConf and WampClientConf['active']: |
198 if _transportFactory and _WampSession: |
220 if _transportFactory and _WampSession: |
199 StopReconnectWampClient() |
221 StopReconnectWampClient() |
200 StartReconnectWampClient() |
222 StartReconnectWampClient() |
201 else: |
223 else: |
202 StopReconnectWampClient() |
224 StopReconnectWampClient() |
203 |
225 |
204 return WSClientConf |
226 return WampClientConf |
205 |
227 |
206 |
228 |
207 def LoadWampSecret(secretfname): |
229 def LoadWampSecret(secretfname): |
208 WSClientWampSecret = open(secretfname, 'rb').read() |
230 WSClientWampSecret = open(secretfname, 'rb').read() |
209 if len(WSClientWampSecret) == 0 : |
231 if len(WSClientWampSecret) == 0 : |
215 return re.match(r'wss?://[^\s?:#-]+(:[0-9]+)?(/[^\s]*)?$', uri) is not None |
237 return re.match(r'wss?://[^\s?:#-]+(:[0-9]+)?(/[^\s]*)?$', uri) is not None |
216 |
238 |
217 |
239 |
218 def RegisterWampClient(wampconf=None, wampsecret=None): |
240 def RegisterWampClient(wampconf=None, wampsecret=None): |
219 global _WampConf, _WampSecret |
241 global _WampConf, _WampSecret |
220 if wampsecret: |
242 _WampConfDefault = os.path.join(WorkingDir, "wampconf.json") |
|
243 _WampSecretDefault = os.path.join(WorkingDir, "wamp.secret") |
|
244 |
|
245 # default project's wampconf has precedance over commandline given |
|
246 if os.path.exists(_WampConfDefault) or wampconf is None: |
|
247 _WampConf = _WampConfDefault |
|
248 else : |
|
249 _WampConf = wampconf |
|
250 |
|
251 WampClientConf = GetConfiguration() |
|
252 |
|
253 if wampsecret is not None: |
|
254 WampClientConf["secret"] = LoadWampSecret(wampsecret) |
221 _WampSecret = wampsecret |
255 _WampSecret = wampsecret |
222 if wampconf: |
256 else : |
223 _WampConf = wampconf |
257 if os.path.exists(_WampSecretDefault): |
224 |
258 WampClientConf["secret"] = LoadWampSecret(_WampSecretDefault) |
225 WSClientConf = GetConfiguration() |
259 else : |
226 |
260 print(_("WAMP authentication has no secret configured")) |
227 if not WSClientConf["active"]: |
261 _WampSecret = _WampSecretDefault |
|
262 |
|
263 if not WampClientConf["active"]: |
228 print(_("WAMP deactivated in configuration")) |
264 print(_("WAMP deactivated in configuration")) |
229 return |
265 return |
230 |
266 |
231 if _WampSecret is not None: |
|
232 WSClientConf["secret"] = LoadWampSecret(_WampSecret) |
|
233 |
|
234 # create a WAMP application session factory |
267 # create a WAMP application session factory |
235 component_config = types.ComponentConfig( |
268 component_config = types.ComponentConfig( |
236 realm=WSClientConf["realm"], |
269 realm=WampClientConf["realm"], |
237 extra=WSClientConf) |
270 extra=WampClientConf) |
238 session_factory = wamp.ApplicationSessionFactory( |
271 session_factory = wamp.ApplicationSessionFactory( |
239 config=component_config) |
272 config=component_config) |
240 session_factory.session = WampSession |
273 session_factory.session = WampSession |
241 |
274 |
242 # create a WAMP-over-WebSocket transport client factory |
275 # create a WAMP-over-WebSocket transport client factory |
243 ReconnectingWampWebSocketClientFactory( |
276 ReconnectingWampWebSocketClientFactory( |
244 component_config, |
277 component_config, |
245 session_factory, |
278 session_factory, |
246 url=WSClientConf["url"], |
279 url=WampClientConf["url"], |
247 serializers=[MsgPackSerializer()]) |
280 serializers=[MsgPackSerializer()]) |
248 |
281 |
249 # start the client from a Twisted endpoint |
282 # start the client from a Twisted endpoint |
250 if _transportFactory: |
283 if _transportFactory: |
251 conn = connectWS(_transportFactory) |
284 conn = connectWS(_transportFactory) |
252 print(_("WAMP client connecting to :"), WSClientConf["url"]) |
285 print(_("WAMP client connecting to :"), WampClientConf["url"]) |
253 return True |
286 return True |
254 else: |
287 else: |
255 print(_("WAMP client can not connect to :"), WSClientConf["url"]) |
288 print(_("WAMP client can not connect to :"), WampClientConf["url"]) |
256 return False |
289 return False |
257 |
290 |
258 |
291 |
259 def StopReconnectWampClient(): |
292 def StopReconnectWampClient(): |
260 if _transportFactory is not None : |
293 if _transportFactory is not None : |
290 def SetServer(pysrv): |
323 def SetServer(pysrv): |
291 _PySrv = pysrv |
324 _PySrv = pysrv |
292 |
325 |
293 |
326 |
294 #### WEB CONFIGURATION INTERFACE #### |
327 #### WEB CONFIGURATION INTERFACE #### |
295 |
328 WAMP_SECRET_URL = "secret" |
296 webExposedConfigItems = ['active', 'url', 'ID'] |
329 webExposedConfigItems = ['active', 'url', 'ID'] |
297 |
330 |
298 def wampConfigDefault(ctx,argument): |
331 def wampConfigDefault(ctx,argument): |
299 if lastKnownConfig is not None : |
332 if lastKnownConfig is not None : |
300 return lastKnownConfig.get(argument.name, None) |
333 return lastKnownConfig.get(argument.name, None) |
301 |
334 |
302 def wampConfig(**kwargs): |
335 def wampConfig(**kwargs): |
|
336 secretfile_field = kwargs["secretfile"] |
|
337 if secretfile_field is not None: |
|
338 secret = secretfile_field.file.read() |
|
339 SetWampSecret(secret) |
|
340 |
303 newConfig = lastKnownConfig.copy() |
341 newConfig = lastKnownConfig.copy() |
304 for argname in webExposedConfigItems: |
342 for argname in webExposedConfigItems: |
305 newConfig[argname] = kwargs[argname] |
343 newConfig[argname] = kwargs[argname] |
306 |
344 |
307 SetConfiguration(newConfig) |
345 SetConfiguration(newConfig) |
|
346 |
|
347 class FileUploadDownload(annotate.FileUpload): |
|
348 pass |
|
349 |
|
350 |
|
351 class FileUploadDownloadRenderer(webform.FileUploadRenderer): |
|
352 def input(self, context, slot, data, name, value): |
|
353 slot[_("Upload:")] |
|
354 slot = webform.FileUploadRenderer.input(self, context, slot, data, name, value) |
|
355 download_url = data.typedValue.getAttribute('download_url') |
|
356 return slot[tags.a(href=download_url)[_("Download")]] |
|
357 |
|
358 registerAdapter(FileUploadDownloadRenderer, FileUploadDownload, formless.iformless.ITypedRenderer) |
|
359 |
|
360 def getDownloadUrl(ctx, argument): |
|
361 if lastKnownConfig is not None : |
|
362 currentID = lastKnownConfig.get("ID", None) |
|
363 return url.URL.fromContext(ctx).\ |
|
364 child(WAMP_SECRET_URL).\ |
|
365 child(lastKnownConfig["ID"]+".secret") |
308 |
366 |
309 webFormInterface = [ |
367 webFormInterface = [ |
310 ("status", |
368 ("status", |
311 annotate.String(label=_("Current status"), |
369 annotate.String(label=_("Current status"), |
312 immutable = True, |
370 immutable = True, |
313 default = lambda *k:getWampStatus())), |
371 default = lambda *k:getWampStatus())), |
314 ("ID", |
372 ("ID", |
315 annotate.String(label=_("ID"), |
373 annotate.String(label=_("ID"), |
316 default = wampConfigDefault)), |
374 default = wampConfigDefault)), |
|
375 ("secretfile", |
|
376 FileUploadDownload( |
|
377 label = _("File containing secret for that ID"), |
|
378 download_url = getDownloadUrl, |
|
379 )), |
317 ("active", |
380 ("active", |
318 annotate.Boolean(label=_("Enable WAMP connection"), |
381 annotate.Boolean(label=_("Enable WAMP connection"), |
319 default=wampConfigDefault)), |
382 default=wampConfigDefault)), |
320 ("url", |
383 ("url", |
321 annotate.String(label=_("WAMP Server URL"), |
384 annotate.String(label=_("WAMP Server URL"), |