diff --git a/luser/models.py b/luser/models.py index ef2034a..710ef68 100644 --- a/luser/models.py +++ b/luser/models.py @@ -1,6 +1,8 @@ import ldap3 from ldap3 import Server,Connection,ALL,MODIFY_REPLACE from datetime import datetime +import datetime as dt +import os OBJECTCLASSES = ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount', 'shadowAccount'] USERATTRIBUTES = ['cn' , 'sn', 'givenName', 'uid', 'uidNumber' , 'gidNumber', 'homeDirectory', 'loginShell', 'gecos' , 'shadowLastChange', 'shadowMax', 'userPassword', 'mail', 'description'] @@ -184,7 +186,7 @@ class LUSER(): messured in days from UNIX epoch ''' - return str((datetime.utcnow() - datetime(1970,1,1)).days) + return str((datetime.now() - datetime(1970,1,1)).days) def add(self, user, password): ''' @@ -323,3 +325,112 @@ class LUSER(): userpass = userpass.decode('utf-8') return userpass + +class ratelimit(): + ''' + Class that manages rate limiting + + period := time period in seconds when the limit restarts + limit := maxiumum number of actions allowed in the time period + path := path to the file where the number of occurances are stored + ''' + + def save(self): + f = open(self.path,'w') + f.write(str(self.period) + "\n") + f.write(str(self.limit) + "\n") + f.write(self.startdate.isoformat() + "\n") + f.write(str(self.current) + "\n") + f.close() + + def load(self): + f = open(self.path,'r') + self.period = int(f.readline()) + self.limit = int(f.readline()) + self.startdate = datetime.fromisoformat(f.readline()[:-1]) + self.current = int(f.readline()) + f.close() + + def parseperiod(self, period: str)->int: + last = period[-1] + + if last.isalpha() == False: + return int(period) + + number = int(period[:-1]) + + if last == 's': + return number + if last == 'm': + return number * 60 + if last == 'h': + return number * 3600 + if last == 'd': + return number * 86400 + if last == 'w': + return number * 604800 + + raise ValueError("Write the period in seconds as intiger or with accepted units as suffix: s(seconds), m(minutes), h(hours), d(days), w(weeks)") + + def __init__(self, period : str, limit : int, path : str): + self.period = self.parseperiod(period) + self.limit = limit + self.path = path + + # date from when the period start + self.startdate = datetime.now() + + # current number of actions in this time period + self.current = 0 + + if os.path.exists(path) == True: + setperiod = self.period + setlimit = self.limit + self.load() + self.period = setperiod + self.limit = setlimit + + # actions might have happened before the new smaller period started + if setperiod < self.period: + self.current = 0 + + self.save() + + self.update() + + def check(self, actions = 1)->bool: + self.update() + if self.current + actions > self.limit: + return False + else: + return True + + def add(self, actions = 1)->int: + self.update() + if self.check(actions): + self.current += actions + else: + return 0 + + self.save() + return self.current + + def sub(self, actions = 1)->int: + self.update() + self.current -= actions + if self.current < 0: + self.current = 0 + self.save() + return self.current + + def update(self)->bool: + r = False + self.load() + delta = (datetime.now() - self.startdate).seconds + if delta > self.period: + self.startdate += dt.timedelta(seconds=(delta - delta%self.period)) + self.current = 0 + r = True + self.save() + return r +