async def submit(self, submission): """Save a user submission in the submissions collection.""" submission_time = utils.now() if submission_time < self.start or submission_time >= self.end: raise errors.SurveyDoesNotAcceptSubmissionsAtTheMomentError() if not self.validator.validate(submission): raise errors.InvalidSubmissionError() submission = { 'submission_time': submission_time, 'data': submission, } if self.authentication == 'open': await self.submissions.insert_one(submission) if self.authentication == 'email': submission['_id'] = verification.token() while True: try: await self.submissions.insert_one(submission) break except pymongo.errors.DuplicateKeyError: submission['_id'] = verification.token() status = await email.send_submission_verification( submission['data'][str(self.ei + 1)], self.username, self.survey_name, self.configuration['title'], submission['_id'], ) if status != 200: raise errors.InternalServerError()
async def update(self, username, account_data): """Update existing user account data in the database.""" # TODO handle username change with transactions # TODO handle email change specially, as it needs to be reverified if not self.validator.validate(account_data): raise errors.InvalidAccountDataError() entry = await database.database['accounts'].find_one( filter={'_id': username}, projection={ '_id': False, 'email_address': True, 'password_hash': True, }, ) if entry is None: raise errors.UserNotFoundError() update = {} if account_data['username'] != username: raise errors.NotImplementedError() if account_data['email_address'] != entry['email_address']: raise errors.NotImplementedError() if not pw.verify(account_data['password'], entry['password_hash']): update['password_hash'] = pw.hash(account_data['password']) if update: update['modification_time'] = utils.now() result = await database.database['accounts'].update_one( filter={'_id': username}, update={'$set': update}, ) if result.matched_count == 0: raise errors.UserNotFoundError()
def min_max_for_all(): """Logic for sending min/max mail""" config = get_config() for recipient in config['recipients']: for transaction in recipient['transactions']: print('\n' + '-' * 50 + '\n') sv = StockValue(symbol=transaction['symbol'], config=config) bankier = sv.get_bankier() current_value, bankier_time = sv.get_values(bankier) hm = HistoryManager() global_min = hm.get_min(transaction['symbol'], recipient['address']) global_max = hm.get_max(transaction['symbol'], recipient['address']) calculator = Calculator(transaction['buy_quantity'], transaction['buy_price'], transaction['buy_quantity'], current_value) if transaction['min_max_mail'] == 'yes': prepare_min_max_email(recipient['address'], transaction['symbol'], current_value, global_min, global_max, config, calculator) hm.update_history(current_value, transaction['symbol'], recipient['address'], bankier_time, now())
async def create(self, username, account_data): """Create new user account with some default account data.""" if username != account_data['username']: raise errors.InvalidAccountDataError() if not self.validator.validate(account_data): raise errors.InvalidAccountDataError() timestamp = utils.now() account_data = { '_id': username, 'email_address': account_data['email_address'], 'password_hash': pw.hash(account_data['password']), 'superuser': False, 'creation_time': timestamp, 'modification_time': timestamp, 'verified': False, 'verification_token': verification.token(), } while True: try: await database.database['accounts'].insert_one(account_data) break except pymongo.errors.DuplicateKeyError as error: index = str(error).split()[7] if index == '_id_': raise errors.UsernameAlreadyTakenError() if index == 'email_address_index': raise errors.EmailAddressAlreadyTakenError() if index == 'verification_token_index': account_data['verification_token'] = verification.token() else: raise errors.InternalServerError() status = await email.send_account_verification( account_data['email_address'], username, account_data['verification_token'], ) if status != 200: # we do not delete the unverified account here, as the user could # request a new verification email, and the account gets deleted # anyways after a few minutes raise errors.InternalServerError()
async def verify(self, verification_token): """Verify the user's email address and save submission as verified.""" verification_time = utils.now() if self.authentication != 'email': raise errors.InvalidVerificationTokenError() if verification_time < self.start or verification_time >= self.end: raise errors.SurveyDoesNotAcceptSubmissionsAtTheMomentError() submission = await self.submissions.find_one( {'_id': verification_token}, ) if submission is None: raise errors.InvalidVerificationTokenError() submission['verification_time'] = verification_time submission['_id'] = submission['data'][str(self.ei + 1)] await self.verified_submissions.find_one_and_replace( filter={'_id': submission['_id']}, replacement=submission, upsert=True, ) return fastapi.responses.RedirectResponse( f'{settings.FRONTEND_URL}/{self.username}/{self.survey_name}' f'/success')
def generate(username): """Generate JWT access token containing username and expiration. Note that the backend returns the access_token in the form {'access_token': 'tomato', 'token_type': 'bearer'} but expects only the actual 'tomato' value for any routes that require authorization. """ timestamp = utils.now() payload = { 'iss': 'FastSurvey', 'sub': username, 'iat': timestamp, 'exp': timestamp + 7 * 24 * 60 * 60, # tokens are valid for 7 days } access_token = jwt.encode( payload, settings.PRIVATE_RSA_KEY, algorithm='RS256', ) access_token = access_token.decode('utf-8') return {'access_token': access_token, 'token_type': 'bearer'}
async def aggregate(self): """Query the survey submissions and return aggregated results.""" if utils.now() < self.end: raise errors.NotImplementedError() self.results = self.results or await self.aggregator.fetch() return self.results
'COMMIT_SHA', 'BRANCH_NAME', ] for env in _ENVS: assert os.getenv(env), f'environment variable {env} not set' # development / production / test environment ENVIRONMENT = os.getenv('ENVIRONMENT') # frontend url FRONTEND_URL = os.getenv('FRONTEND_URL') # console url CONSOLE_URL = os.getenv('CONSOLE_URL') # backend url BACKEND_URL = os.getenv('BACKEND_URL') # public JSON Web Token signature key PUBLIC_RSA_KEY = base64.b64decode(os.getenv('PUBLIC_RSA_KEY')) # private JSON Web Token signature key PRIVATE_RSA_KEY = base64.b64decode(os.getenv('PRIVATE_RSA_KEY')) # MongoDB connection string MONGODB_CONNECTION_STRING = os.getenv('MONGODB_CONNECTION_STRING') # Mailgun api key MAILGUN_API_KEY = os.getenv('MAILGUN_API_KEY') # git commit hash COMMIT_SHA = os.getenv('COMMIT_SHA') # git branch name BRANCH_NAME = os.getenv('BRANCH_NAME') # timestamp of when the server was started START_TIME = utils.now() # sender email address SENDER = 'FastSurvey <*****@*****.**>'