def addStamp(self, stampclass, keys, passkey=None, **kwargs): """Adds a stamp of type `class` to the current envelope.""" if passkey is not None: keys.unlock(passkey) signature = keys.signstring(self.payload.text()) # Generate the full stamp obj we will insert. fullstamp = {} fullstamp['class'] = stampclass fullstamp['keyformat'] = keys.keydetails['format'] fullstamp['pubkey'] = keys.pubkey fullstamp['signature'] = signature fullstamp['time_added'] = TavernUtils.inttime() # Copy in any passed values for key in kwargs.keys(): # Remove the kwargs we know we already added if key not in ["stampclass", "keys", "passkey"]: fullstamp[key] = kwargs[key] proof = {} proof['class'] = 'sha256' proof['difficulty'] = self.server.serversettings.settings[ 'proof-of-work-difficulty'] proof['proof'] = TavernUtils.proveWork(self.payload.hash(), proof['difficulty']) fullstamp['proof-of-work'] = proof self.dict['envelope']['stamps'].append(fullstamp)
def addStamp(self, stampclass, keys, passkey=None, **kwargs): """Adds a stamp of type `class` to the current envelope.""" if passkey is not None: keys.unlock(passkey) signature = keys.signstring(self.payload.text()) # Generate the full stamp obj we will insert. fullstamp = {} fullstamp['class'] = stampclass fullstamp['keyformat'] = keys.keydetails['format'] fullstamp['pubkey'] = keys.pubkey fullstamp['signature'] = signature fullstamp['time_added'] = TavernUtils.inttime() # Copy in any passed values for key in kwargs.keys(): # Remove the kwargs we know we already added if key not in ["stampclass", "keys", "passkey"]: fullstamp[key] = kwargs[key] proof = {} proof['class'] = 'sha256' proof[ 'difficulty'] = self.server.serversettings.settings[ 'proof-of-work-difficulty'] proof['proof'] = TavernUtils.proveWork( self.payload.hash(), proof['difficulty']) fullstamp['proof-of-work'] = proof self.dict['envelope']['stamps'].append(fullstamp)
def generate(self, password=None, passkey=None, random=False, autoexpire=False): """Generate a new set of keys. Store only the encrypted version """ if password is None and passkey is None and random is False: raise Exception( 'KeyError', 'Invalid call to generate() - Must include a password, passkey, or Random' ) if random is True: password = TavernUtils.randstr(100) ret = password else: ret = None super().generate(autoexpire=autoexpire) if self.passkey is None and passkey is not None: self.passkey = passkey if self.passkey is None and password is not None: self.passkey = self.get_passkey(password=password) self.lock(passkey=self.passkey) return ret
def generate(self, password=None, passkey=None, random=False, autoexpire=False): """Generate a new set of keys. Store only the encrypted version """ if password is None and passkey is None and random is False: raise Exception( 'KeyError', 'Invalid call to generate() - Must include a password, passkey, or Random') if random is True: password = TavernUtils.randstr(100) ret = password else: ret = None super().generate(autoexpire=autoexpire) if self.passkey is None and passkey is not None: self.passkey = passkey if self.passkey is None and password is not None: self.passkey = self.get_passkey(password=password) self.lock(passkey=self.passkey) return ret
def messages(self, topic, maxposts, before=None): """Get all messages in a topic, no later than `before`""" if topic != 'all': if isinstance(topic, str): topics = [] topics.append(server.sorttopic(topic)) elif isinstance(topic, list): topics = [] for t in topic: topics.append(server.sorttopic(t)) sorttopic = {'envelope.local.sorttopic': {'$in': topics}} else: sorttopic = {'envelope.local.sorttopic': {'$exists': True}} # Don't do this in the def, so that our cache is respected. if before is None: before = TavernUtils.inttime() # Append our search topic query. subjects = [] search = {'envelope.payload.class': 'message', 'envelope.payload.regarding': {'$exists': False}, 'envelope.local.time_added': {'$lt': before}} search.update(sorttopic) for envelope in server.db.unsafe.find('envelopes', search, limit=maxposts, sortkey='envelope.local.time_added', sortdirection='descending'): e = Envelope() e.loaddict(envelope) subjects.append(e) return subjects
def getbackdate(self, topic, maxposts, after): """Get the earliest dated message, before `after`""" sorttopic = server.sorttopic(topic) subjects = [] if topic != "all": for envelope in server.db.unsafe.find('envelopes', {'envelope.local.sorttopic': sorttopic, 'envelope.payload.class': 'message', 'envelope.payload.regarding': {'$exists': False}, 'envelope.local.time_added': {'$gt': after}}, limit=maxposts + 1, sortkey='envelope.local.time_added', sortdirection='ascending'): subjects.append(envelope) else: for envelope in server.db.unsafe.find('envelopes', {'envelope.payload.class': 'message', 'envelope.payload.regarding': {'$exists': False}, 'envelope.local.time_added': {'$gt': after}}, limit=maxposts + 1, sortkey='envelope.local.time_added', sortdirection='ascending'): subjects.append(envelope) # Adding 1 to maxposts above, because we're going to use this to get the 10 posts AFTER the date we return from this function. # This is also the reason why, if we don't have maxposts posts, we # subtract 1 below. This ensures that we get ALL the posts in the # range. if len(subjects) > 0: if len(subjects) <= maxposts: ret = subjects[-1]['envelope']['local']['time_added'] + 1 print("ret") else: ret = subjects[-1]['envelope']['local']['time_added'] else: ret = TavernUtils.inttime() return ret
def encrypt_file(self, newfile): """Encrypt a string, to the gpg key of the specified recipient)""" # In order for this to work, we need to temporarily import B's key into A's keyring. # We then do the encryptions, and immediately remove it. recipient = Key(pub=encrypt_to) recipient._format_keys() self.gpg.import_keys(recipient.pubkey) tmpfilename = "tmp/gpgfiles/" + TavernUtils.longtime( ) + TavernUtils.randstr(50, printable=True) self.gpg.encrypt_file(stream=oldfile, recipients=[recipient.fingerprint], always_trust=True, armor=True, output=tmpfilename) self.gpg.delete_keys(recipient.fingerprint) return tmpfilename
def encrypt_file(self, newfile): """Encrypt a string, to the gpg key of the specified recipient)""" # In order for this to work, we need to temporarily import B's key into A's keyring. # We then do the encryptions, and immediately remove it. recipient = Key(pub=encrypt_to) recipient._format_keys() self.gpg.import_keys(recipient.pubkey) tmpfilename = "tmp/gpgfiles/" + TavernUtils.longtime( ) + TavernUtils.randstr(50, printable=True) self.gpg.encrypt_file( stream=oldfile, recipients=[recipient.fingerprint], always_trust=True, armor=True, output=tmpfilename) self.gpg.delete_keys(recipient.fingerprint) return tmpfilename
def getvars(self, AllowGuestKey=True): """Retrieve the basic user variables out of your cookies.""" self.user = User() # Load in our session token if we have one. userid = self.get_secure_cookie("tavern_settings") loaded = False if userid is not None: loaded = self.user.load_mongo_by_sha512(userid.decode('utf-8')) if loaded is False: # Either no cookie, or bad cookie. Either way, abort. self.clear_all_cookies() self.set_cookie("_xsrf", self.xsrf_token) self.user = User() # Get the passkey to unlock our privkey passkey = self.get_secure_cookie("tavern_passkey") if passkey is not None: self.user.passkey = passkey # Ensure our user has all expected fields # This method will also generate a key if necessary. if self.user.generate(AllowGuestKey=AllowGuestKey): self.setvars() # Check to see if we have support for datauris in our browser. # If we do, send the first ~10 pages with datauris. # After that switch back, since caching the images is likely to be # better, if you're a recurrent reader if not 'datauri' in self.user.UserSettings: if TavernUtils.randrange(1, 10) == 5: self.user.UserSettings['datauri'] = False if 'datauri' in self.user.UserSettings: self.user.datauri = self.user.UserSettings['datauri'] elif self.browser[ 'ua_family'] == 'IE' and self.browser['ua_versions'][0] < 8: self.user.datauri = False elif self.browser[ 'ua_family'] == 'IE' and self.browser['ua_versions'][0] >= 8: self.user.datauri = True else: self.user.datauri = True if 'datauri' in self.request.arguments: if self.get_argument("datauri").lower() == 'true': self.user.datauri = True elif self.get_argument("datauri").lower() == 'false': self.user.datauri = False return self.user.UserSettings['username']
def getvars(self, AllowGuestKey=True): """Retrieve the basic user variables out of your cookies.""" self.user = User() # Load in our session token if we have one. userid = self.get_secure_cookie("tavern_settings") loaded = False if userid is not None: loaded = self.user.load_mongo_by_sha512(userid.decode('utf-8')) if loaded is False: # Either no cookie, or bad cookie. Either way, abort. self.clear_all_cookies() self.set_cookie("_xsrf", self.xsrf_token) self.user = User() # Get the passkey to unlock our privkey passkey = self.get_secure_cookie("tavern_passkey") if passkey is not None: self.user.passkey = passkey # Ensure our user has all expected fields # This method will also generate a key if necessary. if self.user.generate(AllowGuestKey=AllowGuestKey): self.setvars() # Check to see if we have support for datauris in our browser. # If we do, send the first ~10 pages with datauris. # After that switch back, since caching the images is likely to be # better, if you're a recurrent reader if not 'datauri' in self.user.UserSettings: if TavernUtils.randrange(1, 10) == 5: self.user.UserSettings['datauri'] = False if 'datauri' in self.user.UserSettings: self.user.datauri = self.user.UserSettings['datauri'] elif self.browser['ua_family'] == 'IE' and self.browser['ua_versions'][0] < 8: self.user.datauri = False elif self.browser['ua_family'] == 'IE' and self.browser['ua_versions'][0] >= 8: self.user.datauri = True else: self.user.datauri = True if 'datauri' in self.request.arguments: if self.get_argument("datauri").lower() == 'true': self.user.datauri = True elif self.get_argument("datauri").lower() == 'false': self.user.datauri = False return self.user.UserSettings['username']
def topicCount(self, topic, after=0, before=None, toponly=True): # Don't do this in the def, so that our cache is respected. if before is None: before = TavernUtils.inttime() sorttopic = server.sorttopic(topic) if toponly: count = server.db.unsafe.count( 'envelopes', {'envelope.local.time_added': {'$lt': before}, 'envelope.local.time_added': {'$gt': after}, 'envelope.payload.regarding': {'$exists': False}, 'envelope.local.sorttopic': sorttopic}) else: count = server.db.unsafe.count( 'envelopes', {'envelope.local.time_added': {'$lt': before}, 'envelope.local.time_added': {'$gt': after}, 'envelope.local.sorttopic': sorttopic}) return count
def validate(self): """Ensures an envelope is valid, legal, and according to spec.""" self.registerpayload() # Check headers if 'envelope' not in self.dict: self.server.logger.debug("Invalid Envelope. No Header") return False # Ensure we have 1 and only 1 author signature stamp stamps = self.dict['envelope']['stamps'] foundauthor = 0 for stamp in stamps: if stamp['class'] == "author": foundauthor += 1 if foundauthor == 0: if self.dict['envelope']['payload']['class'] != 'privatemessage': self.server.logger.debug("No author stamp.") return False if foundauthor > 1: self.server.logger.debug("Too Many author stamps") return False # Ensure Every stamp validates. stamps = self.dict['envelope']['stamps'] for stamp in stamps: if 'keyformat' not in stamp: self.server.logger.debug("Key format is not specififed.") return False if stamp['keyformat'] != 'gpg': self.server.logger.debug( "Key not in acceptable container format.") return False # Retrieve the key, ensure it's valid. stampkey = Key(pub=stamp['pubkey']) if stampkey is None: self.server.logger.debug("Key is invalid.") return False if stampkey.keydetails['algorithm'] not in ['ElGamal', 'RSA', 'DSA']: self.server.logger.debug( "Key does not use an acceptable algorithm.") return False if stampkey.keydetails['algorithm'] in ['ElGamal', 'RSA', 'DSA']: if int(stampkey.keydetails['length']) < 3072: self.server.logger.debug("Key is too small.") return False elif stampkey.keydetails['algorithm'] == 'ECDSA': if int(stampkey.keydetails['length']) < 233: self.server.logger.debug("Key is too small.") return False for uid in stampkey.keydetails['uids']: if uid not in [None, 'TAVERN', '']: self.server.logger.debug( "Key UID is potentially leaking information.") return False # Ensure it matches the signature. if not stampkey.verify_string(stringtoverify=self.payload.text(), signature=stamp['signature']): self.server.logger.debug("Signature Failed to verify for stamp :: " + stamp['class'] + " :: " + stamp['pubkey']) return False # If they specify a proof-of-work in the stamp, make sure it's # valid. if 'proof-of-work' in stamp: proof = stamp['proof-of-work']['proof'] difficulty = stamp['proof-of-work']['difficulty'] if stamp['proof-of-work']['class'] == 'sha256': result = TavernUtils.checkWork( self.payload.hash(), proof, difficulty) if result is False: self.server.logger.debug( "Proof of work cannot be verified.") return False else: self.server.logger.debug( "Proof of work in unrecognized format. Ignoring.") # It's OK if they don't include a user-agent, but not if they include a # bad one. # if 'useragent' in self.dict['envelope']['payload']['author']: # if not 'name' in self.dict['envelope']['payload']['author']['useragent']: # self.server.logger.debug( # "If you supply a user agent, it must have a valid name") # return False # if not isinstance(self.dict['envelope']['payload']['author']['useragent']['version'], int) and not isinstance(self.dict['envelope']['payload']['author']['useragent']['version'], float): # self.server.logger.debug( # "Bad Useragent version must be a float or integer") # return False # Do this last, so we don't waste time if the stamps are bad. if not self.payload.validate(): self.server.logger.info("Payload does not validate.") return False return True
def validate(self): """Ensures an envelope is valid, legal, and according to spec.""" self.registerpayload() # Check headers if 'envelope' not in self.dict: self.server.logger.debug("Invalid Envelope. No Header") return False # Ensure we have 1 and only 1 author signature stamp stamps = self.dict['envelope']['stamps'] foundauthor = 0 for stamp in stamps: if stamp['class'] == "author": foundauthor += 1 if foundauthor == 0: if self.dict['envelope']['payload']['class'] != 'privatemessage': self.server.logger.debug("No author stamp.") return False if foundauthor > 1: self.server.logger.debug("Too Many author stamps") return False # Ensure Every stamp validates. stamps = self.dict['envelope']['stamps'] for stamp in stamps: if 'keyformat' not in stamp: self.server.logger.debug("Key format is not specififed.") return False if stamp['keyformat'] != 'gpg': self.server.logger.debug( "Key not in acceptable container format.") return False # Retrieve the key, ensure it's valid. stampkey = Key(pub=stamp['pubkey']) if stampkey is None: self.server.logger.debug("Key is invalid.") return False if stampkey.keydetails['algorithm'] not in [ 'ElGamal', 'RSA', 'DSA' ]: self.server.logger.debug( "Key does not use an acceptable algorithm.") return False if stampkey.keydetails['algorithm'] in ['ElGamal', 'RSA', 'DSA']: if int(stampkey.keydetails['length']) < 3072: self.server.logger.debug("Key is too small.") return False elif stampkey.keydetails['algorithm'] == 'ECDSA': if int(stampkey.keydetails['length']) < 233: self.server.logger.debug("Key is too small.") return False for uid in stampkey.keydetails['uids']: if uid not in [None, 'TAVERN', '']: self.server.logger.debug( "Key UID is potentially leaking information.") return False # Ensure it matches the signature. if not stampkey.verify_string(stringtoverify=self.payload.text(), signature=stamp['signature']): self.server.logger.debug( "Signature Failed to verify for stamp :: " + stamp['class'] + " :: " + stamp['pubkey']) return False # If they specify a proof-of-work in the stamp, make sure it's # valid. if 'proof-of-work' in stamp: proof = stamp['proof-of-work']['proof'] difficulty = stamp['proof-of-work']['difficulty'] if stamp['proof-of-work']['class'] == 'sha256': result = TavernUtils.checkWork(self.payload.hash(), proof, difficulty) if result is False: self.server.logger.debug( "Proof of work cannot be verified.") return False else: self.server.logger.debug( "Proof of work in unrecognized format. Ignoring.") # It's OK if they don't include a user-agent, but not if they include a # bad one. # if 'useragent' in self.dict['envelope']['payload']['author']: # if not 'name' in self.dict['envelope']['payload']['author']['useragent']: # self.server.logger.debug( # "If you supply a user agent, it must have a valid name") # return False # if not isinstance(self.dict['envelope']['payload']['author']['useragent']['version'], int) and not isinstance(self.dict['envelope']['payload']['author']['useragent']['version'], float): # self.server.logger.debug( # "Bad Useragent version must be a float or integer") # return False # Do this last, so we don't waste time if the stamps are bad. if not self.payload.validate(): self.server.logger.info("Payload does not validate.") return False return True
def updateconfig(self): # Create a string/immutable version of ServerSettings that we can # compare against later to see if anything changed. tmpsettings = str(self.settings) if not 'hostname' in self.settings: self.settings['hostname'] = platform.node() if not 'web_url' in self.settings: self.settings['web_url'] = None if not 'downloadsurl' in self.settings: self.settings['downloadsurl'] = '/binaries/' # The Canonical URL is specified so that Google won't detect duplicate # content for every Tavern server, and penalize. if not 'canonical_url' in self.settings: self.settings['canonical_url'] = "https://tavern.is" if not 'logfile' in self.settings: self.settings[ 'logfile'] = "logs/" + self.settings['hostname'] + '.log' if not 'loglevel' in self.settings: self.settings[ 'loglevel'] = "INFO" if not 'mongo-hostname' in self.settings: self.settings['mongo-hostname'] = 'localhost' if not 'mongo-port' in self.settings: self.settings['mongo-port'] = 27017 if not 'postgres-hostname' in self.settings: self.settings['postgres-hostname'] = 'localhost' if not 'postgres-user' in self.settings: self.settings['postgres-user'] = getpass.getuser() if not 'postgres-hostname' in self.settings: self.settings['postgres-hostname'] = "localhost" if not 'postgres-port' in self.settings: self.settings['postgres-port'] = 5432 if not 'dbname' in self.settings: self.settings['dbname'] = 'Tavern' if not 'bin-mongo-hostname' in self.settings: self.settings['bin-mongo-hostname'] = 'localhost' if not 'bin-mongo-port' in self.settings: self.settings['bin-mongo-port'] = 27017 if not 'bin-mongo-db' in self.settings: self.settings['bin-mongo-db'] = 'Tavern_Binaries' if not 'sessions-db-hostname' in self.settings: self.settings['sessions-db-hostname'] = 'localhost' if not 'sessions-db-port' in self.settings: self.settings['sessions-db-port'] = 27017 if not 'sessions-db-name' in self.settings: self.settings['sessions-db-name'] = 'Tavern_Sessions' if not 'cache' in self.settings: self.settings['cache'] = {} if not 'user-trust' in self.settings['cache']: self.settings['cache']['user-trust'] = {} self.settings['cache']['user-trust']['seconds'] = 300 self.settings['cache']['user-trust']['size'] = 10000 if not 'message-ratings' in self.settings['cache']: self.settings['cache']['message-ratings'] = {} self.settings['cache']['message-ratings']['seconds'] = 300 self.settings['cache']['message-ratings']['size'] = 10000 if not 'avatarcache' in self.settings['cache']: self.settings['cache']['avatarcache'] = {} self.settings['cache']['avatarcache']['size'] = 100000 self.settings['cache']['avatarcache']['seconds'] = None if not 'embedded' in self.settings['cache']: self.settings['cache']['embedded'] = {} self.settings['cache']['embedded']['size'] = 1000 self.settings['cache']['embedded']['seconds'] = 3601 if not 'user-note' in self.settings['cache']: self.settings['cache']['user-note'] = {} self.settings['cache']['user-note']['size'] = 10000 self.settings['cache']['user-note']['seconds'] = 60 if not 'subjects-in-topic' in self.settings['cache']: self.settings['cache']['subjects-in-topic'] = {} self.settings['cache']['subjects-in-topic']['size'] = 1000 self.settings['cache']['subjects-in-topic']['seconds'] = 1 if not 'toptopics' in self.settings['cache']: self.settings['cache']['toptopics'] = {} self.settings['cache']['toptopics']['size'] = 1 self.settings['cache']['toptopics']['seconds'] = 3602 # Settings related to the Web View if not 'templates' in self.settings['cache']: self.settings['cache']['templates'] = {} self.settings['cache']['templates']['size'] = 1000 self.settings['cache']['templates']['seconds'] = 5 if not 'getpagelemenent' in self.settings['cache']: self.settings['cache']['getpagelemenent'] = {} self.settings['cache']['getpagelemenent']['size'] = 1000 self.settings['cache']['getpagelemenent']['seconds'] = 20 if not 'message-page' in self.settings['cache']: self.settings['cache']['message-page'] = {} self.settings['cache']['message-page']['size'] = 1000 self.settings['cache']['message-page']['seconds'] = 1 if not 'topic-page' in self.settings['cache']: self.settings['cache']['topic-page'] = {} self.settings['cache']['topic-page']['size'] = 1000 self.settings['cache']['topic-page']['seconds'] = 1 if not 'uasparser' in self.settings['cache']: self.settings['cache']['uasparser'] = {} self.settings['cache']['uasparser']['size'] = 1000 self.settings['cache']['uasparser']['seconds'] = 36001 if not 'topiccount' in self.settings['cache']: self.settings['cache']['topiccount'] = {} self.settings['cache']['topiccount']['size'] = 1000 self.settings['cache']['topiccount']['seconds'] = 10 if not 'receiveEnvelope' in self.settings['cache']: self.settings['cache']['receiveEnvelope'] = {} self.settings['cache']['receiveEnvelope']['size'] = 10000 self.settings['cache']['receiveEnvelope']['seconds'] = 1000 if not 'getUsersPosts' in self.settings['cache']: self.settings['cache']['getUsersPosts'] = {} self.settings['cache']['getUsersPosts']['size'] = 10000 self.settings['cache']['getUsersPosts']['seconds'] = 2 if not 'sorttopic' in self.settings['cache']: self.settings['cache']['sorttopic'] = {} self.settings['cache']['sorttopic']['size'] = 1000000 self.settings['cache']['sorttopic']['seconds'] = 10000000000 if not 'formatText' in self.settings['cache']: self.settings['cache']['formatText'] = {} self.settings['cache']['formatText']['size'] = 1000 self.settings['cache']['formatText']['seconds'] = 10000000000 if not 'error_envelope' in self.settings['cache']: self.settings['cache']['error_envelope'] = {} self.settings['cache']['error_envelope']['size'] = 20 self.settings['cache']['error_envelope']['seconds'] = 10000000000 if not 'KeyGenerator' in self.settings: self.settings['KeyGenerator'] = {} if not 'num_pregens' in self.settings['KeyGenerator']: self.settings['KeyGenerator']['num_pregens'] = 5 if not 'workers' in self.settings['KeyGenerator']: self.settings['KeyGenerator']['workers'] = 1 if not 'keys' in self.settings: self.settings['keys'] = {} if not 'keysize' in self.settings['keys']: self.settings['keys']['keysize'] = 3072 if not 'upload-dir' in self.settings: self.settings['upload-dir'] = '../nginx/uploads' if not 'mark-origin' in self.settings: self.settings['mark-origin'] = False if not 'mark-seen' in self.settings: self.settings['mark-seen'] = False if not 'max-upload-preview-size' in self.settings: self.settings['max-upload-preview-size'] = 10485760 if not 'cookie-encryption' in self.settings: self.settings['cookie-encryption'] = TavernUtils.randstr(255) if not 'serverkey-password' in self.settings: self.settings[ 'serverkey-password'] = TavernUtils.randstr(255) if not 'embedserver' in self.settings: self.settings['embedserver'] = 'http://embed.is' if not 'maxembeddedurls' in self.settings: self.settings['maxembeddedurls'] = 10 if not 'mongo-connections' in self.settings: self.settings['mongo-connections'] = 10 if not 'dbtype' in self.settings: self.settings['dbtype'] = 'mongo' if not 'proof-of-work-difficulty' in self.settings: self.settings['proof-of-work-difficulty'] = 19 # Report back on if there were any changes. if str(self.settings) == tmpsettings: return False else: return True
def decrypt_file(self, tmpfile): tmpfilename = "tmp/gpgfiles/" + TavernUtils.longtime( ) + TavernUtils.randstr(50, printable=True) self.gpg.decrypt_file(tmpfile, output=tmpfilename) return tmpfilename
def decrypt_file(self, tmpfile): tmpfilename = "tmp/gpgfiles/" + TavernUtils.longtime( ) + TavernUtils.randstr(50, printable=True) self.gpg.decrypt_file(tmpfile, output=tmpfilename) return tmpfilename
def __init__(self, settingsfile=None, workingdir=None, slot='default'): super().__init__(slot) # Don't run this more than once. if self.__dict__.get('set') is True: return else: self.set = True self.logger = logging.getLogger('Tavern') # Should the server run in debug mode self.debug = False self.serversettings = ServerSettings.ServerSettings(settingsfile) if self.serversettings.updateconfig(): self.serversettings.saveconfig() if not 'pubkey' in self.serversettings.settings or not 'privkey' in self.serversettings.settings: # Generate New config self.logger.info("Generating new Config") self.ServerKeys = Key() self.ServerKeys.generate() # We can't encrypt this.. We'd need to store the key here on the machine... # If you're worried about it, encrypt the disk, or use a loopback. self.serversettings.settings['pubkey'] = self.ServerKeys.pubkey self.serversettings.settings['privkey'] = self.ServerKeys.privkey self.serversettings.updateconfig() self.serversettings.saveconfig() self.mc = OrderedDict self.unusedkeycache = multiprocessing.Queue( self.serversettings.settings['KeyGenerator']['num_pregens']) # Break out the settings into it's own file, so we can include it without including all of server # This does cause a few shenanigans while loading here, but hopefully # it's minimal self.ServerKeys = Key(pub=self.serversettings.settings['pubkey'], priv=self.serversettings.settings['privkey']) self.db = DBWrapper( name=self.serversettings.settings['dbname'], dbtype=self.serversettings.settings['dbtype'], host=self.serversettings.settings['mongo-hostname'], port=self.serversettings.settings['mongo-port']) self.sessions = DBWrapper( name=self.serversettings.settings['sessions-db-name'], dbtype=self.serversettings.settings['dbtype'], host=self.serversettings.settings['sessions-db-hostname'], port=self.serversettings.settings['sessions-db-port']) self.binaries = DBWrapper( name=self.serversettings.settings['bin-mongo-db'], dbtype='mongo', host=self.serversettings.settings['bin-mongo-hostname'], port=self.serversettings.settings['bin-mongo-port']) self.bin_GridFS = GridFS(self.binaries.unsafe.mongo) # Ensure we have Proper indexes. self.db.safe.ensure_index('envelope', 'envelope.local.time_added') self.db.safe.ensure_index('envelope', 'envelope.local.sorttopic') self.db.safe.ensure_index('envelope', 'envelope.local.payload_sha512') self.db.safe.ensure_index('envelope', 'envelope.payload.class') self.db.safe.ensure_index('envelope', 'envelope.payload.regarding') self.db.safe.ensure_index( 'envelope', 'envelope.payload.binaries.sha_512') self.db.safe.ensure_index('envelope', 'envelope.local.payload_sha512') self.db.safe.ensure_index('envelope', 'envelope.payload.author.pubkey') self.db.safe.ensure_index('envelope', 'envelope.payload.author.pubkey') self.db.safe.ensure_index('envelope', 'usertrusts.asking') self.db.safe.ensure_index('envelope', 'incomingtrust') self.binaries.safe.ensure_index('fs.files', 'filename') self.binaries.safe.ensure_index('fs.files', 'uploadDate') self.binaries.safe.ensure_index('fs.files', '_id') self.binaries.safe.ensure_index('fs.files', 'uploadDate') # Get a list of all the valid templates that can be used, to compare # against later on. self.availablethemes = [] for name in os.listdir('themes'): if os.path.isdir(os.path.join('themes', name)): if name[:1] != ".": self.availablethemes.append(name) self.serversettings.settings['static-revision'] = int(time.time()) self.fortune = TavernUtils.randomWords(fortunefile="data/fortunes") self.wordlist = TavernUtils.randomWords(fortunefile="data/wordlist") # Define out logging options. formatter = logging.Formatter('[%(levelname)s] %(message)s') self.consolehandler = logging.StreamHandler() self.consolehandler.setFormatter(formatter) self.handler_file = logging.FileHandler( filename=self.serversettings.settings['logfile']) self.handler_file.setFormatter(formatter) self.logger.setLevel(self.serversettings.settings['loglevel']) level = self.serversettings.settings['loglevel'] # Cache our JS, so we can include it later. file = open("static/scripts/instance.min.js") self.logger.info("Cached JS") TavernCache.cache['instance.js'] = file.read() file.close() self.guestacct = None
def updateconfig(self): # Create a string/immutable version of ServerSettings that we can # compare against later to see if anything changed. tmpsettings = str(self.settings) if not 'hostname' in self.settings: self.settings['hostname'] = platform.node() if not 'web_url' in self.settings: self.settings['web_url'] = None if not 'downloadsurl' in self.settings: self.settings['downloadsurl'] = '/binaries/' # The Canonical URL is specified so that Google won't detect duplicate # content for every Tavern server, and penalize. if not 'canonical_url' in self.settings: self.settings['canonical_url'] = "https://tavern.is" if not 'logfile' in self.settings: self.settings[ 'logfile'] = "logs/" + self.settings['hostname'] + '.log' if not 'loglevel' in self.settings: self.settings['loglevel'] = "INFO" if not 'mongo-hostname' in self.settings: self.settings['mongo-hostname'] = 'localhost' if not 'mongo-port' in self.settings: self.settings['mongo-port'] = 27017 if not 'postgres-hostname' in self.settings: self.settings['postgres-hostname'] = 'localhost' if not 'postgres-user' in self.settings: self.settings['postgres-user'] = getpass.getuser() if not 'postgres-hostname' in self.settings: self.settings['postgres-hostname'] = "localhost" if not 'postgres-port' in self.settings: self.settings['postgres-port'] = 5432 if not 'dbname' in self.settings: self.settings['dbname'] = 'Tavern' if not 'bin-mongo-hostname' in self.settings: self.settings['bin-mongo-hostname'] = 'localhost' if not 'bin-mongo-port' in self.settings: self.settings['bin-mongo-port'] = 27017 if not 'bin-mongo-db' in self.settings: self.settings['bin-mongo-db'] = 'Tavern_Binaries' if not 'sessions-db-hostname' in self.settings: self.settings['sessions-db-hostname'] = 'localhost' if not 'sessions-db-port' in self.settings: self.settings['sessions-db-port'] = 27017 if not 'sessions-db-name' in self.settings: self.settings['sessions-db-name'] = 'Tavern_Sessions' if not 'cache' in self.settings: self.settings['cache'] = {} if not 'user-trust' in self.settings['cache']: self.settings['cache']['user-trust'] = {} self.settings['cache']['user-trust']['seconds'] = 300 self.settings['cache']['user-trust']['size'] = 10000 if not 'message-ratings' in self.settings['cache']: self.settings['cache']['message-ratings'] = {} self.settings['cache']['message-ratings']['seconds'] = 300 self.settings['cache']['message-ratings']['size'] = 10000 if not 'avatarcache' in self.settings['cache']: self.settings['cache']['avatarcache'] = {} self.settings['cache']['avatarcache']['size'] = 100000 self.settings['cache']['avatarcache']['seconds'] = None if not 'embedded' in self.settings['cache']: self.settings['cache']['embedded'] = {} self.settings['cache']['embedded']['size'] = 1000 self.settings['cache']['embedded']['seconds'] = 3601 if not 'user-note' in self.settings['cache']: self.settings['cache']['user-note'] = {} self.settings['cache']['user-note']['size'] = 10000 self.settings['cache']['user-note']['seconds'] = 60 if not 'subjects-in-topic' in self.settings['cache']: self.settings['cache']['subjects-in-topic'] = {} self.settings['cache']['subjects-in-topic']['size'] = 1000 self.settings['cache']['subjects-in-topic']['seconds'] = 1 if not 'toptopics' in self.settings['cache']: self.settings['cache']['toptopics'] = {} self.settings['cache']['toptopics']['size'] = 1 self.settings['cache']['toptopics']['seconds'] = 3602 # Settings related to the Web View if not 'templates' in self.settings['cache']: self.settings['cache']['templates'] = {} self.settings['cache']['templates']['size'] = 1000 self.settings['cache']['templates']['seconds'] = 5 if not 'getpagelemenent' in self.settings['cache']: self.settings['cache']['getpagelemenent'] = {} self.settings['cache']['getpagelemenent']['size'] = 1000 self.settings['cache']['getpagelemenent']['seconds'] = 20 if not 'message-page' in self.settings['cache']: self.settings['cache']['message-page'] = {} self.settings['cache']['message-page']['size'] = 1000 self.settings['cache']['message-page']['seconds'] = 1 if not 'topic-page' in self.settings['cache']: self.settings['cache']['topic-page'] = {} self.settings['cache']['topic-page']['size'] = 1000 self.settings['cache']['topic-page']['seconds'] = 1 if not 'uasparser' in self.settings['cache']: self.settings['cache']['uasparser'] = {} self.settings['cache']['uasparser']['size'] = 1000 self.settings['cache']['uasparser']['seconds'] = 36001 if not 'topiccount' in self.settings['cache']: self.settings['cache']['topiccount'] = {} self.settings['cache']['topiccount']['size'] = 1000 self.settings['cache']['topiccount']['seconds'] = 10 if not 'receiveEnvelope' in self.settings['cache']: self.settings['cache']['receiveEnvelope'] = {} self.settings['cache']['receiveEnvelope']['size'] = 10000 self.settings['cache']['receiveEnvelope']['seconds'] = 1000 if not 'getUsersPosts' in self.settings['cache']: self.settings['cache']['getUsersPosts'] = {} self.settings['cache']['getUsersPosts']['size'] = 10000 self.settings['cache']['getUsersPosts']['seconds'] = 2 if not 'sorttopic' in self.settings['cache']: self.settings['cache']['sorttopic'] = {} self.settings['cache']['sorttopic']['size'] = 1000000 self.settings['cache']['sorttopic']['seconds'] = 10000000000 if not 'formatText' in self.settings['cache']: self.settings['cache']['formatText'] = {} self.settings['cache']['formatText']['size'] = 1000 self.settings['cache']['formatText']['seconds'] = 10000000000 if not 'error_envelope' in self.settings['cache']: self.settings['cache']['error_envelope'] = {} self.settings['cache']['error_envelope']['size'] = 20 self.settings['cache']['error_envelope']['seconds'] = 10000000000 if not 'KeyGenerator' in self.settings: self.settings['KeyGenerator'] = {} if not 'num_pregens' in self.settings['KeyGenerator']: self.settings['KeyGenerator']['num_pregens'] = 5 if not 'workers' in self.settings['KeyGenerator']: self.settings['KeyGenerator']['workers'] = 1 if not 'keys' in self.settings: self.settings['keys'] = {} if not 'keysize' in self.settings['keys']: self.settings['keys']['keysize'] = 3072 if not 'upload-dir' in self.settings: self.settings['upload-dir'] = '../nginx/uploads' if not 'mark-origin' in self.settings: self.settings['mark-origin'] = False if not 'mark-seen' in self.settings: self.settings['mark-seen'] = False if not 'max-upload-preview-size' in self.settings: self.settings['max-upload-preview-size'] = 10485760 if not 'cookie-encryption' in self.settings: self.settings['cookie-encryption'] = TavernUtils.randstr(255) if not 'serverkey-password' in self.settings: self.settings['serverkey-password'] = TavernUtils.randstr(255) if not 'embedserver' in self.settings: self.settings['embedserver'] = 'http://embed.is' if not 'maxembeddedurls' in self.settings: self.settings['maxembeddedurls'] = 10 if not 'mongo-connections' in self.settings: self.settings['mongo-connections'] = 10 if not 'dbtype' in self.settings: self.settings['dbtype'] = 'mongo' if not 'proof-of-work-difficulty' in self.settings: self.settings['proof-of-work-difficulty'] = 19 # Report back on if there were any changes. if str(self.settings) == tmpsettings: return False else: return True
def __init__(self, settingsfile=None, workingdir=None, slot='default'): super().__init__(slot) # Don't run this more than once. if self.__dict__.get('set') is True: return else: self.set = True self.logger = logging.getLogger('Tavern') # Should the server run in debug mode self.debug = False self.serversettings = ServerSettings.ServerSettings(settingsfile) if self.serversettings.updateconfig(): self.serversettings.saveconfig() if not 'pubkey' in self.serversettings.settings or not 'privkey' in self.serversettings.settings: # Generate New config self.logger.info("Generating new Config") self.ServerKeys = Key() self.ServerKeys.generate() # We can't encrypt this.. We'd need to store the key here on the machine... # If you're worried about it, encrypt the disk, or use a loopback. self.serversettings.settings['pubkey'] = self.ServerKeys.pubkey self.serversettings.settings['privkey'] = self.ServerKeys.privkey self.serversettings.updateconfig() self.serversettings.saveconfig() self.mc = OrderedDict self.unusedkeycache = multiprocessing.Queue( self.serversettings.settings['KeyGenerator']['num_pregens']) # Break out the settings into it's own file, so we can include it without including all of server # This does cause a few shenanigans while loading here, but hopefully # it's minimal self.ServerKeys = Key(pub=self.serversettings.settings['pubkey'], priv=self.serversettings.settings['privkey']) self.db = DBWrapper( name=self.serversettings.settings['dbname'], dbtype=self.serversettings.settings['dbtype'], host=self.serversettings.settings['mongo-hostname'], port=self.serversettings.settings['mongo-port']) self.sessions = DBWrapper( name=self.serversettings.settings['sessions-db-name'], dbtype=self.serversettings.settings['dbtype'], host=self.serversettings.settings['sessions-db-hostname'], port=self.serversettings.settings['sessions-db-port']) self.binaries = DBWrapper( name=self.serversettings.settings['bin-mongo-db'], dbtype='mongo', host=self.serversettings.settings['bin-mongo-hostname'], port=self.serversettings.settings['bin-mongo-port']) self.bin_GridFS = GridFS(self.binaries.unsafe.mongo) # Ensure we have Proper indexes. self.db.safe.ensure_index('envelope', 'envelope.local.time_added') self.db.safe.ensure_index('envelope', 'envelope.local.sorttopic') self.db.safe.ensure_index('envelope', 'envelope.local.payload_sha512') self.db.safe.ensure_index('envelope', 'envelope.payload.class') self.db.safe.ensure_index('envelope', 'envelope.payload.regarding') self.db.safe.ensure_index('envelope', 'envelope.payload.binaries.sha_512') self.db.safe.ensure_index('envelope', 'envelope.local.payload_sha512') self.db.safe.ensure_index('envelope', 'envelope.payload.author.pubkey') self.db.safe.ensure_index('envelope', 'envelope.payload.author.pubkey') self.db.safe.ensure_index('envelope', 'usertrusts.asking') self.db.safe.ensure_index('envelope', 'incomingtrust') self.binaries.safe.ensure_index('fs.files', 'filename') self.binaries.safe.ensure_index('fs.files', 'uploadDate') self.binaries.safe.ensure_index('fs.files', '_id') self.binaries.safe.ensure_index('fs.files', 'uploadDate') # Get a list of all the valid templates that can be used, to compare # against later on. self.availablethemes = [] for name in os.listdir('themes'): if os.path.isdir(os.path.join('themes', name)): if name[:1] != ".": self.availablethemes.append(name) self.serversettings.settings['static-revision'] = int(time.time()) self.fortune = TavernUtils.randomWords(fortunefile="data/fortunes") self.wordlist = TavernUtils.randomWords(fortunefile="data/wordlist") # Define out logging options. formatter = logging.Formatter('[%(levelname)s] %(message)s') self.consolehandler = logging.StreamHandler() self.consolehandler.setFormatter(formatter) self.handler_file = logging.FileHandler( filename=self.serversettings.settings['logfile']) self.handler_file.setFormatter(formatter) self.logger.setLevel(self.serversettings.settings['loglevel']) level = self.serversettings.settings['loglevel'] # Cache our JS, so we can include it later. file = open("static/scripts/instance.min.js") self.logger.info("Cached JS") TavernCache.cache['instance.js'] = file.read() file.close() self.guestacct = None