aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpzread <netfirewall@gmail.com>2013-06-09 01:48:00 +0800
committerpzread <netfirewall@gmail.com>2013-06-09 01:48:00 +0800
commit8cf636373548c8e3484a137268ddd041d12bbe4a (patch)
tree073d3d4cbca57798ef737a6ec3702621c66fba21
parent9fa7badc787ec364d58f65b95355c8725ad75a9c (diff)
downloadtaiwan-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.py55
-rw-r--r--src/py/center_server.py32
-rw-r--r--src/py/imc/async.py13
-rw-r--r--src/py/imc/auth.py27
-rwxr-xr-xsrc/py/imc/proxy.py69
-rw-r--r--src/py/netio.py51
-rw-r--r--src/py/tojauth.py126
-rwxr-xr-xsrc/py/user.py336
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
+