def setDatabases(self, settingsDatabase):
     self.__settingsTable = RMUserSettingsTable(settingsDatabase)
     self.__tokensTable = RMAuthTokensTable(settingsDatabase)
 def setDatabases(self, settingsDatabase):
     self.__settingsTable = RMUserSettingsTable(settingsDatabase)
     self.__tokensTable = RMAuthTokensTable(settingsDatabase)
class RMAuth:
    def __init__(self):
        self.__password = self.__encryptPassword("")
        self.__tokens = OrderedDict()
        self.__isLoaded = False
        self.__tokenLongExpirationTimeout = 5 * 365 * 24 * 3600 # ~5 years # TODO: OpenWRT time_t is 32bit Y2038 problem
        self.__tokenShortExpirationTimeout = 1 * 24 * 3600 # 1 day

        self.__settingsTable = None
        self.__tokensTable = None

    def setDatabases(self, settingsDatabase):
        self.__settingsTable = RMUserSettingsTable(settingsDatabase)
        self.__tokensTable = RMAuthTokensTable(settingsDatabase)

    def load(self):
        if not self.__isLoaded:
            password = self.__settingsTable.getPassword()
            if password:
                self.__password = password

            self.__tokensTable.deleteExpiredRecords(rmCurrentTimestamp())

            self.__tokens = self.__tokensTable.getAllRecords(True)
            self.__isLoaded = True

    def factoryReset(self):
        self.__password = self.__encryptPassword("")

        self.__tokens.clear()
        self.__tokensTable.deleteAllRecords()

        self.__settingsTable.savePassword(self.__password)

    def  doPasswordsMatch (self, password):
        if self.__password == self.__encryptPassword(password):
            return True
        return False

    def authenticateByPassword(self, password, remember, oldAccessToken):
        self.__deleteExpiredTokens()
        if self.doPasswordsMatch(password):
            self.__deleteAccessToken(oldAccessToken)
            return self.__generateToken(remember), self.__manglePassword(self.__password)
        return None, None

    def authenticateByToken(self, token):
        self.__deleteExpiredTokens()
        return self.__tokens.get(token, None)

    def authenticateByTOTP(self, totp):
        if self.validateTOTP(totp, length=6, interval=14400, drift=2):
            return self.__generateToken(longLiveToken=False)
        return None

    def changePassword(self, oldPassword, newPassword, oldAccessToken, requireOldPassword = True):
        if requireOldPassword and self.__encryptPassword(oldPassword) != self.__password:
            return None, None
        if not newPassword or len(newPassword) < 3:
            return None, None

        token = self.__tokens.get(oldAccessToken, None)

        self.__password = self.__encryptPassword(newPassword)
        self.__tokens.clear()

        self.__settingsTable.savePassword(self.__password)
        self.__tokensTable.deleteAllRecords()

        if token is None:
            token = self.__generateToken()
        else:
            self.__tokens[token.token] = token
            self.__tokensTable.addRecord(token.token, token.expiration)

        return token, self.__manglePassword(self.__password)

    def changePasswordEx(self, mangleddPassword, oldAccessToken):
        if not mangleddPassword or len(mangleddPassword) < len(self.__password):
            return None, None

        token = self.__tokens.get(oldAccessToken, None)
        encryptedPassword = self.__unmanglePassword(mangleddPassword)

        self.__password = encryptedPassword
        self.__tokens.clear()

        self.__settingsTable.savePassword(self.__password)
        self.__tokensTable.deleteAllRecords()

        if token is None:
            token = self.__generateToken()
        else:
            self.__tokens[token.token] = token
            self.__tokensTable.addRecord(token.token, token.expiration)

        return token, self.__manglePassword(self.__password)

    # Functions to generate/validate a TOTP
    # follows http://jacob.jkrall.net/totp/
    def generateTOTP(self, length=6, interval=30, asString=True, clock=None):
        if clock is None:
            clock = time.time()
        secret = self.__password
        counter = int(clock) // interval
        message = struct.pack(">Q", counter)
        hmacDigest = hmac.new(secret, message, hashlib.sha1).digest() # HMAC-SHA-1(SECRET, time()/30)
        lastByte = ord(hmacDigest[-1])
        offset = lastByte & 0xF # last nibble
        (dbc1, ) = struct.unpack(">I", hmacDigest[offset:offset + 4])  # Dynamic Binary Code #1
        dbc2 = dbc1 & 0x7fffffff # 31 bit number with top bit cleared # Dynamic Binary Code #2

        if asString:
            digits = str(dbc2)[-length:]
            return digits
        return dbc2

    def validateTOTP(self, totp, length=6, interval=30, drift=0):
        if totp is None:
            return False
        clock = time.time()
        secret = self.__password
        for d in range(-drift, drift + 1):
            candidate = self.generateTOTP(length, interval, asString=True, clock=int(clock) + (d * interval))
            #log.debug("Validate %s against candidate %s" % (totp, candidate))
            try:
                if totp == candidate:
                    return True
            except Exception,e:
                continue
        return False
