Server: Add ACL support

This commit is contained in:
Maurizio Porrato 2010-10-31 23:08:32 +01:00
parent 0ce6e39b10
commit a7b1e6ab25
3 changed files with 151 additions and 13 deletions

View File

@ -5,6 +5,7 @@
import shelve
from twisted.python import log
from frn.user import FRNUser
CLIENTS_ORDER = ['Crosslink', 'Parrot', 'Gateway', 'PC Only']
@ -39,6 +40,7 @@ class ClientTracker(object):
self._mute = {}
self._block = {}
self._admin = {}
self._acl = {}
self.cbClient = cbClient
self.cbNet = cbNet
self.cbMute = cbMute
@ -53,6 +55,7 @@ class ClientTracker(object):
self._mute = s['mute']
self._block = s['block']
self._admin = s['admin']
self._acl = s['acl']
except KeyError: pass
s.close()
@ -61,6 +64,7 @@ class ClientTracker(object):
s['mute'] = self._mute
s['block'] = self._block
s['admin'] = self._admin
s['acl'] = self._acl
s.close()
def isLoggedIn(self, clientId):
@ -78,7 +82,7 @@ class ClientTracker(object):
return l.index(clientId)+1
def getNetworkList(self):
return self._net.keys()
return sorted(set(self._net.keys())|set(self._acl.keys()))
def getClientList(self, network=[]):
if network:
@ -113,7 +117,10 @@ class ClientTracker(object):
def login(self, client, status=0):
clientId = client.user.ID
if clientId not in self._clientData:
client.user.S = status
if not self.aclTalkOk(client.user.NT, client.user.EA) and status < 1:
client.user.S = 1
else:
client.user.S = status
self._clientData[clientId] = client
net = client.user.NT
nc = self._net.get(net, [])
@ -133,7 +140,7 @@ class ClientTracker(object):
self._mute[clientId].AI = a
else:
client.user.M = 0
if clientId in self._block:
if not self.canLogin(client.user):
client.role = "BLOCK"
if client.user.EA in self._admin:
client.role = "ADMIN"
@ -159,11 +166,13 @@ class ClientTracker(object):
self.cbClient(net, self.getClientList(net))
def setStatus(self, clientId, status):
oldStatus = self._clientData[clientId].user.S
user = self.getClient(clientId)
oldStatus = user.S
if not self.aclTalkOk(user.NT, user.EA) and status < 1:
status = 1
if oldStatus != str(status):
net = self._clientData[clientId].user.NT
self._clientData[clientId].user.S = status
self.cbClient(net, self.getClientList(net))
self.cbClient(user.NT, self.getClientList(user.NT))
def mute(self, admin, clientId):
if clientId not in self._mute:
@ -210,5 +219,81 @@ class ClientTracker(object):
self.save()
self.cbAdmin(self.getAdminList())
def _getAcl(self, network):
return self._acl.get(network, (False, False, {}))
def _setAcl(self, network, ac, tx, l):
log.msg("Changing ACL for %s" % network)
if ac == tx == False and len(l) == 0:
log.msg("Default settings detected")
if network in self._acl:
log.msg("Deleting")
del self._acl[network]
else:
log.msg("Setting ACL for %s to %s" % (network, str((ac,tx,l))))
self._acl[network] = (ac, tx, l)
self.save()
def getAcl(self, network):
ac, tx, l = self._getAcl(network)
return l.values()
def getAclFlags(self, network):
ac, tx, l = self._getAcl(network)
return (ac, tx)
def setAclFlags(self, network, ac, tx):
oac, otx, l = self._getAcl(network)
self._setAcl(network, ac, tx, l)
def addAcl(self, network, email):
log.msg("Adding %s to ACL for %s" % (email, network))
ac, tx, l = self._getAcl(network)
if email not in l:
knownUsers = dict([(x.user.EA, x.user) for x in self._clientData.values() + self._mute.values() + self._block.values()])
user = knownUsers.get(email, FRNUser(EA=email)).copy(ID=email, AI=1)
l[email] = user
self._setAcl(network, ac, tx, l)
def delAcl(self, network, email):
log.msg("Removing %s from ACL for %s" % (email, network))
ac, tx, l = self._getAcl(network)
if email in l:
del l[email]
self._setAcl(network, ac, tx, l)
def setAclTx(self, network, email, tx):
oac, otx, l = self._getAcl(network)
if email in l:
if tx:
l[email].AI = 0
else:
l[email].AI = 1
self._setAcl(network, oac, otx, l)
def aclLoginOk(self, network, email):
ac, tx, l = self._getAcl(network)
if not ac:
return True
return email in l
def aclTalkOk(self, network, email):
ac, tx, l = self._getAcl(network)
if not tx:
return True
if email not in l:
return False
return l[email].AI == '0'
def canLogin(self, user):
if self.isBlocked(user.ID):
return False
return self.aclLoginOk(user.NT, user.EA)
def canTalk(self, clientId):
if self.isMute(clientId):
return False
u = self.getClient(clientId)
return self.aclTalkOk(u.NT, u.EA)
# vim: set et ai sw=4 ts=4 sts=4:

