async def update_apiary( body: ApiaryPutModel, apiary_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Update an Apiary object """ user_id = await get_logged_in_user(access_token) logger.info("Put apiary received", user_id=user_id, payload=body, apiary_id=apiary_id) if all(v is None for v in body.dict().values()): raise HTTPException(400, detail="No argument provided") apiary = session.query(Apiaries).get(apiary_id) if apiary is None or apiary.deleted_at or apiary.user_id != user_id: raise HTTPException(status_code=404) for key, value in body.dict().items(): if value is not None: setattr(apiary, key, value) try: session.commit() except Exception as exc: logger.exception("Something went wrong when updating the apiary", apiary=apiary) raise HTTPException(status_code=400, detail="Database error") from exc logger.info("Put apiary successfull", apiary_id=apiary_id, user_id=user_id)
async def delete_apiary( apiary_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Delete apiary """ user_id = await get_logged_in_user(access_token) logger.info("Delete apiary received", user_id=user_id, apiary_id=apiary_id) apiary = session.query(Apiaries).get(apiary_id) if apiary is None or apiary.deleted_at or apiary.user_id != user_id: raise HTTPException(status_code=404) if apiary.nb_hives > 0: raise HTTPException(status_code=400, detail="Hives are linked to this apiary") try: apiary.deleted_at = datetime.datetime.utcnow() session.commit() except Exception as exc: logger.exception("Something went wrong when deleting the apiary", apiary=apiary) raise HTTPException(status_code=400, detail="Database error") from exc logger.info("Delete apiary successful", apiary_id=apiary_id, user_id=user_id)
def desimpersonate(access_token: str = Cookie(None), session: Session = Depends(get_session)): if not access_token: raise HTTPException(status_code=401, detail="Missing authorization header") data = helpers.validate_jwt(access_token) if data is None or "impersonator_id" not in data: raise HTTPException(status_code=401) impersonator = (session.query(Users).options(joinedload( Users.permissions)).get(data["impersonator_id"])) if impersonator is None: logger.warning("Could not find impersonator. This is odd") raise HTTPException(status_code=400) logger.info( f"Desimpersonating from {impersonator.email}", impersonator_id=str(impersonator.id), ) data = { "username": data["impersonator_username"], "email": data["impersonator_email"], "admin": impersonator.permissions is not None, } return { "access_token": helpers.generate_jwt(user_id=impersonator.id, extra_data=data) }
async def delete_hive( hive_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Delete hive """ user_id = await get_logged_in_user(access_token) logger.info("Delete hive received", user_id=user_id, hive_id=hive_id) hive = session.query(Hives).get(hive_id) if hive is None or hive.deleted_at or hive.user_id != user_id: raise HTTPException(status_code=404) try: now = datetime.datetime.now() hive.deleted_at = now hive.apiary_id = None # TODO: how to manage swarm when hive is being deleted # if hive.swarm: # hive.swarm.deleted_at = now session.commit() except Exception as exc: logger.exception("Something went wrong when deleting the hive", hive=hive) raise HTTPException(status_code=400, detail="Database error") from exc logger.info("Delete hive successfull", user_id=user_id, hive_id=hive_id)
def reset_user_password_request( data: PasswordResetRequestModel, session: Session = Depends(get_session), language: str = Cookie(None), ): user_credentials = (session.query(Credentials).join(Users).options( joinedload(Credentials.user)).filter( (Credentials.username == data.username) | (Users.email == data.username)).one_or_none()) if user_credentials is None: raise HTTPException(status_code=404) reset_id = uuid.uuid4() if not helpers.send_event_user_password_reset( user=user_credentials.user, reset_id=reset_id, language=language): raise HTTPException( status_code=409, detail="Password reset requests cannot be handled at the moment", ) user_credentials.reset_id = reset_id session.commit() logger.info( f"User password reset request successful: {user_credentials.user.email}", user_id=user_credentials.user.id, user_email=user_credentials.user.email, ) return {"reset_id": str(reset_id)}
async def post_hive( data: HivePostModel, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Create an Hive object and return it as json """ user_id = await get_logged_in_user(access_token) logger.info("Post hive received", user_id=user_id, payload=data) hive = Hives( name=data.name, condition_id=data.condition_id, owner_id=data.owner_id, swarm_id=data.swarm_id, apiary_id=data.apiary_id, user_id=user_id, ) session.add(hive) try: session.commit() except Exception as exc: logger.exception("Database error", hive=hive) raise HTTPException( status_code=400, detail="Couldn't save the hive in database") from exc logger.info("Post hive successfull", user_id=user_id, hive_id=hive.id) return hive
async def delete_swarm( swarm_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Delete a swarm """ user_id = await get_logged_in_user(access_token) logger.info("Delete swarm received", user_id=user_id, swarm_id=swarm_id) swarm = session.query(Swarms).get(swarm_id) if swarm is None or swarm.deleted_at: raise HTTPException(status_code=404, detail="Swarm not found") if swarm.user_id != user_id: raise HTTPException(status_code=403) try: swarm.deleted_at = datetime.datetime.now() swarm.hive = None session.commit() except Exception as exc: logger.exception("Database error", swarm=swarm) raise HTTPException( status_code=400, detail="Couldn't delete the swarm in database") from exc logger.info("Delete swarm successfull", user_id=user_id, swarm_id=swarm_id)
async def post_swarm( data: SwarmPostModel, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Create an Swarm object and return it as json """ user_id = await get_logged_in_user(access_token) logger.info("Post swarm received", user_id=user_id, payload=data) swarm = Swarms( health_status_id=data.health_status_id, queen_year=data.queen_year, user_id=user_id, ) session.add(swarm) try: session.commit() except Exception as exc: logger.exception("Database error", swarm=swarm) raise HTTPException( status_code=400, detail="Couldn't save the swarm in database") from exc logger.info("Post swarm successfull", user_id=user_id, swarm_id=swarm.id) return swarm
async def put_swarm( data: SwarmPutModel, swarm_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Modify a swarm """ user_id = await get_logged_in_user(access_token) logger.info("Put swarm received", user_id=user_id, swarm_id=swarm_id) swarm = session.query(Swarms).get(swarm_id) if swarm is None or swarm.deleted_at or swarm.user_id != user_id: raise HTTPException(status_code=404, detail="Swarm not found") if not (data.health_status_id or data.queen_year): raise HTTPException(status_code=400, detail="No data provided") swarm.health_status_id = data.health_status_id or swarm.health_status_id swarm.queen_year = data.queen_year or swarm.queen_year try: session.commit() except Exception as exc: logger.exception("Database error", swarm=swarm) raise HTTPException( status_code=400, detail="Couldn't update the swarm in database") from exc logger.info("Put swarm successfull", user_id=user_id, swarm_id=swarm_id)
async def post_apiary( data: ApiaryPostModel, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Create an Apiary object and return it as json """ user_id = await get_logged_in_user(access_token) logger.info("Post apiary received", user_id=user_id, payload=data) apiary = Apiaries( name=data.name, location=data.location, honey_type_id=data.honey_type, user_id=user_id, ) session.add(apiary) try: session.commit() except Exception as exc: logger.exception("Database error", apiary=apiary) raise HTTPException( status_code=400, detail="Couldn't save the apiary in database") from exc logger.info("Post apiary successfull", apiary_id=apiary.id, user_id=user_id) return apiary
async def create_setup_data( body: SetupDataPostModel, data_type: SetupDataType, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Create a setup object and return it as json """ user_id = await get_logged_in_user(access_token) logger.info("Create setup data received", user_id=user_id, data_type=data_type, payload=body) model = MAPPING[data_type] obj = model(name=body.value, user_id=user_id) session.add(obj) try: session.commit() except Exception as exc: logger.exception("Something went wrong when saving the object") raise HTTPException(status_code=400, detail="Database error") from exc session.refresh(obj) logger.info( "Create setup data successfull", user_id=user_id, data_type=data_type, obj_id=obj.id, ) return obj
def send(self, message): sender = message.email[SENDER] if sender not in self.open_connections: self.login(sender) client = self.open_connections[sender] client.send_message(message.email) logger.info(f"Email sent to {message.email[TO]}", email=message.email)
async def get_swarms(access_token: str = Cookie(None), session: Session = Depends(get_session)): """ Get a list of swarms """ user_id = await get_logged_in_user(access_token) swarms = (session.query(Swarms).filter(Swarms.user_id == user_id, Swarms.deleted_at.is_(None)).all()) logger.info("Get list of swarms successfull", user_id=user_id, nb_swarms=len(swarms)) return swarms
async def get_hives(access_token: str = Cookie(None), session: Session = Depends(get_session)): """ Get a list of hives """ user_id = await get_logged_in_user(access_token) hives = (session.query(Hives).filter(Hives.user_id == user_id, Hives.deleted_at.is_(None)).all()) logger.info("Get list of hives successfull", user_id=user_id, nb_hives=len(hives)) return hives
def initialize_user(properties, body): body = json.loads(body) user_id = body.get("user", {}).get("id") language = body.get("language") if user_id is None: logger.error( "Incorrect message received: missing 'user.id' key", body=body, properties=properties, ) return False if language is None: logger.error( "Incorrect message received: missing 'language' key", body=body, properties=properties, ) return False if language not in LANGUAGES: logger.error(f"Incorrect language: {body['languages']}") return False objects = [ HiveConditions(name=item[language], user_id=user_id) for item in DEFAULT_HIVE_CONDITIONS ] objects.extend( HoneyTypes(name=item[language], user_id=user_id) for item in DEFAULT_HONEY_TYPES) objects.extend( SwarmHealthStatuses(name=item[language], user_id=user_id) for item in DEFAULT_SWARM_HEALTH_STATUSES) objects.extend( EventTypes(name=item[language], user_id=user_id) for item in DEFAULT_EVENT_TYPES) objects.extend( EventStatuses(name=item[language], user_id=user_id) for item in DEFAULT_EVENT_STATUSES) db_client = db() with db_client as session: session.bulk_save_objects(objects) session.commit() logger.info("Default data created for user", user_id=user_id) return True
def close(self, emails=None): if emails is None: emails = list(self.open_connections.keys()) elif isinstance(emails, str): emails = [emails] for email in emails: connection = self.open_connections.get(email) if connection is None: logger.info(f"No opened connection for {email}") continue connection.close() logger.info(f"Closed connection for {email}") del self.open_connections[email]
async def get_apiaries( access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Create an Apiary object and return it as json """ user_id = await get_logged_in_user(access_token) apiaries = (session.query(Apiaries).filter( Apiaries.user_id == user_id, Apiaries.deleted_at.is_(None)).all()) logger.info("Get list of apiaries sucessfull", user_id=user_id, nb_apiaries=len(apiaries)) return apiaries
async def get_hive( hive_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Get a single hive """ user_id = await get_logged_in_user(access_token) hive = session.query(Hives).get(hive_id) if hive is None or hive.user_id != user_id or hive.deleted_at: raise HTTPException(status_code=404) logger.info("Get hive successfull", hive_id=hive_id) return hive
def send_email(payload, template, **kwargs): language = payload["language"] receiver = payload["user"]["email"] sender = CONFIG.NOREPLY_EMAIL subject = template[language]["subject"] body = template[language]["body"].format(**kwargs) email = Email(sender=sender, receiver=receiver, subject=subject, body=body) client = EmailEngine() client.send(email) logger.info(f"Email '{subject}' sent to {receiver} !", payload=payload, subject=subject) client.close()
async def get_swarm( swarm_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Get a swarm """ user_id = await get_logged_in_user(access_token) swarm = session.query(Swarms).get(swarm_id) if swarm is None or swarm.deleted_at or swarm.user_id != user_id: raise HTTPException(status_code=404, detail="Swarm not found") logger.info("Get swarm successfull", user_id=user_id, swarm_id=swarm_id) return swarm
def login(self, email_address): if email_address in self.open_connections: return password_var_name = "{name}_EMAIL_PASSWORD".format( name=email_address.split("@")[0].upper()) password = CONFIG.get(password_var_name) if password is None: logger.error( f"Trying to connect to an unknown email: {email_address}") raise ValueError(f"{email_address} is unknown") client = self.get_email_client() client.login(email_address, password) logger.info(f"Connected to {email_address}") self.open_connections[email_address] = client
def create_user( user_data: UserModel, session: Session = Depends(get_session), language: str = Cookie(None), ): # validate data if language not in ("fr", "en"): language = "fr" if not helpers.validate_email(user_data.email): raise HTTPException(400, "Invalid email address") if not helpers.validate_password(user_data.password): raise HTTPException(400, "Invalid password") if session.query(exists().where(Users.email == user_data.email)).scalar(): raise HTTPException(400, "Email already exists") if session.query(exists().where( Credentials.username == user_data.username)).scalar(): raise HTTPException(400, "Username already exists") # create all objects user = Users(email=user_data.email) credentials = Credentials( user=user, username=user_data.username, password=helpers.get_password_hash(user_data.password), ) owner = Owners(user=user, name=user_data.username) try: session.add_all((user, credentials, owner)) session.commit() logger.info(f"User created: {user.email}", user_id=user.id, user_email=user.email) except Exception as exc: logger.exception(f"Could not create user {user_data.email}") raise HTTPException(400, "Database error when creating the user") from exc if not helpers.send_event_user_created(user=user, language=language): logger.error("User created but could not publish the rabbitmq message", user_id=user.id) return user
def impersonate_user( user_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): user = session.query(Users).get(user_id) if user is None: raise HTTPException(status_code=404) if not access_token: raise HTTPException(status_code=401, detail="Missing authorization header") data = helpers.validate_jwt(access_token) if data is None: raise HTTPException(status_code=401) impersonator = (session.query(Users).options(joinedload( Users.permissions)).get(data["user_id"])) if impersonator is None: raise HTTPException(status_code=401) if impersonator.permissions is None or not impersonator.permissions.impersonate: raise HTTPException(status_code=403) logger.info( f"Impersonating user: {user.email} (by {impersonator.email})", user_id=str(user.id), impersonator_id=str(impersonator.id), ) data = { "username": f"{user.email} ({data['username']})", "email": user.email, "admin": user.permissions is not None, "impersonator_id": str(impersonator.id), "impersonator_email": data["email"], "impersonator_username": data["username"], } return { "access_token": helpers.generate_jwt(user_id=user_id, extra_data=data) }
async def move_hive( hive_id: uuid.UUID, apiary_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Move hive to a new apiary """ user_id = await get_logged_in_user(access_token) logger.info("Move hive received", user_id=user_id, hive_id=hive_id, new_apiary_id=apiary_id) hive = session.query(Hives).get(hive_id) if hive is None or hive.user_id != user_id or hive.deleted_at: raise HTTPException(status_code=404, detail="Hive not found") apiary = session.query(Apiaries).get(apiary_id) if apiary is None or apiary.user_id != user_id or apiary.deleted_at: raise HTTPException(status_code=404, detail="Apiary not found") try: hive.apiary = apiary session.commit() except Exception as exc: logger.exception( "Something went wrong while moving a hive to a new apiary", hive=hive, apiary=apiary, ) raise HTTPException(status_code=400, detail="Database error") from exc logger.info( "Apiary successfully moved", user_id=user_id, new_apiary_id=apiary_id, hive_id=hive_id, )
async def get_setup_data( data_type: SetupDataType, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Get a setup object and return it as json """ user_id = await get_logged_in_user(access_token) model = MAPPING[data_type] objects = (session.query(model).filter(model.user_id == user_id, model.deleted_at.is_(None)).all()) logger.info( "Get setup object successfull", user_id=user_id, nb_object=len(objects), data_type=data_type, ) return objects
def reset_user_password(properties, payload): logger.info( "Received message to reset a user password !", properties=properties, payload=payload, ) try: payload = json.loads(payload) except: logger.exception("Message is not in JSON format") return False required_keys = ("user", "language", "reset_link") if any(key not in payload for key in required_keys): logger.error("Incorrect message received - some keys are missing", payload=payload) return False send_email(payload, RESET_PASSWORD_EMAIL, reset_link=payload["reset_link"]) return True
async def delete_setup_data( data_type: SetupDataType, obj_id: uuid.UUID, access_token: str = Cookie(None), session: Session = Depends(get_session), ): """ Update a setup object and return it as json """ user_id = await get_logged_in_user(access_token) logger.info( "Delete setup data received", user_id=user_id, data_type=data_type, obj_id=obj_id, ) model = MAPPING[data_type] obj = session.query(model).get(obj_id) if obj is None: raise HTTPException(status_code=404) if obj.user_id != user_id: raise HTTPException(status_code=403) try: obj.deleted_at = datetime.datetime.utcnow() session.commit() except Exception as exc: logger.exception("Something went wrong when saving the object") raise HTTPException(status_code=400, detail="Database error") from exc logger.info( "Delete setup data successfull", user_id=user_id, data_type=data_type, obj_id=obj_id, )
def consume(self): while True: logger.info("Starting consuming...", queue=self.queue) try: with self.connection_manager as channel: self.setup(channel) channel.basic_consume(queue=self.queue, on_message_callback=self.process) channel.start_consuming() except KeyboardInterrupt: logger.info("Goodbye") self.connection_manager.close() return except pika.exceptions.StreamLostError: logger.warning( "RBMQ connection stream has been lost. Trying to reconnect" ) continue except: logger.exception("Critical error on consumer") self.connection_manager.close() raise
def authenticate_user(authorization: str = Header(None), session: Session = Depends(get_session)): if not authorization: raise HTTPException(status_code=401, detail="Missing authorization header") credentials = helpers.parse_authorization_header(authorization) if credentials is None: raise HTTPException(status_code=401, detail="Could not parse access_token") user_credentials = (session.query(Credentials).join(Users).filter( or_( Credentials.username == credentials.username, Users.email == credentials.username, )).one_or_none()) if user_credentials is None or user_credentials.password != credentials.password: raise HTTPException(status_code=401, detail="Wrong credentials") user_id = str(user_credentials.user_id) user_credentials.last_seen = datetime.datetime.utcnow() session.commit() logger.info( f"User logged in: {credentials.username}", user_id=user_id, username=user_credentials.username, ) data = { "username": user_credentials.username, "email": user_credentials.user.email, "admin": user_credentials.user.permissions is not None, } return { "access_token": helpers.generate_jwt(user_id=user_id, extra_data=data) }
def activate_user( data: ActivateModel, session: Session = Depends(get_session), ): user = session.query(Users).get(data.user_id) if user is None: raise HTTPException(status_code=404) if user.activated: return JSONResponse(content="Already activated", status_code=200) if user.activation_id != data.activation_id: raise HTTPException(status_code=400) user.activation_id = None user.activated = True session.commit() logger.info(f"User activated: {user.email}", user_id=user.id, user_email=user.email)