diff options
author | pzread <netfirewall@gmail.com> | 2013-06-09 01:48:00 +0800 |
---|---|---|
committer | pzread <netfirewall@gmail.com> | 2013-06-09 01:48:00 +0800 |
commit | 8cf636373548c8e3484a137268ddd041d12bbe4a (patch) | |
tree | 073d3d4cbca57798ef737a6ec3702621c66fba21 | |
parent | 9fa7badc787ec364d58f65b95355c8725ad75a9c (diff) | |
download | taiwan-online-judge-8cf636373548c8e3484a137268ddd041d12bbe4a.tar.gz taiwan-online-judge-8cf636373548c8e3484a137268ddd041d12bbe4a.tar.zst taiwan-online-judge-8cf636373548c8e3484a137268ddd041d12bbe4a.zip |
Add user module. Stable sendfile. Change async. Change imc call
-rw-r--r-- | src/py/backend_server.py | 55 | ||||
-rw-r--r-- | src/py/center_server.py | 32 | ||||
-rw-r--r-- | src/py/imc/async.py | 13 | ||||
-rw-r--r-- | src/py/imc/auth.py | 27 | ||||
-rwxr-xr-x | src/py/imc/proxy.py | 69 | ||||
-rw-r--r-- | src/py/netio.py | 51 | ||||
-rw-r--r-- | src/py/tojauth.py | 126 | ||||
-rwxr-xr-x | src/py/user.py | 336 |
8 files changed, 542 insertions, 167 deletions
diff --git a/src/py/backend_server.py b/src/py/backend_server.py index 087551a..7535183 100644 --- a/src/py/backend_server.py +++ b/src/py/backend_server.py @@ -37,7 +37,7 @@ class BackendWorker(tornado.tcpserver.TCPServer): def start(self): sock_port = random.randrange(4096,8192) - self.sock_addr = ('10.8.0.6',sock_port) + self.sock_addr = ('10.8.0.10',sock_port) self.bind(sock_port,'',socket.AF_INET,65536) super().start() @@ -68,14 +68,14 @@ class BackendWorker(tornado.tcpserver.TCPServer): conn.add_close_callback(lambda conn : self.del_client(conn.linkid)) Proxy.instance.add_conn(conn) - imc_call_async(self._idendesc,'/center/' + self.center_conn.linkid + '/','add_client',{'backend_linkid':self._linkid,'client_linkid':linkid}) + #imc_call_async(self._idendesc,'/center/' + self.center_conn.linkid + '/','add_client',{'backend_linkid':self._linkid,'client_linkid':linkid}) return conn def del_client(self,linkid): del self._client_linkidmap[linkid] - imc_call_async(self._idendesc,'/center/' + self.center_conn.linkid + '/','del_client',linkid) + #imc_call_async(self._idendesc,'/center/' + self.center_conn.linkid + '/','del_client',linkid) def _conn_center(self): def __retry(conn): @@ -102,10 +102,10 @@ class BackendWorker(tornado.tcpserver.TCPServer): imc_register_call('','test_dst',self._test_dst) #imc_register_call('','test_dsta',self._test_dsta) - time.sleep(2) + #time.sleep(2) if int(self._linkid) == 2: - self._test_call(None,'9') + self._test_call('9') sock_ip,sock_port = self.sock_addr netio.send_pack(stream,bytes(json.dumps({ @@ -227,18 +227,24 @@ class BackendWorker(tornado.tcpserver.TCPServer): pass @imc.async.caller - def _test_call(self,iden,param): - param = '6' + def _test_call(self,param): + dst = '/backend/' + '3' + '/' + ret = imc_call_async(self._idendesc,dst,'test_dst',lambda result : print(result),'test',113) + print(ret) + + ret = imc_call(self._idendesc,'/center/1/','create_iden','client','1234',1221,TOJAuth.ROLETYPE_USER,{'uid':31}) + print(ret) + + return pend = [] - for i in range(0,3): - if str((i % 8) + 2) == self._linkid: + for i in range(0,32): + if str((i % 16) + 2) == self._linkid: continue - fileres = Proxy.instance.sendfile('/backend/' + str((i % 8) + 2) + - '/','test.py') + fileres = Proxy.instance.sendfile('/backend/' + str((i % 16) + 2) + '/','Fedora-18-x86_64-DVD.iso') - dst = '/backend/' + str((i % 8) + 2) + '/' + dst = '/backend/' + str((i % 16) + 2) + '/' ret = imc_call(self._idendesc,dst,'test_dst',fileres.filekey) pend.append(fileres) @@ -249,13 +255,18 @@ class BackendWorker(tornado.tcpserver.TCPServer): print(self._linkid) @imc.async.caller - def _test_dst(self,iden,param): + def _test_dst(self,param,sdfsdf): #stat,ret = imc_call(self._idendesc,'/backend/' + self._linkid + '/','test_dsta',param) #return ret + ' Too' - Proxy.instance.rejectfile(param) + print(param) + print(sdfsdf) + print(TOJAuth.get_current_iden()) + + #Proxy.instance.rejectfile(param) + #print('recv ' + iden['linkid'] + ' > ' + self._linkid) #fileres = Proxy.instance.recvfile(param,'data') - #print(fileres.wait()) + #print('recv ' + fileres.wait()) return 'ok' @@ -295,7 +306,7 @@ def start_backend_worker(ws_port): ])) http_serv.listen(ws_port) - backend_worker = BackendWorker(('10.8.0.6',5730),ws_port) + backend_worker = BackendWorker(('10.8.0.10',5730),ws_port) backend_worker.start() tornado.ioloop.IOLoop.instance().start() @@ -305,12 +316,12 @@ if __name__ == '__main__': worker_list.append(Process(target = start_backend_worker,args = (81, ))) worker_list.append(Process(target = start_backend_worker,args = (82, ))) - worker_list.append(Process(target = start_backend_worker,args = (181, ))) - worker_list.append(Process(target = start_backend_worker,args = (182, ))) - worker_list.append(Process(target = start_backend_worker,args = (183, ))) - worker_list.append(Process(target = start_backend_worker,args = (184, ))) - worker_list.append(Process(target = start_backend_worker,args = (185, ))) - worker_list.append(Process(target = start_backend_worker,args = (186, ))) + #worker_list.append(Process(target = start_backend_worker,args = (181, ))) + #worker_list.append(Process(target = start_backend_worker,args = (182, ))) + #worker_list.append(Process(target = start_backend_worker,args = (183, ))) + #worker_list.append(Process(target = start_backend_worker,args = (184, ))) + #worker_list.append(Process(target = start_backend_worker,args = (185, ))) + #worker_list.append(Process(target = start_backend_worker,args = (186, ))) for proc in worker_list: proc.start() diff --git a/src/py/center_server.py b/src/py/center_server.py index d0bd429..f54a57c 100644 --- a/src/py/center_server.py +++ b/src/py/center_server.py @@ -67,10 +67,12 @@ class CenterServer(tornado.tcpserver.TCPServer): TOJAuth(pubkey,privkey) self._linkid = self._create_linkid() - self._idendesc = self._create_idendesc('center',self._linkid) + + self._idendesc = TOJAuth.instance.create_iden('center',self._linkid,1,TOJAuth.ROLETYPE_TOJ) Proxy('center',self._linkid,TOJAuth.instance,self._idendesc) imc_register_call('','lookup_linkid',self._lookup_linkid) + imc_register_call('','create_iden',self._create_iden) imc_register_call('','add_client',self._add_client) imc_register_call('','del_client',self._del_client) @@ -84,7 +86,7 @@ class CenterServer(tornado.tcpserver.TCPServer): linkclass = worker_info['linkclass'] if linkclass == 'backend': linkid = self._create_linkid() - idendesc = self._create_idendesc('backend',linkid) + idendesc = TOJAuth.instance.create_iden('backend',linkid,1,TOJAuth.ROLETYPE_TOJ) BackendWorker(main_stream,linkid,idendesc,worker_info,self._linkid) fd = stream.fileno() @@ -113,7 +115,7 @@ class CenterServer(tornado.tcpserver.TCPServer): return None linkid = self._create_linkid() - idendesc = self._create_idendesc('client',linkid) + idendesc = TOJAuth.instance.create_iden('client',linkid,2,TOJAuth.ROLETYPE_GUEST) backend = self._backend_workerlist[random.randrange(size)] ws_ip,ws_port = backend.ws_addr @@ -131,13 +133,8 @@ class CenterServer(tornado.tcpserver.TCPServer): return linkid - def _create_idendesc(self,linkclass,linkid): - return TOJAuth.instance.create_iden(linkclass,linkid,2) - @imc.async.caller - def _lookup_linkid(self,iden,param): - linkid = param - + def _lookup_linkid(self,linkid): try: worker = self._worker_linkidmap[linkid] @@ -150,7 +147,7 @@ class CenterServer(tornado.tcpserver.TCPServer): #else: # worker = self._worker_linkidmap[str(a - 1)] - if iden['linkclass'] != 'client': + if TOJAuth.get_current_iden()['linkclass'] != 'client': sock_ip,sock_port = worker.sock_addr return { 'worker_linkclass':worker.linkclass, @@ -161,9 +158,14 @@ class CenterServer(tornado.tcpserver.TCPServer): except KeyError: return None + + @imc.async.caller + @TOJAuth.check_access(1,TOJAuth.ACCESS_EXECUTE) + def _create_iden(self,linkclass,linkid,idenid,roletype,payload): + return TOJAuth.instance.create_iden(linkclass,linkid,idenid,roletype,payload) @imc.async.caller - def _add_client(self,iden,param): + def _add_client(self,param): backend_linkid = iden['linkid'] client_linkid = param['client_linkid'] @@ -173,10 +175,8 @@ class CenterServer(tornado.tcpserver.TCPServer): print(client_linkid); - #imc_call_async(self._idendesc,'/client/' + client_linkid + '/','test_call','Hello Client',lambda result:print(result)) - @imc.async.caller - def _del_client(self,iden,param): + def _del_client(self,param): backend_linkid = iden['linkid'] client_linkid = param @@ -188,7 +188,7 @@ class CenterServer(tornado.tcpserver.TCPServer): @imc.async.caller - def _test_dst(self,iden,param): + def _test_dst(self,param): linkidlist = [] clientmaps = self._backend_clientmap.values() for clientmap in clientmaps: @@ -199,7 +199,7 @@ class CenterServer(tornado.tcpserver.TCPServer): return linkidlist @imc.async.caller - def _test_dstb(self,iden,param): + def _test_dstb(self,param): return param + ' World' class WebConnHandler(tornado.web.RequestHandler): diff --git a/src/py/imc/async.py b/src/py/imc/async.py index 8934ee0..c0df338 100644 --- a/src/py/imc/async.py +++ b/src/py/imc/async.py @@ -2,6 +2,7 @@ import traceback import uuid import ssl +import tornado.stack_context from Crypto.Hash import SHA512 from greenlet import greenlet @@ -17,10 +18,12 @@ def switch_top(): assert greenlet.getcurrent() != gr_main old_iden = auth.current_iden + old_contexts = tornado.stack_context._state.contexts auth.current_iden = None result = gr_main.switch(None) + tornado.stack_context._state.contexts = old_contexts auth.current_iden = old_iden return result @@ -46,8 +49,11 @@ def caller(f): grid = id(gr) gr_idmap[grid] = set() old_iden = auth.current_iden + old_contexts = tornado.stack_context._state.contexts result = gr.switch(*args,**kwargs) + + tornado.stack_context._state.contexts = old_contexts auth.current_iden = old_iden if result == None: @@ -58,6 +64,11 @@ def caller(f): return result + except TypeError as err: + traceback.print_stack() + print(err) + return (False,'Eparameter') + except Exception as err: traceback.print_stack() print(err) @@ -94,6 +105,7 @@ def ret(retid,value = None,err = None): try: old_iden = auth.current_iden + old_contexts = tornado.stack_context._state.contexts if err == None: gr.switch(value) @@ -101,6 +113,7 @@ def ret(retid,value = None,err = None): else: gr.throw(err) + tornado.stack_context._state.contexts = old_contexts auth.current_iden = old_iden except TypeError as err: diff --git a/src/py/imc/auth.py b/src/py/imc/auth.py index 08e1417..03c15dc 100644 --- a/src/py/imc/auth.py +++ b/src/py/imc/auth.py @@ -1,7 +1,9 @@ import time import json import binascii +import contextlib +import tornado.stack_context from Crypto.PublicKey import RSA from Crypto.Hash import SHA512 from Crypto.Signature import PKCS1_v1_5 @@ -13,17 +15,30 @@ class Auth: global current_iden self._cache_hashmap = {} - current_iden = None - Auth.instance = self - def change_iden(self,iden): + @staticmethod + def get_current_iden(): global current_iden - old_iden = current_iden - current_iden = iden + return current_iden + + @staticmethod + def change_current_iden(iden): + @contextlib.contextmanager + def context(): + global current_iden + + old_iden = current_iden + current_iden = iden + + try: + yield + + finally: + current_iden = old_iden - return old_iden + return tornado.stack_context.StackContext(context) def set_signkey(self,key): self._signer = PKCS1_v1_5.new(RSA.importKey(key)) diff --git a/src/py/imc/proxy.py b/src/py/imc/proxy.py index 8f37b62..00476f0 100755 --- a/src/py/imc/proxy.py +++ b/src/py/imc/proxy.py @@ -9,7 +9,7 @@ import tornado.ioloop import tornado.stack_context from imc import async -from imc import auth +from imc.auth import Auth class Connection: def __init__(self,linkclass,linkid): @@ -152,8 +152,17 @@ class Proxy: def register_call(self,path,func_name,func): self._call_pathmap[''.join([path,func_name])] = func - def call(self,timeout,idendesc,dst,func_name,param): - return self._route_call(None,async.get_retid(),timeout,idendesc,dst,func_name,param) + def call(self,idendesc,dst,func_name,timeout,*args): + return self._route_call(None,async.get_retid(),idendesc,dst,func_name,timeout,list(args)) + + def call_async(self,idendesc,dst,func_name,timeout,callback,*args): + @async.caller + def _call(): + result = self._route_call(None,async.get_retid(),idendesc,dst,func_name,timeout,list(args)) + if callback != None: + callback(result) + + self._ioloop.add_callback(tornado.stack_context.wrap(_call)) def sendfile(self,dst_link,filepath): filekey = SHA512.new(uuid.uuid1().bytes + ssl.RAND_bytes(64)).hexdigest() @@ -168,7 +177,7 @@ class Proxy: 'timer':self._ioloop.add_timeout(datetime.timedelta(days = 1),lambda : self._ret_sendfile('Etimeout')) } - stat,ret = self.call(65536,self._idendesc,dst_link + 'imc/','pend_recvfile',{'filekey':filekey,'filesize':filesize}) + stat,ret = self.call(self._idendesc,dst_link + 'imc/','pend_recvfile',{'filekey':filekey,'filesize':filesize},655360) if stat == False: raise ConnectionError(ret) @@ -214,13 +223,14 @@ class Proxy: return dst_link = ''.join(['/',info['src_linkclass'],'/',info['src_linkid'],'/']) - self.call(65536,self._idendesc,dst_link + 'imc/','reject_sendfile',{'filekey':filekey}) + self.call(self._idendesc,dst_link + 'imc/','reject_sendfile',{'filekey':filekey},65536) - def _route_call(self,in_conn,caller_retid,timeout,idendesc,dst,func_name,param): + def _route_call(self,in_conn,caller_retid,idendesc,dst,func_name,timeout,param): def __add_wait_caller(conn_linkid): + callback = tornado.stack_context.wrap(lambda result : self._ret_call(caller_linkid,caller_retid,result)) self._conn_retidmap[conn_linkid][caller_retid] = { - 'timer':self._ioloop.add_timeout(datetime.timedelta(milliseconds= timeout),lambda : callback(('False','Etimeout'))), - 'callback':tornado.stack_context.wrap(lambda result : self._ret_call(caller_linkid,caller_retid,result)) + 'timer':self._ioloop.add_timeout(datetime.timedelta(milliseconds = timeout),lambda : callback((False,'Etimeout'))), + 'callback':callback } def __del_wait_caller(conn_linkid): @@ -248,9 +258,13 @@ class Proxy: if iden == None: return __ret(False,'Eilliden') - dst_part = dst.split('/',3) - dst_linkid = dst_part[2] - dst_path = dst_part[3] + try: + dst_part = dst.split('/',3) + dst_linkid = dst_part[2] + dst_path = dst_part[3] + + except Exception: + return __ret(False,'Enoexist') caller_linkid = iden['linkid'] @@ -258,9 +272,8 @@ class Proxy: __add_wait_caller(self._linkid) try: - old_iden = self._auth.change_iden(iden) - result = self._call_pathmap[''.join([dst_path,func_name])](iden,param) - self._auth.change_iden(old_iden) + with Auth.change_current_iden(iden): + result = self._call_pathmap[''.join([dst_path,func_name])](*param) except KeyError: result = (False,'Enoexist') @@ -277,7 +290,7 @@ class Proxy: else: if caller_linkid == self._linkid: __add_wait_caller(conn.linkid) - self._send_msg_call(conn,caller_retid,timeout,idendesc,dst,func_name,param) + self._send_msg_call(conn,caller_retid,idendesc,dst,func_name,timeout,param) result = async.switch_top() @@ -286,7 +299,7 @@ class Proxy: return __ret(result) else: - self._send_msg_call(conn,caller_retid,timeout,idendesc,dst,func_name,param) + self._send_msg_call(conn,caller_retid,idendesc,dst,func_name,timeout,param) return @@ -367,7 +380,7 @@ class Proxy: def _add_wait_filekey(self,conn_linkid,filekey,filesize,callback): callback = tornado.stack_context.wrap(callback) self._conn_filekeymap[conn_linkid][filekey] = { - 'timer':self._ioloop.add_timeout(datetime.timedelta(milliseconds = filesize),lambda : callback('Etimeout')), + 'timer':self._ioloop.add_timeout(datetime.timedelta(milliseconds = min(filesize,1000)),lambda : callback('Etimeout')), 'callback':callback } @@ -428,14 +441,14 @@ class Proxy: elif msg_type == self.MSGTYPE_ABORTFILE: self._recv_msg_abortfile(conn,msg) - def _send_msg_call(self,conn,caller_retid,timeout,idendesc,dst,func_name,param): + def _send_msg_call(self,conn,caller_retid,idendesc,dst,func_name,timeout,param): msg = { 'type':self.MSGTYPE_CALL, 'caller_retid':caller_retid, - 'timeout':timeout, 'idendesc':idendesc, 'dst':dst, 'func_name':func_name, + 'timeout':timeout, 'param':param } @@ -444,13 +457,13 @@ class Proxy: def _recv_msg_call(self,conn,msg): @async.caller def __call(): - self._route_call(conn,caller_retid,timeout,idendesc,dst,func_name,param) + self._route_call(conn,caller_retid,idendesc,dst,func_name,timeout,param) caller_retid = msg['caller_retid'] - timeout = msg['timeout'] idendesc = msg['idendesc'] dst = msg['dst'] func_name = msg['func_name'] + timeout = msg['timeout'] param = msg['param'] __call() @@ -536,17 +549,11 @@ class Proxy: filekey = param['filekey'] self._ioloop.add_callback(self._ret_sendfile,filekey,'Ereject') -def imc_call(idendesc,dst,func_name,param): - return Proxy.instance.call(65536,idendesc,dst,func_name,param) - -def imc_call_async(idendesc,dst,func_name,param,callback = None): - @async.caller - def func(): - ret = imc_call(idendesc,dst,func_name,param) - if callback != None: - callback(ret) +def imc_call(idendesc,dst,func_name,*args): + return Proxy.instance.call(idendesc,dst,func_name,65536,*args) - func() +def imc_call_async(idendesc,dst,func_name,callback,*args): + Proxy.instance.call_async(idendesc,dst,func_name,65536,callback,*args) def imc_register_call(path,func_name,func): Proxy.instance.register_call(path,func_name,func) diff --git a/src/py/netio.py b/src/py/netio.py index 4fe61bd..b71ab0b 100644 --- a/src/py/netio.py +++ b/src/py/netio.py @@ -39,6 +39,7 @@ class SocketStream: self._write_queue = deque() self._stat = tornado.ioloop.IOLoop.ERROR + self._sock.setsockopt(socket.SOL_SOCKET,socket.SO_KEEPALIVE,1) self._sock.setblocking(False) self._ioloop.add_handler(sock.fileno(),self._handle_event,tornado.ioloop.IOLoop.ERROR) @@ -120,6 +121,7 @@ class SocketStream: def _handle_event(self,fd,evt): if evt & tornado.ioloop.IOLoop.ERROR: + print(os.strerror(self._sock.getsockopt(socket.SOL_SOCKET,socket.SO_ERROR))) self.close() return @@ -303,8 +305,6 @@ class SocketConnection(Connection): self.file_addr = file_addr self.add_pend_filestream = add_pend_filestream_fn - self._start_ping() - def send_msg(self,data): if self._closed == True: raise ConnectionError @@ -333,8 +333,7 @@ class SocketConnection(Connection): file_stream.close() os.close(fd) - if err == None: - callback() + callback(err) if self._closed == True: raise ConnectionError @@ -367,16 +366,15 @@ class SocketConnection(Connection): file_stream.close() os.close(fd) - if err == None: - callback() - - else: + if err != None: try: os.remove(filepath) except FileNotFoundError: pass + callback(err) + if self._closed == True: raise ConnectionError @@ -406,8 +404,7 @@ class SocketConnection(Connection): file_stream.set_close_callback(None) file_stream.close() - if err == None: - callback() + callback(err) def _send_cb(data): def __done_cb(): @@ -461,31 +458,21 @@ class SocketConnection(Connection): def start_recv(self,recv_callback): def _recv_size(data): size, = struct.unpack('l',data) - if size > 0: - self.main_stream.read_bytes(size,_recv_data) - else: - if size == -1: #pong - self._ping_delay = 0 - - self.main_stream.read_bytes(8,_recv_size) + self.main_stream.read_bytes(size,_recv_data) + self.main_stream.read_bytes(8,_recv_size) def _recv_data(data): self._recv_callback(self,data) - self.main_stream.read_bytes(8,_recv_size) self._recv_callback = tornado.stack_context.wrap(recv_callback) self.main_stream.read_bytes(8,_recv_size) def close(self): - try: - self._ping_timer.stop() - - except AttributeError: - pass - if self._closed == True: return + traceback.print_stack() + self._closed = True self.main_stream.close() @@ -501,22 +488,6 @@ class SocketConnection(Connection): def _del_wait_filekey(self,filekey): del self._sendfile_filekeymap[filekey] - def _start_ping(self): - def __check(): - try: - self.main_stream.write(struct.pack('l',-1)) - - except ConnectionError: - return - - self._ping_delay += 1 - if self._ping_delay > 10: - self.close() - - self._ping_timer = tornado.ioloop.PeriodicCallback(__check,1000) - self._ping_timer.start() - self._ping_delay = 0 - class WebSocketConnection(Connection): def __init__(self,linkclass,linkid,handler): super().__init__(linkclass,linkid) diff --git a/src/py/tojauth.py b/src/py/tojauth.py index 11877ff..701095b 100644 --- a/src/py/tojauth.py +++ b/src/py/tojauth.py @@ -10,9 +10,16 @@ class TOJAuth(Auth): ACCESS_SETPER = 0x10 ACCESS_EXECUTE = 0x20 + ROLETYPE_USER = 1 + ROLETYPE_3RD = 2 + ROLETYPE_MOD = 3 + ROLETYPE_TOJ = 4 + ROLETYPE_GROUP = 5 + ROLETYPE_GUEST = 6 + auth_accessid = 1 - def __init__(self,pubkey,privkey = None): + def __init__(self, pubkey, privkey = None): super().__init__() self.set_verifykey(pubkey) @@ -20,18 +27,21 @@ class TOJAuth(Auth): self.set_signkey(privkey) TOJAuth.instance = self - TOJAuth.db = AsyncDB(config.CORE_DBNAME,config.CORE_DBUSER, + TOJAuth.db = AsyncDB(config.CORE_DBNAME, config.CORE_DBUSER, config.CORE_DBPASSWORD) - def create_iden(self,linkclass,linkid,idenid): - iden = { - 'linkclass':linkclass, - 'linkid':linkid, - 'idenid':idenid - } + def create_iden(self, linkclass, linkid, idenid, roletype, payload = {}): + iden = payload + iden.update({ + 'linkclass' : linkclass, + 'linkid' : linkid, + 'idenid' : idenid, + 'roletype' : roletype + }) + return self.sign_iden(iden) - def get_iden(self,conn_linkclass,conn_linkid,idendesc): + def get_iden(self, conn_linkclass, conn_linkid, idendesc): iden = super().get_iden(idendesc) if iden == None: return None @@ -41,46 +51,51 @@ class TOJAuth(Auth): return iden - def check_access(self, accessid, access_mask): + @staticmethod + def check_access(accessid, access_mask): def wrapper(f): - idenid = self.current_iden['idenid'] - ok = False - - cur = self.db.cursor() - - if not ok: - sqlstr = ('SELECT "owner_idenid" FROM "ACCESS" WHERE ' - '"accessid"=%s;') - sqlarr = (accessid, ) - cur.execute(sqlstr, sqlarr) - for data in cur: - owner_idenid = data[0] - if owner_idenid == idenid: - ok = True - - if not ok: - sqlstr = ('SELECT "ACCESS_ROLE"."permission" FROM "ACCESS_ROLE"' - ' INNER JOIN "IDEN_ROLE" ON "ACCESS_ROLE"."roleid" = ' - '"IDEN_ROLE"."roleid" WHERE "ACCESS_ROLE"."accessid"=%s' - ' AND "IDEN_ROLE"."idenid"=%s;') - sqlarr = (accessid, idenid) - cur.execute(sqlstr, sqlarr) - - for data in cur: - permission = data[0] - if (permission & access_mask) == access_mask: - ok = True - break - - if ok: - return f - else: - raise Exception('TOJAuth.check_access() : PERMISSION DENIED') + def wrapfunc(*args): + idenid = TOJAuth.get_current_iden()['idenid'] + ok = False + + cur = TOJAuth.instance.db.cursor() + + if not ok: + sqlstr = ('SELECT "owner_idenid" FROM "ACCESS" WHERE ' + '"accessid"=%s;') + sqlarr = (accessid, ) + cur.execute(sqlstr, sqlarr) + for data in cur: + owner_idenid = data[0] + if owner_idenid == idenid: + ok = True + + if not ok: + sqlstr = ('SELECT "ACCESS_ROLE"."permission" FROM "ACCESS_ROLE"' + ' INNER JOIN "IDEN_ROLE" ON "ACCESS_ROLE"."roleid" = ' + '"IDEN_ROLE"."roleid" WHERE "ACCESS_ROLE"."accessid"=%s' + ' AND "IDEN_ROLE"."idenid"=%s;') + sqlarr = (accessid, idenid) + cur.execute(sqlstr, sqlarr) + + for data in cur: + permission = data[0] + if (permission & access_mask) == access_mask: + ok = True + break + + if ok: + return f(*args); + else: + raise Exception('TOJAuth.check_access() : PERMISSION DENIED') + + return wrapfunc return wrapper def create_access(self, owner_idenid): - self.check_access(self.auth_accessid, self.ACCESS_EXECUTE)(0) + self.check_access( + self.auth_accessid, self.ACCESS_EXECUTE)(lambda x:x)(0) cur = self.db.cursor() sqlstr = ('INSERT INTO "ACCESS" ("owner_idenid") VALUES (%s) ' @@ -93,7 +108,7 @@ class TOJAuth(Auth): return accessid def set_access_list(self, accessid, roleid, permission): - self.check_access(accessid, self.ACCESS_SETPER)(0) + self.check_access(accessid, self.ACCESS_SETPER)(lambda x:x)(0) cur = self.db.cursor() table = 'ACCESS_ROLE' @@ -107,7 +122,7 @@ class TOJAuth(Auth): cur.upsert(table, cond, value) def del_access_list(self, accessid, roleid): - self.check_access(accessid, self.ACCESS_SETPER)(0) + self.check_access(accessid, self.ACCESS_SETPER)(lambda x:x)(0) cur = self.db.cursor() sqlstr = ('DELETE FROM "ACCESS_ROLE" WHERE "accessid"=%s ' @@ -116,19 +131,25 @@ class TOJAuth(Auth): cur.execute(sqlstr, sqlarr) def create_role(self, rolename, roletype): - self.check_access(self.auth_accessid, self.ACCESS_EXECUTE)(0) + self.check_access( + self.auth_accessid, self.ACCESS_EXECUTE)(lambda x:x)(0) cur = self.db.cursor() - sqlstr = ('INSERT INTO "ROLE" ("rolename") VALUES (%s)' + sqlstr = ('INSERT INTO "ROLE" ("rolename", "roletype") VALUES (%s, %s)' ' RETURNING "roleid";') - sqlarr = (rolename, ) + sqlarr = (rolename, roletype) cur.execute(sqlstr, sqlarr) for data in cur: roleid = data[0] + + if(roleid != None): + self.set_role_relation(roleid, roleid) + return roleid def set_role_relation(self, idenid, roleid): - self.check_access(self.auth_accessid, self.ACCESS_EXECUTE)(0) + self.check_access( + self.auth_accessid, self.ACCESS_EXECUTE)(lambda x:x)(0) cur = self.db.cursor() table = 'IDEN_ROLE' @@ -139,7 +160,8 @@ class TOJAuth(Auth): cur.upsert(table, cond) def del_role_relation(self, idenid, roleid): - self.check_access(self.auth_accessid, self.ACCESS_EXECUTE)(0) + self.check_access( + self.auth_accessid, self.ACCESS_EXECUTE)(lambda x:x)(0) cur = self.db.cursor() sqlstr = ('DELETE FROM "IDEN_ROLE" WHERE "idenid"=%s ' @@ -148,7 +170,7 @@ class TOJAuth(Auth): cur.execute(sqlstr, sqlarr) def set_owner(self, idenid, accessid): - self.check_access(accessid, self.ACCESS_SETPER)(0) + self.check_access(accessid, self.ACCESS_SETPER)(lambda x:x)(0) cur = self.db.cursor() sqlstr = ('UPDATE "ACCESS" SET "owner_idenid"=%s WHERE "accessid"=%s;') diff --git a/src/py/user.py b/src/py/user.py new file mode 100755 index 0000000..45e195a --- /dev/null +++ b/src/py/user.py @@ -0,0 +1,336 @@ +import psycopg2 +from Crypto.Hash import SHA512 + +from tojauth import TOJAuth +from asyncdb import AsyncDB +import imc.proxy +import config + +class User: + auth_accessid = 2 + + USERNAME_LEN_MIN = 5 + USERNAME_LEN_MAX = 50 + PASSWORD_LEN_MIN = 5 + PASSWORD_LEN_MAX = 50 + NICKNAME_LEN_MIN = 1 + NICKNAME_LEN_MAX = 50 + EMAIL_LEN_MIN = 5 + EMAIL_LEN_MAX = 100 + AVATAR_LEN_MIN = 0 + AVATAR_LEN_MAX = 200 + ABOUTME_LEN_MIN = 0 + ABOUTME_LEN_MAX = 1000 + + def __init__(self, mod_iden): + User.instance = self + User.db = AsyncDB(config.CORE_DBNAME, config.CORE_DBUSER, + config.CORE_DBPASSWORD) + User.mod_iden = mod_iden + + @imc.async.caller + def register(self, username, password, nickname, email, avatar, aboutme): + if( + type(username) != str or + type(password) != str or + type(nickname) != str or + type(email) != str or + type(avatar) != str or + type(aboutme) != str + ): + return 'Eparameter' + + if len(username) < self.USERNAME_LEN_MIN: + return 'Eusername_too_short' + elif len(username) > self.USERNAME_LEN_MAX: + return 'Eusername_too_long' + elif len(password) < self.PASSWORD_LEN_MIN: + return 'Epassword_too_short' + elif len(password) > self.PASSWORD_LEN_MAX: + return 'Epassword_too_long' + elif len(nickname) < self.NICKNAME_LEN_MIN: + return 'Enickname_too_short' + elif len(nickname) > self.NICKNAME_LEN_MAX: + return 'Enickname_too_long' + elif len(email) < self.EMAIL_LEN_MIN: + return 'Eemail_too_short' + elif len(email) > self.EMAIL_LEN_MAX: + return 'Eemail_too_long' + elif len(avatar) < self.AVATAR_LEN_MIN: + return 'Eavatar_too_short' + elif len(avatar) > self.AVATAR_LEN_MAX: + return 'Eavatar_too_long' + elif len(aboutme) < self.ABOUTME_LEN_MIN: + return 'Eaboutme_too_short' + elif len(aboutme) > self.ABOUTME_LEN_MAX: + return 'Eaboutme_too_long' + + passhash = self._password_hash(password) + + with TOJAuth.change_current_iden(self.mod_iden): + try: + uid = self._create_user( + username, passhash, nickname, email, avatar, aboutme) + except psycopg2.IntegrityError: + return 'Eusername_already_exists' + + return {'uid' : uid} + + @TOJAuth.check_access(auth_accessid, TOJAuth.ACCESS_EXECUTE) + def _create_user(self, username, passhash, nickname, email, avatar, + aboutme): + roleid = TOJAuth.instance.create_role(username, TOJAuth.ROLETYPE_USER) + + cur = self.db.cursor() + sqlstr = ('INSERT INTO "USER" ("username", "passhash", "nickname", ' + '"email", "avatar", "aboutme", "idenid") ' + 'VALUES (%s, %s, %s, %s, %s, %s, %s) RETURNING "uid";') + sqlarr = (username, passhash, nickname, email, avatar, aboutme, roleid) + cur.execute(sqlstr, sqlarr) + + for data in cur: + uid = data[0] + return uid + + @imc.async.caller + def login(self, username, password): + if( + type(username) != str or + type(password) != str + ): + return 'Eparameter' + + uid = self.get_uid_by_username(username) + if uid == None: + return 'Eno_such_uid' + + passhash = self._password_hash(password) + + cur = self.db.cursor() + sqlstr = ('SELECT "idenid" FROM "USER" WHERE "uid" = %s ' + 'AND "passhash" = %s;') + sqlarr = (uid, passhash) + cur.execute(sqlstr, sqlarr) + + idenid = None + for data in cur: + idenid = data[0] + + if idenid == None: + return 'Ewrong_password' + + linkclass = TOJAuth.get_current_iden()['linkclass'] + linkid = TOJAuth.get_current_iden()['linkid'] + + with TOJAuth.change_current_iden(self.mod_iden): + idendesc = TOJAuth.instance.create_iden( + linkclass, linkid, idenid, TOJAuth.ROLETYPE_USER, {'uid' : uid} + ) + + ret = { + 'idendesc' : idendesc, + 'uid' : uid, + 'hash' : self._uid_passhash_hash(uid, passhash) + } + + return ret + + @imc.async.caller + def cookie_login(self, uid, uphash): + if( + type(uid) != int or + type(uphash) != str + ): + return 'Eparameter' + + idenid = None + real_uphash = None + + cur = self.db.cursor() + sqlstr = ('SELECT "idenid", "passhash" FROM "USER" WHERE "uid" = %s;') + sqlarr = (uid, ) + cur.execute(sqlstr, sqlarr) + + for data in cur: + idenid = data[0] + real_uphash = self._uid_passhash_hash(uid, data[1]) + + if idenid == None: + return 'Eno_such_uid' + + if real_uphash != uphash: + return 'Ewrong_uphash' + + linkclass = TOJAuth.get_current_iden()['linkclass'] + linkid = TOJAuth.get_current_iden()['linkid'] + + with TOJAuth.change_current_iden(self.mod_iden): + idendesc = TOJAuth.instance.create_iden( + linkclass, linkid, idenid, TOJAuth.ROLETYPE_USER, {'uid' : uid} + ) + + ret = { + 'idendesc' : idendesc, + 'uid' : uid, + 'hash' : uphash + } + + return ret + + @imc.async.caller + def get_user_info(self, uid): + if( + type(uid) != int + ): + return 'Eparameter' + + ret = self._get_user_info_by_uid(uid) + if ret == None: + return 'Eno_such_uid' + + return ret + + @imc.async.caller + def set_user_info(self, uid, nickname, email, avatar, aboutme): + if( + type(uid) != int or + type(nickname) != str or + type(email) != str or + type(avatar) != str or + type(aboutme) != str + ): + return 'Eparameter' + + if len(nickname) < self.NICKNAME_LEN_MIN: + return 'Enickname_too_short' + elif len(nickname) > self.NICKNAME_LEN_MAX: + return 'Enickname_too_long' + elif len(email) < self.EMAIL_LEN_MIN: + return 'Eemail_too_short' + elif len(email) > self.EMAIL_LEN_MAX: + return 'Eemail_too_long' + elif len(avatar) < self.AVATAR_LEN_MIN: + return 'Eavatar_too_short' + elif len(avatar) > self.AVATAR_LEN_MAX: + return 'Eavatar_too_long' + elif len(aboutme) < self.ABOUTME_LEN_MIN: + return 'Eaboutme_too_short' + elif len(aboutme) > self.ABOUTME_LEN_MAX: + return 'Eaboutme_too_long' + + idenid = self.get_idenid_by_uid(uid) + if idenid == None: + return 'Eno_such_uid' + + if idenid != TOJAuth.get_current_iden()['idenid']: + TOJAuth.check_access( + self.auth_accessid, TOJAuth.ACCESS_EXECUTE)(lambda x:x)(0) + + cur = self.db.cursor() + sqlstr = ('UPDATE "USER" SET "nickname" = %s, "email" = %s, ' + '"avatar" = %s, "aboutme" = %s WHERE "uid" = %s;') + sqlarr = (nickname, email, avatar, aboutme, uid) + cur.execute(sqlstr, sqlarr) + + @imc.async.caller + def change_user_password(self, uid, old_password, new_password): + if( + type(uid) != int or + type(old_password) != str or + type(new_password) != str + ): + return 'Eparameter' + + if len(new_password) < self.PASSWORD_LEN_MIN: + return 'Epassword_too_short' + elif len(new_password) > self.PASSWORD_LEN_MAX: + return 'Epassword_too_long' + + idenid = self.get_idenid_by_uid(uid) + if idenid == None: + return 'Eno_such_uid' + + if idenid != TOJAuth.get_current_iden()['idenid']: + TOJAuth.check_access( + self.auth_accessid, TOJAuth.ACCESS_EXECUTE)(lambda x:x)(0) + + old_passhash = self._password_hash(old_password) + + cur = self.db.cursor() + sqlstr = ('SELECT "idenid" FROM "USER" WHERE "uid" = %s ' + 'AND "passhash" = %s;') + sqlarr = (uid, old_passhash) + cur.execute(sqlstr, sqlarr) + + idenid = None + for data in cur: + idenid = data[0] + + if idenid == None: + return 'Ewrong_old_password' + + new_passhash = self._password_hash(new_password) + + sqlstr = ('UPDATE "USER" SET "passhash" = %s WHERE "uid" = %s;') + sqlarr = (new_passhash, uid) + cur.execute(sqlstr, sqlarr) + + @imc.async.caller + def oauth_login(self): + raise NotImplementedError + + def _password_hash(self, password): + h = SHA512.new(bytes(password + config.USER_PASSHASH_SALT, 'utf-8')) + return h.hexdigest() + + def _uid_passhash_hash(self, uid, passhash): + return self._password_hash( + 'GENGJIAN_WEISUO_KING^^' + str(uid) + '@E__E@' + passhash + 'Yo!') + + def _get_user_info_by_uid(self, uid): + cur = self.db.cursor() + sqlstr = ('SELECT * FROM "USER" WHERE "uid" = %s;') + sqlarr = (uid, ) + cur.execute(sqlstr, sqlarr) + + ret = None + for data in cur: + ret = {} + ret['uid'] = data[0] + ret['username'] = data[1] + ret['nickname'] = data[3] + ret['email'] = data[4] + ret['avatar'] = data[5] + ret['aboutme'] = data[6] + + return ret + + def get_idenid_by_uid(self, uid): + cur = self.db.cursor() + sqlstr = ('SELECT "idenid" FROM "USER" WHERE "uid" = %s;') + sqlarr = (uid, ) + cur.execute(sqlstr, sqlarr) + + ret = None + for data in cur: + ret = data[0] + + return ret + + def get_uid_by_username(self, username): + cur = self.db.cursor() + sqlstr = ('SELECT "uid" FROM "USER" WHERE "username" = %s;') + sqlarr = (username, ) + cur.execute(sqlstr, sqlarr) + + uid = None + for data in cur: + uid = data[0] + + return uid + + def does_username_exist(self, username): + uid = self.get_uid_by_username(username) + + return uid != None + |