View File

@ -55,8 +55,12 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
self.sendBlockList(self.factory.tracker.getBlockList())
if self.role == 'OWNER':
self.sendAdminList(self.factory.tracker.getAdminList())
self.sendAccessFlags(None)
self.sendAccessList([])
ac, tx = self.factory.tracker.getAclFlags(self.user.NT)
self.sendAccessFlags(ac,tx)
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
if self.role not in ['OK', 'ADMIN', 'OWNER']:
self.transport.loseConnection()
return
self.startPinging()
self.setTimeout(10.0)
return
@ -113,8 +117,10 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
if self.role == 'OK':
if self.user.EA == self.factory.serverAuth.OW:
self.role = 'OWNER'
log.msg("%s promoted to OWNER" % clientId)
elif self.factory.tracker.isAdmin(self.user.ID):
self.role = 'ADMIN'
log.msg("%s promoted to ADMIN" % clientId)
if versions.server > 2009004:
self.waitingKey = True
self.sendLine(str(versions.client))
@ -201,9 +207,53 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
if self.role == "OWNER":
self.factory.tracker.unAdmin(body['ip'])
def decodeETX(self, body):
log.msg("decodeETX(%s)" % str(body))
if self.role == "OWNER":
self.factory.tracker.setAclTx(self.user.NT, body['ea'], True)
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
def decodeRTX(self, body):
log.msg("decodeRTX(%s)" % str(body))
if self.role == "OWNER":
self.factory.tracker.setAclTx(self.user.NT, body['ea'], False)
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
def decodeDT(self, body):
log.msg("decodeDT(%s)" % str(body))
if self.role == "OWNER":
self.factory.tracker.delAcl(self.user.NT, body['ea'])
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
def decodeAT(self, body):
log.msg("decodeAT(%s)" % str(body))
if self.role == "OWNER":
self.factory.tracker.addAcl(self.user.NT, body['ea'])
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
def decodeTXR(self, body):
log.msg("decodeTXR(%s)" % str(body))
if self.role == "OWNER":
en, _ = self.factory.tracker.getAclFlags(self.user.NT)
tx = body == '1'
self.factory.tracker.setAclFlags(self.user.NT, en, tx)
en,tx = self.factory.tracker.getAclFlags(self.user.NT)
self.sendAccessFlags(en, tx)
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
def decodeENA(self, body):
log.msg("decodeENA(%s)" % str(body))
if self.role == "OWNER":
_, tx = self.factory.tracker.getAclFlags(self.user.NT)
en = body == '1'
self.factory.tracker.setAclFlags(self.user.NT, en, tx)
en,tx = self.factory.tracker.getAclFlags(self.user.NT)
self.sendAccessFlags(en, tx)
self.sendAccessList(self.factory.tracker.getAcl(self.user.NT))
def audioFrameReceived(self, frame):
#log.msg("audioFrameReceived")
if not self.factory.tracker.isMute(self.user.ID):
if self.factory.tracker.canTalk(self.user.ID):
clientIdx = self.getIndex()
for c in self.factory.tracker.getClientList(self.user.NT):
if int(c.S) < 2 and c.ID != self.user.ID:
@ -250,16 +300,19 @@ class FRNServer(BufferingLineReceiver, TimeoutMixin):
self.sendLine(c.asXML('NN','CT','BC','ON','ID'))
def sendAccessList(self, clients):
log.msg("Sending ACL to %s: %s" % (self.user.ON, str(clients)))
self.transport.write(chr(7))
self.sendLine(str(len(clients)))
for c in clients: # FIXME
self.sendLine(c.asXML('AI','NN','CT','BC','ON','ID'))
def sendAccessFlags(self, flags):
def sendAccessFlags(self, access, talk):
log.msg("Sending ACL flags to %s: %s" % (self.user.ON, str((access, talk))))
FV = {True: '1', False: 'o'}
self.transport.write(chr(10))
self.sendLine('2') # TODO
self.sendLine('o')
self.sendLine('o')
self.sendLine(FV[access])
self.sendLine(FV[talk])
def sendTextMessage(self, clientId, message, target):
self.transport.write(chr(4))

View File

@ -35,7 +35,7 @@ class FRNUser(object):
self._fields[field.lower()] = str(value)
def get(self, field, default=''):
return self._fields[field.lower()]
return self._fields.get(field.lower(),'')
def items(self, *fields):
if len(fields) == 0: