from time import sleep import subprocess,json, os from OpenSSL import SSL from twisted.internet import reactor,ssl from twisted.python import log from twisted.web import http from twisted.web.server import Site from twisted.web.static import File from twisted.web.static import Data from twisted.application import service, strports from twisted.web.guard import DigestCredentialFactory from twisted.web.resource import Resource from twisted.web.wsgi import WSGIResource from twisted.python.threadpool import ThreadPool from autobahn.resource import WebSocketResource, HTTPChannelHixie76Aware from autobahn.websocket import WebSocketServerFactory, \ WebSocketServerProtocol, \ listenWS from crypt import crypt factory = None class EchoServerProtocol(WebSocketServerProtocol): def onOpen(self): counter=0 message = info() self.sendMessage(message) self.factory.register(self) def onConnect(self,request): if(self.transport not in self.factory.clients): raise http.HttpException(1008,"Nope") def connectionLost(self, reason): WebSocketServerProtocol.connectionLost(self, reason) self.factory.unregister(self) def onMessage(self,msg,binary): self.factory.broadcast(msg) class BroadcastServerFactory(WebSocketServerFactory): def __init__(self, url, debug = False, debugCodePaths = False): WebSocketServerFactory.__init__(self, url, debug = debug, debugCodePaths = debugCodePaths) self.clients = [] reactor.callLater(10,self.tick) def tick(self): self.broadcast("hello world") reactor.callLater(10,self.tick) def register(self, client): if not client in self.clients: print "registered client " + client.peerstr self.clients.append(client) def unregister(self, client): if client in self.clients: print "unregistered client " + client.peerstr self.clients.remove(client) def broadcast(self,data): print 'sending data',data for c in self.clients: c.sendMessage(data) # Create and start a thread pool, wsgiThreadPool = ThreadPool() wsgiThreadPool.start() # ensuring that it will be stopped when the reactor shuts down reactor.addSystemEventTrigger('after', 'shutdown', wsgiThreadPool.stop) def application(environ, start_response): start_response('200 OK', [('Content-type','text/plain')]) return ['Hello World!'] __all__ = ['HtPasswdWrapper'] class UnauthorizedResource(Resource): isLeaf = 1 def __init__(self, realm, errorPage): Resource.__init__(self) self.realm = realm self.errorPage = errorPage def render(self, request): request.setResponseCode(http.UNAUTHORIZED) # FIXME: Does realm need to be quoted? request.setHeader('WWW-authenticate', 'basic realm="%s"' % self.realm) return self.errorPage.render(request) class HtPasswdWrapper(Resource): """Apache-style htpasswd protection for a resource. Requires a client to authenticate (using HTTP basic auth) to access a resource. If they fail to authenticate, or their username and password aren't accepted, they receive an error page. The username and password are checked against a htpasswd(1) file using crypt. The file is re-read for every request. TODO: - Integrate this into twisted.web.woven.guard / newcred? - Support MD5 password hashes in the htpasswd file, as well as crypt. @cvar unauthorizedPage: L{Resource} that will be used to render the error page given when a user is unauthorized. """ unauthorizedPage = Data( 'Access Denied.', 'text/html' ) def __init__(self, resource, htpasswdFilename, realm): """Constructor. @param resource: resource to protect with authentication. @param htpasswdFilename: filename of an htpasswd file to authenticate with. Currently only crypt(3)-format passwords are supported. @param realm: HTTP auth realm. """ Resource.__init__(self) self.resource = resource self.filename = htpasswdFilename self.realm = realm def getChildWithDefault(self, path, request): if self.authenticateUser(request): return self.resource.getChildWithDefault(path, request) else: return self.unauthorized() def render(self, request): global factory if self.authenticateUser(request): factory.client.append(self) return self.resource.render(request) else: return self.unauthorized().render(request) def authenticateUser(self, request): username, password = request.getUser(), request.getPassword() lines = [l.rstrip().split(':', 1) for l in file(self.filename).readlines()] lines = [l for l in lines if l[0] == username] if not lines: return 0 hashedPassword = lines[0][1] return hashedPassword == crypt(password, hashedPassword[:2]) def unauthorized(self): return UnauthorizedResource(self.realm, self.unauthorizedPage) if __name__ == '__main__': contextFactory = ssl.DefaultOpenSSLContextFactory('server.key', 'server.crt') print 'securing wss..' ServerFactory = BroadcastServerFactory factory = ServerFactory("wss://localhost:9000",debug = False) print 'opening ws port 9000' factory.protocol = EchoServerProtocol factory.setProtocolOptions(allowHixie76 = True) listenWS(factory,contextFactory) webdir = File("www/") web = Site(HtPasswdWrapper(webdir,'ht.passwd','websocket')) web.protocol = HTTPChannelHixie76Aware webdir.contentTypes['.crt'] = 'application/x-x509-ca-cert' resource = WSGIResource(reactor, reactor.getThreadPool(), application) reactor.listenSSL(443,web,contextFactory) print 'listening on port 443 for webserver' reactor.run()