def add_story(db: DatabaseHandler, story: dict, feeds_id: int, skip_checking_if_new: bool = False) -> Optional[dict]: """If the story is new, add story to the database with the feed of the download as story feed. Returns created story or None if story wasn't created. """ story = decode_object_from_bytes_if_needed(story) if isinstance(feeds_id, bytes): feeds_id = decode_object_from_bytes_if_needed(feeds_id) feeds_id = int(feeds_id) if isinstance(skip_checking_if_new, bytes): skip_checking_if_new = decode_object_from_bytes_if_needed(skip_checking_if_new) skip_checking_if_new = bool(int(skip_checking_if_new)) if db.in_transaction(): raise McAddStoryException("add_story() can't be run from within transaction.") db.begin() db.query("LOCK TABLE stories IN ROW EXCLUSIVE MODE") if not skip_checking_if_new: if not is_new(db=db, story=story): log.debug("Story '{}' is not new.".format(story['url'])) db.commit() return None medium = db.find_by_id(table='media', object_id=story['media_id']) if story.get('full_text_rss', None) is None: story['full_text_rss'] = medium.get('full_text_rss', False) or False if len(story.get('description', '')) == 0: story['full_text_rss'] = False try: story = db.create(table='stories', insert_hash=story) except Exception as ex: db.rollback() # FIXME get rid of this, replace with native upsert on "stories_guid" unique constraint if 'unique constraint \"stories_guid' in str(ex): log.warning( "Failed to add story for '{}' to GUID conflict (guid = '{}')".format(story['url'], story['guid']) ) return None else: raise McAddStoryException("Error adding story: {}\nStory: {}".format(str(ex), str(story))) db.find_or_create( table='feeds_stories_map', insert_hash={ 'stories_id': story['stories_id'], 'feeds_id': feeds_id, } ) db.commit() return story
def regenerate_api_key(db: DatabaseHandler, email: str) -> None: """Regenerate API key -- creates new non-IP limited API key, removes all IP-limited API keys.""" email = decode_object_from_bytes_if_needed(email) if not email: raise McAuthProfileException('Email address is empty.') # Check if user exists try: user = user_info(db=db, email=email) except Exception as _: raise McAuthProfileException( "User with email address '%s' does not exist." % email) db.begin() # Purge all IP-limited API keys db.query( """ DELETE FROM auth_user_api_keys WHERE ip_address IS NOT NULL AND auth_users_id = ( SELECT auth_users_id FROM auth_users WHERE email = %(email)s ) """, {'email': email}) # Regenerate non-IP limited API key db.query( """ UPDATE auth_user_api_keys -- DEFAULT points to a generation function SET api_key = DEFAULT WHERE ip_address IS NULL AND auth_users_id = ( SELECT auth_users_id FROM auth_users WHERE email = %(email)s ) """, {'email': email}) message = AuthAPIKeyResetMessage(to=email, full_name=user.full_name()) if not send_email(message): db.rollback() raise McAuthProfileException( "Unable to send email about reset API key.") db.commit()
def regenerate_api_key(db: DatabaseHandler, email: str) -> None: """Regenerate API key -- creates new non-IP limited API key, removes all IP-limited API keys.""" email = decode_object_from_bytes_if_needed(email) if not email: raise McAuthProfileException('Email address is empty.') # Check if user exists try: user = user_info(db=db, email=email) except Exception: raise McAuthProfileException( "User with email address '%s' does not exist." % email) db.begin() # Purge all API keys db.query( """ DELETE FROM auth_user_api_keys WHERE auth_users_id = %(auth_users_id)s """, {'auth_users_id': user.user_id()}) # Regenerate non-IP limited API key db.query( """ INSERT INTO auth_user_api_keys ( auth_users_id, api_key, ip_address ) VALUES ( %(auth_users_id)s, -- DEFAULT points to a generation function DEFAULT, NULL ) """, {'auth_users_id': user.user_id()}) message = AuthAPIKeyResetMessage(to=email, full_name=user.full_name()) if not send_email(message): db.rollback() raise McAuthProfileException( "Unable to send email about reset API key.") db.commit()
def regenerate_api_key(db: DatabaseHandler, email: str) -> None: """Regenerate API key -- creates new non-IP limited API key, removes all IP-limited API keys.""" email = decode_object_from_bytes_if_needed(email) if not email: raise McAuthProfileException('Email address is empty.') # Check if user exists try: user = user_info(db=db, email=email) except Exception: raise McAuthProfileException("User with email address '%s' does not exist." % email) db.begin() # Purge all IP-limited API keys db.query(""" DELETE FROM auth_user_api_keys WHERE ip_address IS NOT NULL AND auth_users_id = ( SELECT auth_users_id FROM auth_users WHERE email = %(email)s ) """, {'email': email}) # Regenerate non-IP limited API key db.query(""" UPDATE auth_user_api_keys -- DEFAULT points to a generation function SET api_key = DEFAULT WHERE ip_address IS NULL AND auth_users_id = ( SELECT auth_users_id FROM auth_users WHERE email = %(email)s ) """, {'email': email}) message = AuthAPIKeyResetMessage(to=email, full_name=user.full_name()) if not send_email(message): db.rollback() raise McAuthProfileException("Unable to send email about reset API key.") db.commit()
def activate_user_via_token(db: DatabaseHandler, email: str, activation_token: str) -> None: """Change password with a password token sent by email.""" email = decode_object_from_bytes_if_needed(email) activation_token = decode_object_from_bytes_if_needed(activation_token) if not email: raise McAuthRegisterException("Email is empty.") if not activation_token: raise McAuthRegisterException('Password reset token is empty.') # Validate the token once more (was pre-validated in controller) if not password_reset_token_is_valid( db=db, email=email, password_reset_token=activation_token): raise McAuthRegisterException('Activation token is invalid.') db.begin() # Set the password hash db.query( """ UPDATE auth_users SET active = TRUE WHERE email = %(email)s """, {'email': email}) # Unset the password reset token db.query( """ UPDATE auth_users SET password_reset_token_hash = NULL WHERE email = %(email)s """, {'email': email}) user = user_info(db=db, email=email) message = AuthActivatedMessage(to=email, full_name=user.full_name()) if not send_email(message): db.rollback() raise McAuthRegisterException( "Unable to send email about an activated user.") db.commit()
def activate_user_via_token(db: DatabaseHandler, email: str, activation_token: str) -> None: """Change password with a password token sent by email.""" email = decode_object_from_bytes_if_needed(email) activation_token = decode_object_from_bytes_if_needed(activation_token) if not email: raise McAuthRegisterException("Email is empty.") if not activation_token: raise McAuthRegisterException('Password reset token is empty.') # Validate the token once more (was pre-validated in controller) if not password_reset_token_is_valid(db=db, email=email, password_reset_token=activation_token): raise McAuthRegisterException('Activation token is invalid.') db.begin() # Set the password hash db.query(""" UPDATE auth_users SET active = TRUE WHERE email = %(email)s """, {'email': email}) # Unset the password reset token db.query(""" UPDATE auth_users SET password_reset_token_hash = NULL WHERE email = %(email)s """, {'email': email}) user = user_info(db=db, email=email) message = AuthActivatedMessage(to=email, full_name=user.full_name()) if not send_email(message): db.rollback() raise McAuthRegisterException("Unable to send email about an activated user.") db.commit()
def update_user(db: DatabaseHandler, user_updates: ModifyUser) -> None: """Update an existing user.""" if not user_updates: raise McAuthProfileException("Existing user is undefined.") # Check if user exists try: user = user_info(db=db, email=user_updates.email()) except Exception as _: raise McAuthProfileException( 'User with email address "%s" does not exist.' % user_updates.email()) db.begin() if user_updates.full_name() is not None: db.query( """ UPDATE auth_users SET full_name = %(full_name)s WHERE email = %(email)s """, { 'full_name': user_updates.full_name(), 'email': user_updates.email(), }) if user_updates.notes() is not None: db.query( """ UPDATE auth_users SET notes = %(notes)s WHERE email = %(email)s """, { 'notes': user_updates.notes(), 'email': user_updates.email(), }) if user_updates.active() is not None: db.query( """ UPDATE auth_users SET active = %(active)s WHERE email = %(email)s """, { 'active': bool(int(user_updates.active())), 'email': user_updates.email(), }) if user_updates.password() is not None: try: change_password( db=db, email=user_updates.email(), new_password=user_updates.password(), new_password_repeat=user_updates.password_repeat(), do_not_inform_via_email=True, ) except Exception as ex: db.rollback() raise McAuthProfileException("Unable to change password: %s" % str(ex)) if user_updates.weekly_requests_limit() is not None: db.query( """ UPDATE auth_user_limits SET weekly_requests_limit = %(weekly_requests_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'weekly_requests_limit': user_updates.weekly_requests_limit(), 'auth_users_id': user.user_id(), }) if user_updates.weekly_requested_items_limit() is not None: db.query( """ UPDATE auth_user_limits SET weekly_requested_items_limit = %(weekly_requested_items_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'weekly_requested_items_limit': user_updates.weekly_requested_items_limit(), 'auth_users_id': user.user_id(), }) if user_updates.role_ids() is not None: db.query( """ DELETE FROM auth_users_roles_map WHERE auth_users_id = %(auth_users_id)s """, {'auth_users_id': user.user_id()}) for auth_roles_id in user_updates.role_ids(): db.insert(table='auth_users_roles_map', insert_hash={ 'auth_users_id': user.user_id(), 'auth_roles_id': auth_roles_id, }) db.commit()
def add_story(db: DatabaseHandler, story: dict, feeds_id: int, skip_checking_if_new: bool = False) -> Optional[dict]: """If the story is new, add story to the database with the feed of the download as story feed. Returns created story or None if story wasn't created. """ story = decode_object_from_bytes_if_needed(story) if isinstance(feeds_id, bytes): feeds_id = decode_object_from_bytes_if_needed(feeds_id) feeds_id = int(feeds_id) if isinstance(skip_checking_if_new, bytes): skip_checking_if_new = decode_object_from_bytes_if_needed( skip_checking_if_new) skip_checking_if_new = bool(int(skip_checking_if_new)) if db.in_transaction(): raise McAddStoryException( "add_story() can't be run from within transaction.") db.begin() db.query("LOCK TABLE stories IN ROW EXCLUSIVE MODE") if not skip_checking_if_new: if not is_new(db=db, story=story): log.debug("Story '{}' is not new.".format(story['url'])) db.commit() return None medium = db.find_by_id(table='media', object_id=story['media_id']) if story.get('full_text_rss', None) is None: story['full_text_rss'] = medium.get('full_text_rss', False) or False if len(story.get('description', '')) == 0: story['full_text_rss'] = False try: story = db.create(table='stories', insert_hash=story) except Exception as ex: db.rollback() # FIXME get rid of this, replace with native upsert on "stories_guid" unique constraint if 'unique constraint \"stories_guid' in str(ex): log.warning( "Failed to add story for '{}' to GUID conflict (guid = '{}')". format(story['url'], story['guid'])) return None else: raise McAddStoryException( "Error adding story: {}\nStory: {}".format( str(ex), str(story))) db.find_or_create(table='feeds_stories_map', insert_hash={ 'stories_id': story['stories_id'], 'feeds_id': feeds_id, }) db.commit() return story
def update_user(db: DatabaseHandler, user_updates: ModifyUser) -> None: """Update an existing user.""" if not user_updates: raise McAuthProfileException("Existing user is undefined.") # Check if user exists try: user = user_info(db=db, email=user_updates.email()) except Exception: raise McAuthProfileException('User with email address "%s" does not exist.' % user_updates.email()) db.begin() if user_updates.full_name() is not None: db.query(""" UPDATE auth_users SET full_name = %(full_name)s WHERE email = %(email)s """, { 'full_name': user_updates.full_name(), 'email': user_updates.email(), }) if user_updates.notes() is not None: db.query(""" UPDATE auth_users SET notes = %(notes)s WHERE email = %(email)s """, { 'notes': user_updates.notes(), 'email': user_updates.email(), }) if user_updates.active() is not None: db.query(""" UPDATE auth_users SET active = %(active)s WHERE email = %(email)s """, { 'active': bool(int(user_updates.active())), 'email': user_updates.email(), }) if user_updates.password() is not None: try: change_password( db=db, email=user_updates.email(), new_password=user_updates.password(), new_password_repeat=user_updates.password_repeat(), do_not_inform_via_email=True, ) except Exception as ex: db.rollback() raise McAuthProfileException("Unable to change password: %s" % str(ex)) if user_updates.weekly_requests_limit() is not None: db.query(""" UPDATE auth_user_limits SET weekly_requests_limit = %(weekly_requests_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'weekly_requests_limit': user_updates.weekly_requests_limit(), 'auth_users_id': user.user_id(), }) if user_updates.weekly_requested_items_limit() is not None: db.query(""" UPDATE auth_user_limits SET weekly_requested_items_limit = %(weekly_requested_items_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'weekly_requested_items_limit': user_updates.weekly_requested_items_limit(), 'auth_users_id': user.user_id(), }) if user_updates.role_ids() is not None: db.query(""" DELETE FROM auth_users_roles_map WHERE auth_users_id = %(auth_users_id)s """, {'auth_users_id': user.user_id()}) for auth_roles_id in user_updates.role_ids(): db.insert(table='auth_users_roles_map', insert_hash={ 'auth_users_id': user.user_id(), 'auth_roles_id': auth_roles_id, }) db.commit()
def add_user(db: DatabaseHandler, new_user: NewUser) -> None: """Add new user.""" if not new_user: raise McAuthRegisterException("New user is undefined.") # Check if user already exists user_exists = db.query( """ SELECT auth_users_id FROM auth_users WHERE email = %(email)s LIMIT 1 """, { 'email': new_user.email() }).hash() if user_exists is not None and 'auth_users_id' in user_exists: raise McAuthRegisterException("User with email '%s' already exists." % new_user.email()) # Hash + validate the password try: password_hash = generate_secure_hash(password=new_user.password()) if not password_hash: raise McAuthRegisterException("Password hash is empty.") except Exception as _: raise McAuthRegisterException('Unable to hash a new password.') db.begin() # Create the user db.create(table='auth_users', insert_hash={ 'email': new_user.email(), 'password_hash': password_hash, 'full_name': new_user.full_name(), 'notes': new_user.notes(), 'active': bool(int(new_user.active())), }) # Fetch the user's ID try: user = user_info(db=db, email=new_user.email()) except Exception as ex: db.rollback() raise McAuthRegisterException( "I've attempted to create the user but it doesn't exist: %s" % str(ex)) # Create roles try: for auth_roles_id in new_user.role_ids(): db.create(table='auth_users_roles_map', insert_hash={ 'auth_users_id': user.user_id(), 'auth_roles_id': auth_roles_id, }) except Exception as ex: raise McAuthRegisterException("Unable to create roles: %s" % str(ex)) # Update limits (if they're defined) if new_user.weekly_requests_limit() is not None: db.query( """ UPDATE auth_user_limits SET weekly_requests_limit = %(weekly_requests_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'auth_users_id': user.user_id(), 'weekly_requests_limit': new_user.weekly_requests_limit(), }) if new_user.weekly_requested_items_limit() is not None: db.query( """ UPDATE auth_user_limits SET weekly_requested_items_limit = %(weekly_requested_items_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'auth_users_id': user.user_id(), 'weekly_requested_items_limit': new_user.weekly_requested_items_limit(), }) # Subscribe to newsletter if new_user.subscribe_to_newsletter(): db.create(table='auth_users_subscribe_to_newsletter', insert_hash={'auth_users_id': user.user_id()}) if not new_user.active(): send_user_activation_token( db=db, email=new_user.email(), activation_link=new_user.activation_url(), subscribe_to_newsletter=new_user.subscribe_to_newsletter(), ) db.commit()
def add_story(db: DatabaseHandler, story: dict, feeds_id: int) -> Optional[dict]: """Return an existing dup story if it matches the url, guid, or title; otherwise, add a new story and return it. Returns found or created story. Adds an is_new = True story if the story was created by the call. """ story = decode_object_from_bytes_if_needed(story) if isinstance(feeds_id, bytes): feeds_id = decode_object_from_bytes_if_needed(feeds_id) feeds_id = int(feeds_id) if db.in_transaction(): raise McAddStoryException( "add_story() can't be run from within transaction.") db.begin() db.query("LOCK TABLE stories IN ROW EXCLUSIVE MODE") db_story = find_dup_story(db, story) if db_story: log.debug("found existing dup story: %s [%s]" % (story['title'], story['url'])) db.commit() return db_story medium = db.find_by_id(table='media', object_id=story['media_id']) if story.get('full_text_rss', None) is None: story['full_text_rss'] = medium.get('full_text_rss', False) or False if len(story.get('description', '')) == 0: story['full_text_rss'] = False try: story = db.create(table='stories', insert_hash=story) except Exception as ex: db.rollback() # FIXME get rid of this, replace with native upsert on "stories_guid" unique constraint if 'unique constraint \"stories_guid' in str(ex): log.warning( "Failed to add story for '{}' to GUID conflict (guid = '{}')". format(story['url'], story['guid'])) return None else: raise McAddStoryException( "Error adding story: {}\nStory: {}".format( str(ex), str(story))) story['is_new'] = True [insert_story_urls(db, story, u) for u in (story['url'], story['guid'])] # on conflict does not work with partitioned feeds_stories_map db.query( """ insert into feeds_stories_map_p ( feeds_id, stories_id ) select %(a)s, %(b)s where not exists ( select 1 from feeds_stories_map where feeds_id = %(a)s and stories_id = %(b)s ) """, { 'a': feeds_id, 'b': story['stories_id'] }) db.commit() log.debug("added story: %s" % story['url']) return story
def add_user(db: DatabaseHandler, new_user: NewUser) -> None: """Add new user.""" if not new_user: raise McAuthRegisterException("New user is undefined.") # Check if user already exists user_exists = db.query(""" SELECT auth_users_id FROM auth_users WHERE email = %(email)s LIMIT 1 """, {'email': new_user.email()}).hash() if user_exists is not None and 'auth_users_id' in user_exists: raise McAuthRegisterException("User with email '%s' already exists." % new_user.email()) # Hash + validate the password try: password_hash = generate_secure_hash(password=new_user.password()) if not password_hash: raise McAuthRegisterException("Password hash is empty.") except Exception as ex: log.error("Unable to hash a new password: {}".format(ex)) raise McAuthRegisterException('Unable to hash a new password.') db.begin() # Create the user db.create( table='auth_users', insert_hash={ 'email': new_user.email(), 'password_hash': password_hash, 'full_name': new_user.full_name(), 'notes': new_user.notes(), 'active': bool(int(new_user.active())), } ) # Fetch the user's ID try: user = user_info(db=db, email=new_user.email()) except Exception as ex: db.rollback() raise McAuthRegisterException("I've attempted to create the user but it doesn't exist: %s" % str(ex)) # Create roles try: for auth_roles_id in new_user.role_ids(): db.create(table='auth_users_roles_map', insert_hash={ 'auth_users_id': user.user_id(), 'auth_roles_id': auth_roles_id, }) except Exception as ex: raise McAuthRegisterException("Unable to create roles: %s" % str(ex)) # Update limits (if they're defined) if new_user.weekly_requests_limit() is not None: db.query(""" UPDATE auth_user_limits SET weekly_requests_limit = %(weekly_requests_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'auth_users_id': user.user_id(), 'weekly_requests_limit': new_user.weekly_requests_limit(), }) if new_user.weekly_requested_items_limit() is not None: db.query(""" UPDATE auth_user_limits SET weekly_requested_items_limit = %(weekly_requested_items_limit)s WHERE auth_users_id = %(auth_users_id)s """, { 'auth_users_id': user.user_id(), 'weekly_requested_items_limit': new_user.weekly_requested_items_limit(), }) # Subscribe to newsletter if new_user.subscribe_to_newsletter(): db.create(table='auth_users_subscribe_to_newsletter', insert_hash={'auth_users_id': user.user_id()}) if not new_user.active(): send_user_activation_token( db=db, email=new_user.email(), activation_link=new_user.activation_url(), subscribe_to_newsletter=new_user.subscribe_to_newsletter(), ) db.commit()