Advertisement
SimplyAutomationized

Websocket with Basic/Digest Auth

Sep 19th, 2015
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.98 KB | None | 0 0
  1. from time import sleep
  2. import subprocess,json, os
  3. from OpenSSL import SSL
  4. from twisted.internet import reactor,ssl
  5. from twisted.python import log
  6. from twisted.web import http
  7. from twisted.web.server import Site
  8. from twisted.web.static import File
  9. from twisted.web.static import Data
  10. from twisted.application import service, strports
  11. from twisted.web.guard import DigestCredentialFactory
  12. from twisted.web.resource import Resource
  13. from twisted.web.wsgi import WSGIResource
  14. from twisted.python.threadpool import ThreadPool
  15.  
  16. from autobahn.resource import WebSocketResource, HTTPChannelHixie76Aware
  17. from autobahn.websocket import WebSocketServerFactory, \
  18.                                WebSocketServerProtocol, \
  19.                                listenWS
  20. from crypt import crypt
  21. factory = None
  22. class EchoServerProtocol(WebSocketServerProtocol):
  23.  
  24.    def onOpen(self):
  25.     counter=0
  26.     message = info()
  27.     self.sendMessage(message)
  28.     self.factory.register(self)
  29.    def onConnect(self,request):
  30.     if(self.transport not in self.factory.clients):
  31.           raise http.HttpException(1008,"Nope")
  32.  
  33.    def connectionLost(self, reason):
  34.       WebSocketServerProtocol.connectionLost(self, reason)
  35.       self.factory.unregister(self)
  36.    def onMessage(self,msg,binary):
  37.       self.factory.broadcast(msg)  
  38.  
  39. class BroadcastServerFactory(WebSocketServerFactory):
  40.    def __init__(self, url, debug = False, debugCodePaths = False):
  41.       WebSocketServerFactory.__init__(self, url, debug = debug, debugCodePaths = debugCodePaths)
  42.       self.clients = []
  43.       reactor.callLater(10,self.tick)
  44.  
  45.    def tick(self):
  46.     self.broadcast("hello world")
  47.     reactor.callLater(10,self.tick)
  48.  
  49.    def register(self, client):
  50.       if not client in self.clients:
  51.      print "registered client " + client.peerstr
  52.          self.clients.append(client)
  53.  
  54.    def unregister(self, client):
  55.       if client in self.clients:
  56.          print "unregistered client " + client.peerstr
  57.          self.clients.remove(client)
  58.  
  59.    def broadcast(self,data):
  60.     print 'sending data',data
  61.     for c in self.clients:
  62.        c.sendMessage(data)
  63.        
  64. # Create and start a thread pool,
  65. wsgiThreadPool = ThreadPool()
  66. wsgiThreadPool.start()
  67.  
  68. # ensuring that it will be stopped when the reactor shuts down
  69. reactor.addSystemEventTrigger('after', 'shutdown', wsgiThreadPool.stop)    
  70.  
  71. def application(environ, start_response):
  72.     start_response('200 OK', [('Content-type','text/plain')])
  73.     return ['Hello World!']
  74. __all__ = ['HtPasswdWrapper']
  75.  
  76. class UnauthorizedResource(Resource):
  77.     isLeaf = 1
  78.     def __init__(self, realm, errorPage):
  79.         Resource.__init__(self)
  80.         self.realm = realm
  81.         self.errorPage = errorPage
  82.  
  83.     def render(self, request):
  84.         request.setResponseCode(http.UNAUTHORIZED)
  85.         # FIXME: Does realm need to be quoted?
  86.         request.setHeader('WWW-authenticate', 'basic realm="%s"' % self.realm)
  87.         return self.errorPage.render(request)
  88.  
  89. class HtPasswdWrapper(Resource):
  90.     """Apache-style htpasswd protection for a resource.
  91.  
  92.    Requires a client to authenticate (using HTTP basic auth) to access a
  93.    resource.  If they fail to authenticate, or their username and password
  94.    aren't accepted, they receive an error page.
  95.  
  96.    The username and password are checked against a htpasswd(1) file using
  97.    crypt.  The file is re-read for every request.
  98.  
  99.    TODO:
  100.        - Integrate this into twisted.web.woven.guard / newcred?
  101.        - Support MD5 password hashes in the htpasswd file, as well as crypt.
  102.  
  103.    @cvar unauthorizedPage: L{Resource} that will be used to render the error
  104.        page given when a user is unauthorized.
  105.    """
  106.  
  107.     unauthorizedPage = Data(
  108.         '<html><body>Access Denied.</body></html>', 'text/html'
  109.     )
  110.  
  111.     def __init__(self, resource, htpasswdFilename, realm):
  112.         """Constructor.
  113.        
  114.        @param resource: resource to protect with authentication.
  115.        @param htpasswdFilename: filename of an htpasswd file to authenticate
  116.            with.  Currently only crypt(3)-format passwords are supported.
  117.        @param realm: HTTP auth realm.
  118.        """
  119.         Resource.__init__(self)
  120.         self.resource = resource
  121.         self.filename = htpasswdFilename
  122.         self.realm = realm
  123.  
  124.     def getChildWithDefault(self, path, request):
  125.         if self.authenticateUser(request):
  126.             return self.resource.getChildWithDefault(path, request)
  127.         else:
  128.             return self.unauthorized()
  129.  
  130.     def render(self, request):
  131.         global factory
  132.         if self.authenticateUser(request):
  133.             factory.client.append(self)
  134.             return self.resource.render(request)
  135.         else:
  136.             return self.unauthorized().render(request)
  137.  
  138.     def authenticateUser(self, request):
  139.         username, password = request.getUser(), request.getPassword()
  140.         lines = [l.rstrip().split(':', 1) for l in file(self.filename).readlines()]
  141.         lines = [l for l in lines if l[0] == username]
  142.         if not lines:
  143.             return 0
  144.         hashedPassword = lines[0][1]
  145.         return hashedPassword == crypt(password, hashedPassword[:2])
  146.  
  147.     def unauthorized(self):
  148.         return UnauthorizedResource(self.realm, self.unauthorizedPage)
  149.    
  150. if __name__ == '__main__':
  151.    contextFactory = ssl.DefaultOpenSSLContextFactory('server.key',
  152.             'server.crt')
  153.    print 'securing wss..'
  154.    ServerFactory = BroadcastServerFactory
  155.    factory = ServerFactory("wss://localhost:9000",debug = False)
  156.    print 'opening ws port 9000'
  157.    factory.protocol = EchoServerProtocol
  158.    factory.setProtocolOptions(allowHixie76 = True)
  159.    listenWS(factory,contextFactory)
  160.    webdir = File("www/")
  161.    web = Site(HtPasswdWrapper(webdir,'ht.passwd','websocket'))
  162.    web.protocol = HTTPChannelHixie76Aware
  163.    webdir.contentTypes['.crt'] = 'application/x-x509-ca-cert'
  164.    resource = WSGIResource(reactor, reactor.getThreadPool(), application)
  165.    reactor.listenSSL(443,web,contextFactory)
  166.    print 'listening on port 443 for webserver'
  167.    reactor.run()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement