Changeset 179 for etherws/trunk


Ignore:
Timestamp:
07/25/12 22:50:48 (12 years ago)
Author:
atzm
Message:
  • handle authentication per instance, not class
File:
1 edited

Legend:

Unmodified
Added
Removed
  • etherws/trunk/etherws.py

    r178 r179  
    198198 
    199199 
     200class Htpasswd(object): 
     201    def __init__(self, path): 
     202        self._path = path 
     203        self._stat = None 
     204        self._data = {} 
     205 
     206    def auth(self, name, passwd): 
     207        passwd = base64.b64encode(hashlib.sha1(passwd).digest()) 
     208        return self._data.get(name) == passwd 
     209 
     210    def load(self): 
     211        old_stat = self._stat 
     212 
     213        with open(self._path) as fp: 
     214            fileno = fp.fileno() 
     215            fcntl.flock(fileno, fcntl.LOCK_SH | fcntl.LOCK_NB) 
     216            self._stat = os.fstat(fileno) 
     217 
     218            unchanged = old_stat and \ 
     219                        old_stat.st_ino == self._stat.st_ino and \ 
     220                        old_stat.st_dev == self._stat.st_dev and \ 
     221                        old_stat.st_mtime == self._stat.st_mtime 
     222 
     223            if not unchanged: 
     224                self._data = self._parse(fp) 
     225 
     226        return self 
     227 
     228    def _parse(self, fp): 
     229        data = {} 
     230        for line in fp: 
     231            line = line.strip() 
     232            if 0 <= line.find(':'): 
     233                name, passwd = line.split(':', 1) 
     234                if passwd.startswith('{SHA}'): 
     235                    data[name] = passwd[5:] 
     236        return data 
     237 
     238 
    200239class TapHandler(DebugMixIn): 
    201240    READ_SIZE = 65535 
     
    258297 
    259298class EtherWebSocketHandler(tornado.websocket.WebSocketHandler, DebugMixIn): 
    260     def __init__(self, app, req, switch, debug=False): 
     299    def __init__(self, app, req, switch, htpasswd=None, debug=False): 
    261300        super(EtherWebSocketHandler, self).__init__(app, req) 
    262301        self._switch = switch 
     302        self._htpasswd = htpasswd 
    263303        self._debug = debug 
     304 
     305        if self._htpasswd: 
     306            self._htpasswd = Htpasswd(self._htpasswd) 
    264307 
    265308    def open(self): 
     
    273316        self._switch.unregister_port(self) 
    274317        self.dprintf('disconnected: %s\n', lambda: self.request.remote_ip) 
     318 
     319    def _execute(self, transforms, *args, **kwargs): 
     320        def do_execute(): 
     321            sp = super(EtherWebSocketHandler, self) 
     322            return sp._execute(transforms, *args, **kwargs) 
     323 
     324        def auth_required(): 
     325            self.stream.write(tornado.escape.utf8( 
     326                'HTTP/1.1 401 Authorization Required\r\n' 
     327                'WWW-Authenticate: Basic realm=etherws\r\n\r\n' 
     328            )) 
     329            self.stream.close() 
     330 
     331        try: 
     332            if not self._htpasswd: 
     333                return do_execute() 
     334 
     335            creds = self.request.headers.get('Authorization') 
     336 
     337            if not creds or not creds.startswith('Basic '): 
     338                return auth_required() 
     339 
     340            name, passwd = base64.b64decode(creds[6:]).split(':', 1) 
     341 
     342            if self._htpasswd.load().auth(name, passwd): 
     343                return do_execute() 
     344        except: 
     345            traceback.print_exc() 
     346 
     347        return auth_required() 
    275348 
    276349 
     
    334407            traceback.print_exc() 
    335408        self.close() 
    336  
    337  
    338 class Htpasswd(object): 
    339     def __init__(self, path): 
    340         self._path = path 
    341         self._stat = None 
    342         self._data = {} 
    343  
    344     def auth(self, name, passwd): 
    345         passwd = base64.b64encode(hashlib.sha1(passwd).digest()) 
    346         return self._data.get(name) == passwd 
    347  
    348     def load(self): 
    349         old_stat = self._stat 
    350  
    351         with open(self._path) as fp: 
    352             fileno = fp.fileno() 
    353             fcntl.flock(fileno, fcntl.LOCK_SH | fcntl.LOCK_NB) 
    354             self._stat = os.fstat(fileno) 
    355  
    356             unchanged = old_stat and \ 
    357                         old_stat.st_ino == self._stat.st_ino and \ 
    358                         old_stat.st_dev == self._stat.st_dev and \ 
    359                         old_stat.st_mtime == self._stat.st_mtime 
    360  
    361             if not unchanged: 
    362                 self._data = self._parse(fp) 
    363  
    364     def _parse(self, fp): 
    365         data = {} 
    366         for line in fp: 
    367             line = line.strip() 
    368             if 0 <= line.find(':'): 
    369                 name, passwd = line.split(':', 1) 
    370                 if passwd.startswith('{SHA}'): 
    371                     data[name] = passwd[5:] 
    372         return data 
    373  
    374  
    375 def wrap_basic_auth(handler_class, htpasswd_path): 
    376     if not htpasswd_path: 
    377         return handler_class 
    378  
    379     old_execute = handler_class._execute 
    380     htpasswd = Htpasswd(htpasswd_path) 
    381  
    382     def execute(self, transforms, *args, **kwargs): 
    383         def auth_required(): 
    384             self.stream.write(tornado.escape.utf8( 
    385                 'HTTP/1.1 401 Authorization Required\r\n' 
    386                 'WWW-Authenticate: Basic realm=etherws\r\n\r\n' 
    387             )) 
    388             self.stream.close() 
    389  
    390         creds = self.request.headers.get('Authorization') 
    391  
    392         if not creds or not creds.startswith('Basic '): 
    393             return auth_required() 
    394  
    395         try: 
    396             name, passwd = base64.b64decode(creds[6:]).split(':', 1) 
    397             htpasswd.load() 
    398  
    399             if not htpasswd.auth(name, passwd): 
    400                 return auth_required() 
    401  
    402             return old_execute(self, transforms, *args, **kwargs) 
    403  
    404         except: 
    405             return auth_required() 
    406  
    407     handler_class._execute = execute 
    408     return handler_class 
    409409 
    410410 
     
    469469    switch = SwitchingHub(fdb, debug=args.debug) 
    470470 
    471     handler = wrap_basic_auth(EtherWebSocketHandler, args.htpasswd) 
    472     srv = (args.path, handler, {'switch': switch, 'debug': args.debug}) 
    473     app = tornado.web.Application([srv]) 
     471    harg = {'switch': switch, 'htpasswd': args.htpasswd, 'debug': args.debug} 
     472    serv = (args.path, EtherWebSocketHandler, harg) 
     473    app = tornado.web.Application([serv]) 
    474474    server = tornado.httpserver.HTTPServer(app, ssl_options=ssl_options) 
    475475    server.listen(args.port, address=args.address) 
Note: See TracChangeset for help on using the changeset viewer.