class RMAuth:
    def __init__(self):
        self.__password = self.__encryptPassword("")
        self.__tokens = OrderedDict()
        self.__isLoaded = False
        self.__tokenLongExpirationTimeout = 5 * 365 * 24 * 3600  # ~5 years # TODO: OpenWRT time_t is 32bit Y2038 problem
        self.__tokenShortExpirationTimeout = 1 * 24 * 3600  # 1 day

        self.__settingsTable = None
        self.__tokensTable = None

    def setDatabases(self, settingsDatabase):
        self.__settingsTable = RMUserSettingsTable(settingsDatabase)
        self.__tokensTable = RMAuthTokensTable(settingsDatabase)

    def load(self):
        if not self.__isLoaded:
            password = self.__settingsTable.getPassword()
            if password:
                self.__password = password

            self.__tokensTable.deleteExpiredRecords(rmCurrentTimestamp())

            self.__tokens = self.__tokensTable.getAllRecords(True)
            self.__isLoaded = True

    def factoryReset(self):
        self.__password = self.__encryptPassword("")

        self.__tokens.clear()
        self.__tokensTable.deleteAllRecords()

        self.__settingsTable.savePassword(self.__password)

    def doPasswordsMatch(self, password):
        if self.__password == self.__encryptPassword(password):
            return True
        return False

    def authenticateByPassword(self, password, remember, oldAccessToken):
        self.__deleteExpiredTokens()
        if self.doPasswordsMatch(password):
            self.__deleteAccessToken(oldAccessToken)
            return self.__generateToken(remember), self.__manglePassword(
                self.__password)
        return None, None

    def authenticateByToken(self, token):
        self.__deleteExpiredTokens()
        return self.__tokens.get(token, None)

    def authenticateByTOTP(self, totp):
        if self.validateTOTP(totp, length=6, interval=14400, drift=2):
            return self.__generateToken(longLiveToken=False)
        return None

    def changePassword(self,
                       oldPassword,
                       newPassword,
                       oldAccessToken,
                       requireOldPassword=True):
        if requireOldPassword and self.__encryptPassword(
                oldPassword) != self.__password:
            return None, None
        if not newPassword or len(newPassword) < 3:
            return None, None

        token = self.__tokens.get(oldAccessToken, None)

        self.__password = self.__encryptPassword(newPassword)
        self.__tokens.clear()

        self.__settingsTable.savePassword(self.__password)
        self.__tokensTable.deleteAllRecords()

        if token is None:
            token = self.__generateToken()
        else:
            self.__tokens[token.token] = token
            self.__tokensTable.addRecord(token.token, token.expiration)

        return token, self.__manglePassword(self.__password)

    def changePasswordEx(self, mangleddPassword, oldAccessToken):
        if not mangleddPassword or len(mangleddPassword) < len(
                self.__password):
            return None, None

        token = self.__tokens.get(oldAccessToken, None)
        encryptedPassword = self.__unmanglePassword(mangleddPassword)

        self.__password = encryptedPassword
        self.__tokens.clear()

        self.__settingsTable.savePassword(self.__password)
        self.__tokensTable.deleteAllRecords()

        if token is None:
            token = self.__generateToken()
        else:
            self.__tokens[token.token] = token
            self.__tokensTable.addRecord(token.token, token.expiration)

        return token, self.__manglePassword(self.__password)

    # Functions to generate/validate a TOTP
    # follows http://jacob.jkrall.net/totp/
    def generateTOTP(self, length=6, interval=30, asString=True, clock=None):
        if clock is None:
            clock = time.time()
        secret = self.__password
        counter = int(clock) // interval
        message = struct.pack(">Q", counter)
        hmacDigest = hmac.new(
            secret, message,
            hashlib.sha1).digest()  # HMAC-SHA-1(SECRET, time()/30)
        lastByte = ord(hmacDigest[-1])
        offset = lastByte & 0xF  # last nibble
        (dbc1, ) = struct.unpack(">I", hmacDigest[offset:offset +
                                                  4])  # Dynamic Binary Code #1
        dbc2 = dbc1 & 0x7fffffff  # 31 bit number with top bit cleared # Dynamic Binary Code #2

        if asString:
            digits = str(dbc2)[-length:]
            return digits
        return dbc2

    def validateTOTP(self, totp, length=6, interval=30, drift=0):
        if totp is None:
            return False
        clock = time.time()
        secret = self.__password
        for d in range(-drift, drift + 1):
            candidate = self.generateTOTP(length,
                                          interval,
                                          asString=True,
                                          clock=int(clock) + (d * interval))
            #log.debug("Validate %s against candidate %s" % (totp, candidate))
            try:
                if totp == candidate:
                    return True
            except Exception, e:
                continue
        return False