def _modifyConfig(self, txn, configDelta): ## determine actual set of modified stuff ## modified = {} for c in configDelta: txn.execute("SELECT value FROM config WHERE key = ?", [ c, ]) res = txn.fetchone() if res: cval = json_loads(res[0]) if cval != configDelta[c]: modified[c] = configDelta[c] txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(configDelta[c]), c]) ## only something to do when there was an actual change ## if len(modified) > 0: ## recache config ## services = self.proto.factory.services if services.has_key("config"): services["config"].recache(txn) ## check if WebSocket option changed .. if so, notify WS factory ## wsOptionChanged = False for k in modified: if k[:2] == 'ws': wsOptionChanged = True break if wsOptionChanged: if self.proto.factory.services.has_key("appws"): self.proto.factory.services["appws"].setOptionsFromConfig() if self.proto.factory.services.has_key("echows"): self.proto.factory.services["echows"].setOptionsFromConfig( ) ## check for restart required ## for k in modified: if k in Database.SERVICES: self.proto.factory.issueRestartRequired() ## notify subscribers ## self.proto.dispatch(URI_EVENT + "on-config-modified", modified, [self.proto]) ## return modified set to caller ## return modified else: ## nothing changed ## return {}
def __repr__(self): r = {'id': self.id, #'connectionTimeout': self.connectionTimeout, 'host': self.host, 'port': self.port, 'database': self.database, 'user': self.user, 'password': self.password} return json_dumps(r)
def render_GET(self, request): port = self.config.get(self.port + "-port", None) tls = self.config.get(self.port + "-tls", None) if self.port == "hub-websocket": path = self.config.get("ws-websocket-path", "") res = (port, tls, path) else: res = (port, tls) return json_dumps(res)
def __repr__(self): r = {'id': self.id, 'driver': self.driver, 'host': self.host, 'port': self.port, 'database': self.database, 'user': self.user, 'password': self.password} return json_dumps(r)
def _storeObject(self, txn, uri, obj): ## check arguments ## if type(uri) not in [str, unicode]: raise Exception( URI_ERROR + "illegal-argument", "Expected type str/unicode for argument uri, but got %s" % str(type(uri))) ## resolve URI ## uri = self.proto.resolveOrPass(uri) modified = True txn.execute("SELECT obj FROM objstore WHERE uri = ?", [uri]) res = txn.fetchone() if res is not None: oldobj = json_loads(res[0]) if obj is not None: objser = json_dumps(obj) if objser != res[0]: txn.execute("UPDATE objstore SET obj = ? WHERE uri = ?", [objser, uri]) else: modified = False else: txn.execute("DELETE FROM objstore WHERE uri = ?", [uri]) else: oldobj = None if obj is not None: objser = json_dumps(obj) txn.execute("INSERT INTO objstore (uri, obj) VALUES (?, ?)", [uri, objser]) else: modified = False if modified: event = {'uri': uri, 'old': oldobj, 'new': obj} self.proto.dispatch(URI_EVENT + "on-store-modified", event, [self.proto]) event["uri"] = self.proto.shrink(uri) return event else: return None
def _createActivationRequest(self, txn, origin, licenseType, extra): LICENSE_TYPES = ['BETA'] if licenseType not in LICENSE_TYPES: raise Exception(URI_ERROR + "illegal-argument", "Unknown license type '%s'" % str(licenseType), LICENSE_TYPES) ## construct license activation request ## hostid = self.factory.services['platform'].getHostId() instanceid = self.serviceConfig._getSingleConfig(txn, "instance-id") msg = {'type': licenseType, 'host-id': hostid, 'instance-id': instanceid} dbcreated = self.serviceConfig._getSingleConfig(txn, "database-created") platform = self.factory.services['platform'].getPlatformInfo() network = self.factory.services['platform'].getNetworkConfig() msg['info'] = {'request-time': utcnow(), 'database-created': dbcreated, 'platform': platform, 'network': network} if extra is not None: msg['extra'] = extra log.msg("created license activation request: %s" % msg) rmsg = json_dumps(msg) ## load instance key pair ## pubkey = str(self.serviceConfig._getSingleConfig(txn, "instance-pub-key")) privkey = str(self.serviceConfig._getSingleConfig(txn, "instance-priv-key")) ## encrypt activation request for Tavendo public key ## and sign encrypted message using instance private key ## (emsg, skey, dig, sig) = encrypt_and_sign(rmsg, privkey, Database.WEBMQ_LICENSE_CA_PUBKEY) payload = "%s,%s,%s,%s,%s,%s" % (emsg, skey, dig, sig, urllib.quote_plus(pubkey), urllib.quote_plus(origin + "/doactivate")) #print payload return {'request': msg, 'url': self.factory.services['master'].licenseserver, 'payload': payload}
def _changePassword(self, txn, oldpassword, newpassword1, newpassword2): attrs = {"oldpassword": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN), "newpassword1": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN), "newpassword2": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN)} errcnt, errs = self.checkDictArg("user password", {"oldpassword": oldpassword, "newpassword1": newpassword1, "newpassword2": newpassword2}, attrs) if newpassword1 != newpassword2: errcnt += 1 if not errs.has_key('newpassword1') or errs.has_key('newpassword2'): p = 'newpassword1' else: p = 'newpassword2' if not errs.has_key(p): errs[p] = [] errs[p].append((self.shrink(URI_ERROR + "invalid-attribute-value"), "New password values do not match")) if errcnt: raise Exception(URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % errcnt, errs) txn.execute("SELECT value FROM config WHERE key = ?", ['admin-password']) res = txn.fetchone() if res: pw = str(json_loads(res[0])) if pw == oldpassword: if newpassword1 != oldpassword: txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(newpassword1), "admin-password"]) else: raise Exception(URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % 2, {'newpassword1': [(self.shrink(URI_ERROR + "attribute-value-unchanged"), "Password unchanged")], 'newpassword2': [(self.shrink(URI_ERROR + "attribute-value-unchanged"), "Password unchanged")]}) else: raise Exception(URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % 1, {'oldpassword': [(self.shrink(URI_ERROR + "invalid-attribute-value"), "Old password is invalid")]}) else: raise Exception(URI_ERROR + "internal-error", "Could not retrieve admin password from database.")
def __repr__(self): r = { 'id': self.id, 'driver': self.driver, 'host': self.host, 'port': self.port, 'database': self.database, 'user': self.user, 'password': self.password } return json_dumps(r)
def __repr__(self): r = { 'id': self.id, #'connectionTimeout': self.connectionTimeout, 'host': self.host, 'port': self.port, 'database': self.database, 'user': self.user, 'password': self.password } return json_dumps(r)
def __repr__(self): r = {'id': self.id, #'connectionTimeout': self.connectionTimeout, 'host': self.host, 'port': self.port, 'sid': self.sid, 'user': self.user, 'password': self.password, 'demo-user': self.demoUser, 'demo-password': self.demoPassword, } return json_dumps(r)
def _modifyConfig(self, txn, configDelta): ## determine actual set of modified stuff ## modified = {} for c in configDelta: txn.execute("SELECT value FROM config WHERE key = ?", [c,]) res = txn.fetchone() if res: cval = json_loads(res[0]) if cval != configDelta[c]: modified[c] = configDelta[c] txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(configDelta[c]), c]) ## only something to do when there was an actual change ## if len(modified) > 0: ## recache config ## services = self.proto.factory.services if services.has_key("config"): services["config"].recache(txn) ## check if WebSocket option changed .. if so, notify WS factory ## wsOptionChanged = False for k in modified: if k[:2] == 'ws': wsOptionChanged = True break if wsOptionChanged: if self.proto.factory.services.has_key("appws"): self.proto.factory.services["appws"].setOptionsFromConfig() if self.proto.factory.services.has_key("echows"): self.proto.factory.services["echows"].setOptionsFromConfig() ## check for restart required ## for k in modified: if k in Database.SERVICES: self.proto.factory.issueRestartRequired() ## notify subscribers ## self.proto.dispatch(URI_EVENT + "on-config-modified", modified, [self.proto]) ## return modified set to caller ## return modified else: ## nothing changed ## return {}
def __repr__(self): r = { 'id': self.id, #'connectionTimeout': self.connectionTimeout, 'host': self.host, 'port': self.port, 'sid': self.sid, 'user': self.user, 'password': self.password, 'demo-user': self.demoUser, 'demo-password': self.demoPassword, } return json_dumps(r)
def _storeObject(self, txn, uri, obj): ## check arguments ## if type(uri) not in [str, unicode]: raise Exception(URI_ERROR + "illegal-argument", "Expected type str/unicode for argument uri, but got %s" % str(type(uri))) ## resolve URI ## uri = self.proto.resolveOrPass(uri) modified = True txn.execute("SELECT obj FROM objstore WHERE uri = ?", [uri]) res = txn.fetchone() if res is not None: oldobj = json_loads(res[0]) if obj is not None: objser = json_dumps(obj) if objser != res[0]: txn.execute("UPDATE objstore SET obj = ? WHERE uri = ?", [objser, uri]) else: modified = False else: txn.execute("DELETE FROM objstore WHERE uri = ?", [uri]) else: oldobj = None if obj is not None: objser = json_dumps(obj) txn.execute("INSERT INTO objstore (uri, obj) VALUES (?, ?)", [uri, objser]) else: modified = False if modified: event = {'uri': uri, 'old': oldobj, 'new': obj} self.proto.dispatch(URI_EVENT + "on-store-modified", event, [self.proto]) event["uri"] = self.proto.shrink(uri) return event else: return None
def _setPassword(self, txn, password1, password2): txn.execute("SELECT value FROM config WHERE key = ?", ['admin-password']) res = txn.fetchone() if res: pw = json_loads(res[0]) if pw is not None: raise Exception((URI_ERROR + "invalid-invocation", "Initial password already set.")) else: raise Exception( URI_ERROR + "internal-error", "Could not retrieve admin password from database.") attrs = { "password1": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN), "password2": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN) } errcnt, errs = self.checkDictArg("user password", { "password1": password1, "password2": password2 }, attrs) if password1 != password2: errcnt += 1 if not errs.has_key('password1') or errs.has_key('password2'): p = 'password1' else: p = 'password2' if not errs.has_key(p): errs[p] = [] errs[p].append((self.shrink(URI_ERROR + "invalid-attribute-value"), "Passwords do not match")) if errcnt: raise Exception( URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % errcnt, errs) txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(password1), "admin-password"])
def _acceptEula(self, txn): txn.execute("SELECT value FROM config WHERE key = ?", ["eula-accepted"]) res = txn.fetchone() if res: eula_accepted = json_loads(res[0]) if eula_accepted: raise Exception(URI_ERROR + "illegal-invocation", "EULA already accepted.") else: now = utcnow() txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(now), "eula-accepted"]) self.factory.services["config"].recache(txn) return now else: raise Exception(URI_ERROR + "internal-error", "EULA key not found.")
def getDiagnostics(self, as_json=False): r = {} r["python-site-packages"] = self.getSitePackages() r["package-info"] = self.getPkgInfo() r["interface-config"] = self.getIfconfig() r["dmesg"] = self.getDmesg() r["twisted-version"] = self.getTwistedVersion() r["python-version"] = self.getPythonVersion() r["os-version"] = self.getOsVersion() r["host-id"] = self.getHostId() r["network-config"] = self.getNetworkConfig() r["sysctl"] = self.getSysctl() if as_json: return json_dumps(r, indent=3) else: return r
def getDiagnostics(self, as_json = False): r = {} r['python-site-packages'] = self.getSitePackages() r['package-info'] = self.getPkgInfo() r['interface-config'] = self.getIfconfig() r['dmesg'] = self.getDmesg() r['twisted-version'] = self.getTwistedVersion() r['python-version'] = self.getPythonVersion() r['os-version'] = self.getOsVersion() r['host-id'] = self.getHostId() r['network-config'] = self.getNetworkConfig() r['sysctl'] = self.getSysctl() if as_json: return json_dumps(r, indent = 3) else: return r
def getDiagnostics(self, as_json=False): r = {} r['python-site-packages'] = self.getSitePackages() r['package-info'] = self.getPkgInfo() r['interface-config'] = self.getIfconfig() r['dmesg'] = self.getDmesg() r['twisted-version'] = self.getTwistedVersion() r['python-version'] = self.getPythonVersion() r['os-version'] = self.getOsVersion() r['host-id'] = self.getHostId() r['network-config'] = self.getNetworkConfig() r['sysctl'] = self.getSysctl() if as_json: return json_dumps(r, indent=3) else: return r
def __repr__(self): r = { 'id': self.id, 'appkey': self.appkey, 'host': self.host, 'port': self.port, 'database': self.database, 'user': self.user, 'password': self.password, 'schemaList': self.schemaList, 'rpcBaseUri': self.rpcBaseUri, 'connectionPoolMinSize': self.connectionPoolMinSize, 'connectionPoolMaxSize': self.connectionPoolMaxSize, 'connectionTimeout': self.connectionTimeout, 'requestTimeout': self.requestTimeout, } return json_dumps(r)
def __repr__(self): r = { "id": self.id, "appkey": self.appkey, "host": self.host, "port": self.port, "database": self.database, "user": self.user, "password": self.password, "schemaList": self.schemaList, "rpcBaseUri": self.rpcBaseUri, "connectionPoolMinSize": self.connectionPoolMinSize, "connectionPoolMaxSize": self.connectionPoolMaxSize, "connectionTimeout": self.connectionTimeout, "requestTimeout": self.requestTimeout, } return json_dumps(r)
def __repr__(self): r = {'id': self.id, 'appkey': self.appkey, 'host': self.host, 'port': self.port, 'sid': self.sid, 'user': self.user, 'password': self.password, 'schemaList': self.schemaList, 'rpcBaseUri': self.rpcBaseUri, 'connectionPoolMinSize': self.connectionPoolMinSize, 'connectionPoolMaxSize': self.connectionPoolMaxSize, 'connectionTimeout': self.connectionTimeout, 'requestTimeout': self.requestTimeout, } return json_dumps(r)
def _setPassword(self, txn, password1, password2): txn.execute("SELECT value FROM config WHERE key = ?", ['admin-password']) res = txn.fetchone() if res: pw = json_loads(res[0]) if pw is not None: raise Exception((URI_ERROR + "invalid-invocation", "Initial password already set.")) else: raise Exception(URI_ERROR + "internal-error", "Could not retrieve admin password from database.") attrs = {"password1": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN), "password2": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN)} errcnt, errs = self.checkDictArg("user password", {"password1": password1, "password2": password2}, attrs) if password1 != password2: errcnt += 1 if not errs.has_key('password1') or errs.has_key('password2'): p = 'password1' else: p = 'password2' if not errs.has_key(p): errs[p] = [] errs[p].append((self.shrink(URI_ERROR + "invalid-attribute-value"), "Passwords do not match")) if errcnt: raise Exception(URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % errcnt, errs) txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(password1), "admin-password"])
def remoteCall(self, call): """ RPC handler remoting to Ext.Direct servers. This method is usually registered via registerHandlerMethodForRpc on a WAMP protocol. """ proto = call.proto uri = call.uri args = call.args ## extract extra information from RPC call handler argument (id, action, method, _) = call.extra ## get the Ext.Direct remote onto which we will forward the call remote = self.remotesById[id] ## construct the POST body d = {'action': action, 'method': method, 'data': args, 'type': 'rpc', 'tid': 1} body = json_dumps(d) if remote.forwardCookies and \ proto.cookies and \ proto.cookies.has_key(remote.routerDomain) and \ proto.cookies[remote.routerDomain] != "": cookie = str(proto.cookies[remote.routerDomain]) else: cookie = None if not remote.usePersistentConnections: ## Do HTTP/POST as individual request ## headers = {'Content-Type': 'application/json', 'User-Agent': ExtDirectRemoter.USER_AGENT} if cookie: headers['Cookie'] = cookie d = getPage(url = remote.routerUrl, method = 'POST', postdata = body, headers = headers, timeout = remote.requestTimeout, connectionTimeout = remote.connectionTimeout, followRedirect = remote.redirectLimit > 0) else: ## Do HTTP/POST via HTTP connection pool ## headers = {'Content-Type': ['application/json'], 'User-Agent': [ExtDirectRemoter.USER_AGENT]} if cookie: headers['Cookie'] = [cookie] agent = Agent(reactor, pool = self.httppools[remote.id], connectTimeout = remote.connectionTimeout) if remote.redirectLimit > 0: agent = RedirectAgent(agent, redirectLimit = remote.redirectLimit) ## FIXME: honor requestTimeout d = agent.request('POST', remote.routerUrl, Headers(headers), StringProducer(body)) def onResponse(response): if response.code == 200: finished = Deferred() response.deliverBody(StringReceiver(finished)) return finished else: return defer.fail("%s [%s]" % (response.code, response.phrase)) d.addCallback(onResponse) ## request information provided as error detail in case of call fails remotingRequest = {'provider': 'extdirect', 'router-url': remote.routerUrl, 'use-persistent-connections': remote.usePersistentConnections, 'request-timeout': remote.requestTimeout, 'connection-timeout': remote.connectionTimeout, 'action': action, 'method': method} d.addCallbacks(self._onRemoteCallResult, self._onRemoteCallError, callbackArgs = [remotingRequest], errbackArgs = [remotingRequest]) ## FIXME! d.addCallback(self.onAfterRemoteCallSuccess, id) d.addErrback(self.onAfterRemoteCallError, id) return d
def _setServicePorts(self, txn, ports, dryRun): ## check entry argument types ## if type(ports) != dict: raise Exception( URI_ERROR + "invalid-argument", "Invalid argument of type '%s' [expected dict]" % str(type(ports))) ## errors will be accumulated here (per port-key) ## errs = {} ## convenience handling in JS for u in Database.NETPORTS_TLS_PREFIXES: o = u + "-tlskey" if ports.has_key(o): if ports[o] == "null" or ports[o] == "": ports[o] = None ## check each port in change for itself ## uports = {} c_tls_flags = {} c_tls_keys = {} for k in ports.keys(): if k in Database.NETPORTS: try: port = int(ports[k]) except: errs[k] = ( self.proto.shrink(URI_ERROR + "not-an-integer"), "Invalid value '%s' for port '%s' (not an integer)" % (ports[k], k)) else: if port < 1 or port > 65535: errs[k] = (self.proto.shrink( URI_ERROR + "out-of-range" ), "Invalid value %d for port '%s' (out of valid range [1, 65535])" % (port, k)) else: if k in Database.NETPORTS_READONLY: errs[k] = (self.proto.shrink(URI_ERROR + "read-only"), "Port '%s' is read-only." % k) else: uports[k] = port elif k in Database.NETPORTS_TLS_FLAGS: if type(ports[k]) != bool: errs[k] = (self.proto.shrink(URI_ERROR + "invalid-attribute-type"), "Expected bool for attribute %s, got %s" % (k, str(type(ports[k])))) else: c_tls_flags[k] = ports[k] elif k in Database.NETPORTS_TLS_KEYS: if type(ports[k]) not in [str, unicode, types.NoneType]: errs[k] = ( self.proto.shrink(URI_ERROR + "invalid-attribute-type"), "Expected str/unicode for attribute %s, got %s" % (k, str(type(ports[k])))) else: c_tls_keys[k] = None if ports[k] is not None: ruri = str(ports[k]).strip() if ruri != "": uri = self.proto.resolveOrPass(ruri) id = self.proto.uriToId(uri) c_tls_keys[k] = id else: errs[str(k)] = (self.proto.shrink(URI_ERROR + "unknown-attribute"), "Illegal attribute '%s'" % k) ## determine all TLS flags/keys (changed+existing) and change set (all_tls_flags, changed_tls_flags) = self._getConfigChangeset( txn, Database.NETPORTS_TLS_FLAGS, c_tls_flags) (all_tls_keys, changed_tls_keys) = self._getConfigChangeset( txn, Database.NETPORTS_TLS_KEYS, c_tls_keys) for u in Database.NETPORTS_TLS_PREFIXES: o = u + "-tlskey" if changed_tls_keys.has_key(o): id = changed_tls_keys[o] if id is not None: txn.execute("SELECT cert FROM servicekey WHERE ID = ?", [id]) res = txn.fetchone() if res: if res[0] is None: errs[o] = ( self.proto.shrink( URI_ERROR + "servicekey-without-certificate"), "Service key with URI %s has no certificate" % URI_SERVICEKEY + id) else: errs[o] = ( self.proto.shrink(URI_ERROR + "no-such-object"), "No service key with URI %s" % URI_SERVICEKEY + id) else: if all_tls_flags[u + "-tls"]: errs[o] = ( self.proto.shrink( URI_ERROR + "tls-enabled-without-servicekey"), "TLS set to enabled, but no service key given.") o = u + "-tls" if changed_tls_flags.has_key(o): if changed_tls_flags[o] and all_tls_keys[u + "-tlskey"] is None: errs[o] = (self.proto.shrink(URI_ERROR + "missing-servicekey"), "TLS enabled, but service key missing.") ## For Admin Web/WebSocket pair, disallow running Web via TLS, but WebSocket non-TLS ## i.e. Firefox throws a "security-exception" when we try that .. ## For Hub Web/WebSocket we allow this, since both are "independent" services (that is Hub Web ## does not serve the HTML/JS that connects to Hub WebSocket) ## if all_tls_flags[ "admin-web-tls"] and not all_tls_flags["admin-websocket-tls"]: errs["admin-websocket-tls"] = (self.proto.shrink( URI_ERROR + "non-tls-websocket-from-tls-web" ), "TLS on WebSocket port set to enabled, but corresponding Web serving port running non-TLS." ) errs["admin-web-tls"] = (self.proto.shrink( URI_ERROR + "non-tls-websocket-from-tls-web" ), "TLS on WebSocket port set to enabled, but corresponding Web serving port running non-TLS." ) ## determine all ports (changed+existing) and change set ## (aports, cports) = self._getConfigChangeset(txn, Database.NETPORTS, uports) ## duplicate check ## if len(set(aports.values())) != len(aports): dups = {} for d in aports: if not dups.has_key(aports[d]): dups[aports[d]] = [] dups[aports[d]].append(d) for d in dups: if len(dups[d]) > 1: for k in dups[d]: errs[k] = (self.proto.shrink(URI_ERROR + "duplicate-value"), "Duplicate port %d for %s" % (d, str(dups[d]))) ## valid passive FTP port range ## if aports["ftp-passive-port-start"] > aports["ftp-passive-port-end"]: e = (self.proto.shrink(URI_ERROR + "invalid-range"), "Start port must be <= end port") errs["ftp-passive-port-start"] = e errs["ftp-passive-port-end"] = e ## check collisions of service ports with passive FTP port range ## passive_port_range = xrange(aports["ftp-passive-port-start"], aports["ftp-passive-port-end"] + 1) for p in Database.NETPORTS: if aports[p] in passive_port_range and p not in [ "ftp-passive-port-start", "ftp-passive-port-end" ]: e = (self.proto.shrink( URI_ERROR + "duplicate-value" ), "Duplicate port %d for %s collides with passive FTP port range %d-%d" % (aports[p], p, aports["ftp-passive-port-start"], aports["ftp-passive-port-end"])) errs[p] = e ## bail out on any errors accumulated ## if len(errs) > 0: raise Exception( URI_ERROR + "invalid-argument", "One or more invalid attributes (see errorDetails).", errs) ## now do the actual database update (if there is any change left) ## delta = {} delta.update(cports) delta.update(changed_tls_flags) delta.update(changed_tls_keys) if len(delta) > 0: if not dryRun: for p in delta: txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(delta[p]), p]) ## recache config services = self.proto.factory.services if services.has_key("config"): services["config"].recache(txn) ## automatically restart services when required restartRequired = len(cports) > 0 or len(changed_tls_flags) > 0 for t in Database.NETPORTS_TLS_PREFIXES: if delta.has_key(t + "-tlskey") and all_tls_flags[t + "-tls"]: restartRequired = True break for t in Database.NETPORTS_TLS_KEYS: if delta.has_key(t) and delta[t]: delta[t] = URI_SERVICEKEY + delta[t] if not dryRun: self.proto.dispatch(URI_EVENT + "on-service-ports-set", delta, [self.proto]) for t in Database.NETPORTS_TLS_KEYS: if delta.has_key(t) and delta[t]: delta[t] = self.proto.shrink(delta[t]) if restartRequired and not dryRun: from twisted.internet import reactor reactor.callLater(1, self.proto.serviceControl.restartHub) else: restartRequired = False ## return change set ## return [delta, restartRequired]
def _createActivationRequest(self, txn, origin, licenseType, extra): LICENSE_TYPES = ['BETA'] if licenseType not in LICENSE_TYPES: raise Exception(URI_ERROR + "illegal-argument", "Unknown license type '%s'" % str(licenseType), LICENSE_TYPES) ## construct license activation request ## hostid = self.factory.services['platform'].getHostId() instanceid = self.serviceConfig._getSingleConfig(txn, "instance-id") msg = { 'type': licenseType, 'host-id': hostid, 'instance-id': instanceid } dbcreated = self.serviceConfig._getSingleConfig( txn, "database-created") platform = self.factory.services['platform'].getPlatformInfo() network = self.factory.services['platform'].getNetworkConfig() msg['info'] = { 'request-time': utcnow(), 'database-created': dbcreated, 'platform': platform, 'network': network } if extra is not None: msg['extra'] = extra log.msg("created license activation request: %s" % msg) rmsg = json_dumps(msg) ## load instance key pair ## pubkey = str( self.serviceConfig._getSingleConfig(txn, "instance-pub-key")) privkey = str( self.serviceConfig._getSingleConfig(txn, "instance-priv-key")) ## encrypt activation request for Tavendo public key ## and sign encrypted message using instance private key ## (emsg, skey, dig, sig) = encrypt_and_sign(rmsg, privkey, Database.WEBMQ_LICENSE_CA_PUBKEY) payload = "%s,%s,%s,%s,%s,%s" % ( emsg, skey, dig, sig, urllib.quote_plus(pubkey), urllib.quote_plus(origin + "/doactivate")) #print payload return { 'request': msg, 'url': self.factory.services['master'].licenseserver, 'payload': payload }
def _changePassword(self, txn, oldpassword, newpassword1, newpassword2): attrs = { "oldpassword": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN), "newpassword1": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN), "newpassword2": (True, [str, unicode], AdminWebSocketProtocol.USER_PASSWORD_MIN_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_MAX_LENGTH, AdminWebSocketProtocol.USER_PASSWORD_PATTERN) } errcnt, errs = self.checkDictArg( "user password", { "oldpassword": oldpassword, "newpassword1": newpassword1, "newpassword2": newpassword2 }, attrs) if newpassword1 != newpassword2: errcnt += 1 if not errs.has_key('newpassword1') or errs.has_key( 'newpassword2'): p = 'newpassword1' else: p = 'newpassword2' if not errs.has_key(p): errs[p] = [] errs[p].append((self.shrink(URI_ERROR + "invalid-attribute-value"), "New password values do not match")) if errcnt: raise Exception( URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % errcnt, errs) txn.execute("SELECT value FROM config WHERE key = ?", ['admin-password']) res = txn.fetchone() if res: pw = str(json_loads(res[0])) if pw == oldpassword: if newpassword1 != oldpassword: txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(newpassword1), "admin-password"]) else: raise Exception( URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % 2, { 'newpassword1': [(self.shrink(URI_ERROR + "attribute-value-unchanged"), "Password unchanged")], 'newpassword2': [ (self.shrink(URI_ERROR + "attribute-value-unchanged"), "Password unchanged") ] }) else: raise Exception( URI_ERROR + "illegal-argument", "one or more illegal arguments (%d errors)" % 1, { 'oldpassword': [(self.shrink(URI_ERROR + "invalid-attribute-value"), "Old password is invalid")] }) else: raise Exception( URI_ERROR + "internal-error", "Could not retrieve admin password from database.")
def remoteCall(self, call): """ RPC handler remoting to REST servers. This method is usually registered via registerHandlerMethodForRpc on a WAMP protocol. """ proto = call.proto uri = call.uri args = call.args ## extract extra information from RPC call handler argument (id, method) = call.extra ## get the REST remote onto which we will forward the call remote = self.remotesById[id] body = None if method in ['GET', 'DELETE']: if len(args) != 1: raise Exception(URI_ERROR_REMOTING, "Invalid number of arguments (expected 1, was %d)" % len(args)) elif method in ['PUT', 'POST']: if len(args) != 2: raise Exception(URI_ERROR_REMOTING, "Invalid number of arguments (expected 2, was %d)" % len(args)) body = json_dumps(args[1]) else: ## should not arrive here! raise Exception("logic error") if remote.forwardCookies and \ proto.cookies and \ proto.cookies.has_key(remote.restDomain) and \ proto.cookies[remote.restDomain] != "": cookie = str(proto.cookies[remote.restDomain]) else: cookie = None if type(args[0]) not in [str, unicode]: raise Exception(URI_ERROR_REMOTING, "Invalid type for argument 1 (expected str, was %s)" % type(args[0])) url = urlparse.urljoin(str(remote.restBaseUrl), str(args[0])) if not remote.usePersistentConnections: ## Do HTTP/POST as individual request ## headers = {'Content-Type': 'application/json', 'User-Agent': RestRemoter.USER_AGENT} if cookie: headers['Cookie'] = cookie d = getPage(url = url, method = method, postdata = body, headers = headers, timeout = remote.requestTimeout, connectionTimeout = remote.connectionTimeout, followRedirect = remote.redirectLimit > 0) else: ## Do HTTP/POST via HTTP connection pool ## ## http://twistedmatrix.com/documents/12.1.0/web/howto/client.html ## ## avoid module level reactor import from twisted.web.client import Agent, RedirectAgent headers = {'Content-Type': ['application/json'], 'User-Agent': [RestRemoter.USER_AGENT]} if cookie: headers['Cookie'] = [cookie] agent = Agent(self.reactor, pool = self.httppools[remote.id], connectTimeout = remote.connectionTimeout) if remote.redirectLimit > 0: agent = RedirectAgent(agent, redirectLimit = remote.redirectLimit) ## FIXME: honor requestTimeout if body: d = agent.request(method, url, Headers(headers), StringProducer(body)) else: d = agent.request(method, url, Headers(headers)) def onResponse(response): if response.code == 200: finished = Deferred() response.deliverBody(StringReceiver(finished)) return finished else: return defer.fail("%s [%s]" % (response.code, response.phrase)) d.addCallback(onResponse) ## request information provided as error detail in case of call fails remotingRequest = {'provider': 'rest', 'rest-base-url': remote.restBaseUrl, 'use-persistent-connections': remote.usePersistentConnections, 'request-timeout': remote.requestTimeout, 'connection-timeout': remote.connectionTimeout, 'method': method} d.addCallbacks(self._onRemoteCallResult, self._onRemoteCallError, callbackArgs = [remotingRequest], errbackArgs = [remotingRequest]) ## FIXME! d.addCallback(self.onAfterRemoteCallSuccess, id) d.addErrback(self.onAfterRemoteCallError, id) return d
def remoteCall(self, call): """ RPC handler remoting to Ext.Direct servers. This method is usually registered via registerHandlerMethodForRpc on a WAMP protocol. """ proto = call.proto uri = call.uri args = call.args ## extract extra information from RPC call handler argument (id, action, method, _) = call.extra ## get the Ext.Direct remote onto which we will forward the call remote = self.remotesById[id] ## construct the POST body d = {'action': action, 'method': method, 'data': args, 'type': 'rpc', 'tid': 1} body = json_dumps(d) if remote.forwardCookies and \ proto.cookies and \ proto.cookies.has_key(remote.routerDomain) and \ proto.cookies[remote.routerDomain] != "": cookie = str(proto.cookies[remote.routerDomain]) else: cookie = None if not remote.usePersistentConnections: ## Do HTTP/POST as individual request ## headers = {'Content-Type': 'application/json', 'User-Agent': ExtDirectRemoter.USER_AGENT} if cookie: headers['Cookie'] = cookie d = getPage(url = remote.routerUrl, method = 'POST', postdata = body, headers = headers, timeout = remote.requestTimeout, connectionTimeout = remote.connectionTimeout, followRedirect = remote.redirectLimit > 0) else: ## Do HTTP/POST via HTTP connection pool ## headers = {'Content-Type': ['application/json'], 'User-Agent': [ExtDirectRemoter.USER_AGENT]} if cookie: headers['Cookie'] = [cookie] agent = Agent(self.reactor, pool = self.httppools[remote.id], connectTimeout = remote.connectionTimeout) if remote.redirectLimit > 0: agent = RedirectAgent(agent, redirectLimit = remote.redirectLimit) ## FIXME: honor requestTimeout d = agent.request('POST', remote.routerUrl, Headers(headers), StringProducer(body)) def onResponse(response): if response.code == 200: finished = Deferred() response.deliverBody(StringReceiver(finished)) return finished else: return defer.fail("%s [%s]" % (response.code, response.phrase)) d.addCallback(onResponse) ## request information provided as error detail in case of call fails remotingRequest = {'provider': 'extdirect', 'router-url': remote.routerUrl, 'use-persistent-connections': remote.usePersistentConnections, 'request-timeout': remote.requestTimeout, 'connection-timeout': remote.connectionTimeout, 'action': action, 'method': method} d.addCallbacks(self._onRemoteCallResult, self._onRemoteCallError, callbackArgs = [remotingRequest], errbackArgs = [remotingRequest]) ## FIXME! d.addCallback(self.onAfterRemoteCallSuccess, id) d.addErrback(self.onAfterRemoteCallError, id) return d
cur.execute(""" CREATE TABLE config ( key VARCHAR2(30) PRIMARY KEY, value VARCHAR2(4000) NOT NULL ) """) log.msg("database table '%s' created" % "config") ## store database schema version ## config = [('schema-category', 'demo'), ('schema-version', SCHEMAVERSION), ('schema-created', utcnow())] for key, value in config: cur.execute("INSERT INTO config (key, value) VALUES (:1, :2)", [key, json_dumps(value)]) conn.commit() log.msg("crossbar.io Demo schema created (version %d)!" % SCHEMAVERSION) else: log.msg("crossbar.io Demo schema dropped!") return dbschema.getSchemaVersion(conn, oraschema.LATESTVERSIONS) def setupSchema(app, conn): r = dbschema.getSchemaVersion(conn, oraschema.LATESTVERSIONS) if r['schema-version'] is not None:
def _callSp(self, conn, call): """ Call a remoted stored procedure. This is called using ConnectionPool.runWithConnection on the Twisted main thread from within DbRemoter.remoteCall. :param conn: A database connection from the pool. :type conn: obj :param session: Information on calling WAMP session. :type session: Instance of SessionInfo :param meta: SP metadata. :type meta: tuple :param args: SP calling arguments. :type args: list :return obj -- Result from calling the SP. """ session = call.proto.sessionInfo meta = call.extra args = call.args import cx_Oracle ## http://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm ## http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/datatypes.htm ## Supported: ## ## cx_Oracle.NUMBER ## cx_Oracle.NATIVE_FLOAT ## cx_Oracle.STRING ## cx_Oracle.UNICODE ## cx_Oracle.FIXED_CHAR ## cx_Oracle.FIXED_UNICODE ## cx_Oracle.DATETIME ## cx_Oracle.TIMESTAMP ## cx_Oracle.INTERVAL ## cx_Oracle.CURSOR ## ## Unsupported: ## ## cx_Oracle.OBJECT ## cx_Oracle.ROWID ## cx_Oracle.BINARY ## cx_Oracle.BFILE ## cx_Oracle.LOB ## cx_Oracle.BLOB ## cx_Oracle.CLOB ## cx_Oracle.NCLOB ## cx_Oracle.LONG_BINARY ## cx_Oracle.LONG_STRING ## cx_Oracle.LONG_UNICODE ## Issues: ## ## INTERVAL YEAR TO MONTH Support ## Python datetime.timedelta only supports periods up to 1 day. ## isodate has it's own Duration class to represent longer periods, ## but such objects can't be consumed by cx_Oracle. ## ## map of endpoint.return_type / endpoint.arg_types to ## ## (cx_Oracle bind var type, PL/SQL type, PL/SQL caster) ## ## for input parameters ## TYPEMAP = {'NUMBER': (cx_Oracle.NUMBER, None, None), 'VARCHAR2': (cx_Oracle.STRING, None, None), 'NVARCHAR2': (cx_Oracle.UNICODE, None, None), 'CHAR': (cx_Oracle.FIXED_CHAR, None, None), 'NCHAR': (cx_Oracle.FIXED_UNICODE, None, None), 'BINARY_FLOAT': (cx_Oracle.NATIVE_FLOAT, None, None), 'BINARY_DOUBLE': (cx_Oracle.NATIVE_FLOAT, None, None), 'DATE': (cx_Oracle.DATETIME, None, None), 'TIMESTAMP': (cx_Oracle.TIMESTAMP, None, None), 'TIMESTAMP WITH TIME ZONE': (cx_Oracle.TIMESTAMP, None, None), 'TIMESTAMP WITH LOCAL TIME ZONE': (cx_Oracle.TIMESTAMP, None, None), 'INTERVAL DAY TO SECOND': (cx_Oracle.INTERVAL, None, None), #'INTERVAL YEAR TO MONTH': (cx_Oracle.INTERVAL, None, None), 'REF CURSOR': (cx_Oracle.CURSOR, None, None), 'JSON': (cx_Oracle.CLOB, 'json', 'json'), 'JSON_VALUE': (cx_Oracle.CLOB, 'json_value', 'json_parser.parse_any'), 'JSON_LIST': (cx_Oracle.CLOB, 'json_list', 'json_list'), 'CROSSBAR_SESSION': (None, 'crossbar_session', 'crossbar_session')} ## these object types are treated specially ## JSONTYPES = ['JSON', 'JSON_VALUE', 'JSON_LIST'] DATETIMETYPES = ['DATE', 'TIMESTAMP'] INTERVALTYPES = ['INTERVAL DAY TO SECOND', #'INTERVAL YEAR TO MONTH', ] ## create or get prepared cursor for calling SP ## cur, extra = conn.getPrepared(meta.uri) if not cur: ## construct SQL statement for calling SP ## ## input parameters ## arg_types = [] if len(meta.arg_types) > 0: ## SP takes at least 1 input parameters ## iargs = [] i = 0 j = 0 while i < len(meta.arg_types): if meta.arg_types[i] == 'CROSSBAR_SESSION': if meta.arg_inouts[i] in ['IN', 'IN/OUT']: iargs.append('l_sess') else: raise Exception("invalid direction %s for session object parameter" % meta.arg_sess_inout) else: cast = TYPEMAP[meta.arg_types[i]][2] if cast: ## parameter value is casted iargs.append('%s(:in%d)' % (cast, j)) else: ## plain parameter iargs.append(':in%d' % j) arg_types.append(meta.arg_types[i]) j += 1 i += 1 s_args = "(" + ','.join(iargs) + ")" else: ## SP takes no input parameters ## s_args = "" ## return value ## if meta.return_type is not None: if meta.return_type not in JSONTYPES: s_out = ":out := " else: s_out = "" else: s_out = "" ## anonymous PL/SQL block ## ## For MODIFY_PACKAGE_STATE, see: ## - http://stackoverflow.com/questions/12688317/clear-oracle-session-state ## - http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_sessio.htm#CEGIICCC ## if meta.return_type in JSONTYPES: ## for JSON types the SQL is different since we need to cast from ## JSON object type to CLOB if meta.arg_sess_inout == "IN/OUT": sql = """ DECLARE l_sess crossbar_session := crossbar_session(:so1, :so2, JSON(:so3)); l_out %s; BEGIN DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE); l_out := %s%s; l_out.to_clob(:out); l_sess.data.to_clob(:sout); END; """ % (TYPEMAP[meta.return_type][1], meta.procedure, s_args) elif meta.arg_sess_inout == "IN": sql = """ DECLARE l_sess crossbar_session := crossbar_session(:so1, :so2, JSON(:so3)); l_out %s; BEGIN DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE); l_out := %s%s; l_out.to_clob(:out); END; """ % (TYPEMAP[meta.return_type][1], meta.procedure, s_args) else: sql = """ DECLARE l_out %s; BEGIN DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE); l_out := %s%s; l_out.to_clob(:out); END; """ % (TYPEMAP[meta.return_type][1], meta.procedure, s_args) else: if meta.arg_sess_inout == "IN/OUT": sql = """ DECLARE l_sess crossbar_session := crossbar_session(:so1, :so2, JSON(:so3)); BEGIN DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE); %s%s%s; l_sess.data.to_clob(:sout); END; """ % (s_out, meta.procedure, s_args) elif meta.arg_sess_inout == "IN": sql = """ DECLARE l_sess crossbar_session := crossbar_session(:so1, :so2, JSON(:so3)); BEGIN DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE); %s%s%s; END; """ % (s_out, meta.procedure, s_args) else: sql = """ BEGIN DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE); %s%s%s; END; """ % (s_out, meta.procedure, s_args) ## create fresh cursor and prepare SQL ## cur = conn.cursor() cur.prepare(sql) ## map var types to cx_Oracle types ## if meta.return_type in JSONTYPES: ## when calling a SP that returns a JSON type, the return bind var ## is the last, since we need to cast from JSON to CLOB/STRING ## ttypes = arg_types + [meta.return_type] elif meta.return_type is not None: ## otherwise if the SP returns something, the return bind var ## is the first ## ttypes = [meta.return_type] + arg_types else: ## otherwise when the SP does not return anything, bind vars ## are just input parameters ## ttypes = arg_types if meta.arg_sess_inout: ttypes = ['VARCHAR2', 'VARCHAR2', 'JSON'] + ttypes if meta.arg_sess_inout == "IN/OUT": ttypes.append('JSON') atypes = [TYPEMAP[x][0] for x in ttypes] ## setup cx_Oracle bind vars curvars = cur.setinputsizes(*atypes) ## save the prepared cursor and bindvars. we also save the SQL ## for debugging purposes ## conn.savePrepared(meta.uri, cur, (curvars, sql)) else: ## cursor was saved previously - get the bind vars .. ## curvars, sql = extra ## set parameters and call SP ## ## indexes 'args' i = 0 ## indexes 'curvars' if meta.return_type in JSONTYPES: ## json return value (needs to be unwrapped via local PL/SQL var) j = 0 elif meta.return_type is not None: ## scalar/refcursor return j = 1 else: ## no return value j = 0 ## inject session information ## if meta.arg_sess_inout: curvars[0].setvalue(0, session.sessionId) curvars[1].setvalue(0, session.authenticatedAs) curvars[2].setvalue(0, json_dumps(session.data)) j += 3 ## indexes 'meta.arg_types' k = 0 while k < len(meta.arg_types): if meta.arg_types[k] == 'CROSSBAR_SESSION': k += 1 else: if meta.arg_types[k] in JSONTYPES: val = json_dumps(args[i]) elif meta.arg_types[k] in DATETIMETYPES: ## Target argument is a DATE/TIMESTAMP. Need to convert ## to Python datetime.datetime from string. We use ISO 8601 format. ## if args[i] is not None: try: val = isodate.parse_datetime(args[i]) except Exception, e: raise Exception("invalid value for datetime/timestamp - expected a string in ISO 8601 datetime format [%s]" % e) else: val = None elif meta.arg_types[k] in INTERVALTYPES: ## Target argument is a INTERVAL. Need to convert ## to Python datetime.timedelta from string. We use ISO 8601 format. ## if args[i] is not None: try: val = isodate.parse_duration(args[i]) if not isinstance(val, datetime.timedelta): ## val will be an instance of isodate.Duration, due to ## limits of Python datetime.timedelta raise Exception("invalid value for literal - ISO 8601 years/months currently unsupported") except Exception, e: raise Exception("invalid value for interval - expected a string in ISO 8601 period format [%s]" % e) else: val = None
def remoteCall(self, call): """ RPC handler remoting to REST servers. This method is usually registered via registerHandlerMethodForRpc on a WAMP protocol. """ proto = call.proto uri = call.uri args = call.args ## extract extra information from RPC call handler argument (id, method) = call.extra ## get the REST remote onto which we will forward the call remote = self.remotesById[id] body = None if method in ['GET', 'DELETE']: if len(args) != 1: raise Exception( URI_ERROR_REMOTING, "Invalid number of arguments (expected 1, was %d)" % len(args)) elif method in ['PUT', 'POST']: if len(args) != 2: raise Exception( URI_ERROR_REMOTING, "Invalid number of arguments (expected 2, was %d)" % len(args)) body = json_dumps(args[1]) else: ## should not arrive here! raise Exception("logic error") if remote.forwardCookies and \ proto.cookies and \ proto.cookies.has_key(remote.restDomain) and \ proto.cookies[remote.restDomain] != "": cookie = str(proto.cookies[remote.restDomain]) else: cookie = None if type(args[0]) not in [str, unicode]: raise Exception( URI_ERROR_REMOTING, "Invalid type for argument 1 (expected str, was %s)" % type(args[0])) url = urlparse.urljoin(str(remote.restBaseUrl), str(args[0])) if not remote.usePersistentConnections: ## Do HTTP/POST as individual request ## headers = { 'Content-Type': 'application/json', 'User-Agent': RestRemoter.USER_AGENT } if cookie: headers['Cookie'] = cookie d = getPage(url=url, method=method, postdata=body, headers=headers, timeout=remote.requestTimeout, connectionTimeout=remote.connectionTimeout, followRedirect=remote.redirectLimit > 0) else: ## Do HTTP/POST via HTTP connection pool ## ## http://twistedmatrix.com/documents/12.1.0/web/howto/client.html ## ## avoid module level reactor import from twisted.web.client import Agent, RedirectAgent headers = { 'Content-Type': ['application/json'], 'User-Agent': [RestRemoter.USER_AGENT] } if cookie: headers['Cookie'] = [cookie] agent = Agent(self.reactor, pool=self.httppools[remote.id], connectTimeout=remote.connectionTimeout) if remote.redirectLimit > 0: agent = RedirectAgent(agent, redirectLimit=remote.redirectLimit) ## FIXME: honor requestTimeout if body: d = agent.request(method, url, Headers(headers), StringProducer(body)) else: d = agent.request(method, url, Headers(headers)) def onResponse(response): if response.code == 200: finished = Deferred() response.deliverBody(StringReceiver(finished)) return finished else: return defer.fail("%s [%s]" % (response.code, response.phrase)) d.addCallback(onResponse) ## request information provided as error detail in case of call fails remotingRequest = { 'provider': 'rest', 'rest-base-url': remote.restBaseUrl, 'use-persistent-connections': remote.usePersistentConnections, 'request-timeout': remote.requestTimeout, 'connection-timeout': remote.connectionTimeout, 'method': method } d.addCallbacks(self._onRemoteCallResult, self._onRemoteCallError, callbackArgs=[remotingRequest], errbackArgs=[remotingRequest]) ## FIXME! d.addCallback(self.onAfterRemoteCallSuccess, id) d.addErrback(self.onAfterRemoteCallError, id) return d
def _setServicePorts(self, txn, ports, dryRun): ## check entry argument types ## if type(ports) != dict: raise Exception(URI_ERROR + "invalid-argument", "Invalid argument of type '%s' [expected dict]" % str(type(ports))) ## errors will be accumulated here (per port-key) ## errs = {} ## convenience handling in JS for u in Database.NETPORTS_TLS_PREFIXES: o = u + "-tlskey" if ports.has_key(o): if ports[o] == "null" or ports[o] == "": ports[o] = None ## check each port in change for itself ## uports = {} c_tls_flags = {} c_tls_keys = {} for k in ports.keys(): if k in Database.NETPORTS: try: port = int(ports[k]) except: errs[k] = (self.proto.shrink(URI_ERROR + "not-an-integer"), "Invalid value '%s' for port '%s' (not an integer)" % (ports[k], k)) else: if port < 1 or port > 65535: errs[k] = (self.proto.shrink(URI_ERROR + "out-of-range"), "Invalid value %d for port '%s' (out of valid range [1, 65535])" % (port, k)) else: if k in Database.NETPORTS_READONLY: errs[k] = (self.proto.shrink(URI_ERROR + "read-only"), "Port '%s' is read-only." % k) else: uports[k] = port elif k in Database.NETPORTS_TLS_FLAGS: if type(ports[k]) != bool: errs[k] = (self.proto.shrink(URI_ERROR + "invalid-attribute-type"), "Expected bool for attribute %s, got %s" % (k, str(type(ports[k])))) else: c_tls_flags[k] = ports[k] elif k in Database.NETPORTS_TLS_KEYS: if type(ports[k]) not in [str, unicode, types.NoneType]: errs[k] = (self.proto.shrink(URI_ERROR + "invalid-attribute-type"), "Expected str/unicode for attribute %s, got %s" % (k, str(type(ports[k])))) else: c_tls_keys[k] = None if ports[k] is not None: ruri = str(ports[k]).strip() if ruri != "": uri = self.proto.resolveOrPass(ruri) id = self.proto.uriToId(uri) c_tls_keys[k] = id else: errs[str(k)] = (self.proto.shrink(URI_ERROR + "unknown-attribute"), "Illegal attribute '%s'" % k) ## determine all TLS flags/keys (changed+existing) and change set (all_tls_flags, changed_tls_flags) = self._getConfigChangeset(txn, Database.NETPORTS_TLS_FLAGS, c_tls_flags) (all_tls_keys, changed_tls_keys) = self._getConfigChangeset(txn, Database.NETPORTS_TLS_KEYS, c_tls_keys) for u in Database.NETPORTS_TLS_PREFIXES: o = u + "-tlskey" if changed_tls_keys.has_key(o): id = changed_tls_keys[o] if id is not None: txn.execute("SELECT cert FROM servicekey WHERE ID = ?", [id]) res = txn.fetchone() if res: if res[0] is None: errs[o] = (self.proto.shrink(URI_ERROR + "servicekey-without-certificate"), "Service key with URI %s has no certificate" % URI_SERVICEKEY + id) else: errs[o] = (self.proto.shrink(URI_ERROR + "no-such-object"), "No service key with URI %s" % URI_SERVICEKEY + id) else: if all_tls_flags[u + "-tls"]: errs[o] = (self.proto.shrink(URI_ERROR + "tls-enabled-without-servicekey"), "TLS set to enabled, but no service key given.") o = u + "-tls" if changed_tls_flags.has_key(o): if changed_tls_flags[o] and all_tls_keys[u + "-tlskey"] is None: errs[o] = (self.proto.shrink(URI_ERROR + "missing-servicekey"), "TLS enabled, but service key missing.") ## For Admin Web/WebSocket pair, disallow running Web via TLS, but WebSocket non-TLS ## i.e. Firefox throws a "security-exception" when we try that .. ## For Hub Web/WebSocket we allow this, since both are "independent" services (that is Hub Web ## does not serve the HTML/JS that connects to Hub WebSocket) ## if all_tls_flags["admin-web-tls"] and not all_tls_flags["admin-websocket-tls"]: errs["admin-websocket-tls"] = (self.proto.shrink(URI_ERROR + "non-tls-websocket-from-tls-web"), "TLS on WebSocket port set to enabled, but corresponding Web serving port running non-TLS.") errs["admin-web-tls"] = (self.proto.shrink(URI_ERROR + "non-tls-websocket-from-tls-web"), "TLS on WebSocket port set to enabled, but corresponding Web serving port running non-TLS.") ## determine all ports (changed+existing) and change set ## (aports, cports) = self._getConfigChangeset(txn, Database.NETPORTS, uports) ## duplicate check ## if len(set(aports.values())) != len(aports): dups = {} for d in aports: if not dups.has_key(aports[d]): dups[aports[d]] = [] dups[aports[d]].append(d) for d in dups: if len(dups[d]) > 1: for k in dups[d]: errs[k] = (self.proto.shrink(URI_ERROR + "duplicate-value"), "Duplicate port %d for %s" % (d, str(dups[d]))) ## valid passive FTP port range ## if aports["ftp-passive-port-start"] > aports["ftp-passive-port-end"]: e = (self.proto.shrink(URI_ERROR + "invalid-range"), "Start port must be <= end port") errs["ftp-passive-port-start"] = e errs["ftp-passive-port-end"] = e ## check collisions of service ports with passive FTP port range ## passive_port_range = xrange(aports["ftp-passive-port-start"], aports["ftp-passive-port-end"] + 1) for p in Database.NETPORTS: if aports[p] in passive_port_range and p not in ["ftp-passive-port-start", "ftp-passive-port-end"]: e = (self.proto.shrink(URI_ERROR + "duplicate-value"), "Duplicate port %d for %s collides with passive FTP port range %d-%d" % (aports[p], p, aports["ftp-passive-port-start"], aports["ftp-passive-port-end"]) ) errs[p] = e ## bail out on any errors accumulated ## if len(errs) > 0: raise Exception(URI_ERROR + "invalid-argument", "One or more invalid attributes (see errorDetails).", errs) ## now do the actual database update (if there is any change left) ## delta = {} delta.update(cports) delta.update(changed_tls_flags) delta.update(changed_tls_keys) if len(delta) > 0: if not dryRun: for p in delta: txn.execute("UPDATE config SET value = ? WHERE key = ?", [json_dumps(delta[p]), p]) ## recache config services = self.proto.factory.services if services.has_key("config"): services["config"].recache(txn) ## automatically restart services when required restartRequired = len(cports) > 0 or len(changed_tls_flags) > 0 for t in Database.NETPORTS_TLS_PREFIXES: if delta.has_key(t + "-tlskey") and all_tls_flags[t + "-tls"]: restartRequired = True break for t in Database.NETPORTS_TLS_KEYS: if delta.has_key(t) and delta[t]: delta[t] = URI_SERVICEKEY + delta[t] if not dryRun: self.proto.dispatch(URI_EVENT + "on-service-ports-set", delta, [self.proto]) for t in Database.NETPORTS_TLS_KEYS: if delta.has_key(t) and delta[t]: delta[t] = self.proto.shrink(delta[t]) if restartRequired and not dryRun: from twisted.internet import reactor reactor.callLater(1, self.proto.serviceControl.restartHub) else: restartRequired = False ## return change set ## return [delta, restartRequired]