from datetime import datetime, timedelta from celery import chain from celery.exceptions import TaskError from celery.signals import task_postrun from atlas_um import consts from atlas_um.accounts_validation.services import ( NonActiveAccountsNotificationsService, ) from atlas_um.auth0.services import ( SyncAuth0Account, SyncAuth0AccountExternalStates, ) from atlas_um.dna_accounts.services import ( SyncDNAAccountWithAuth0Service, SendDNAInvitationService, ImportDNAAccountFromAuth0Service, SendDNAAccessExpirationNotificationService, SyncDNAAccountExternalStates, ) from atlas_um.dna_accounts.services import ( SyncDNAAccountWithUSM, SyncInternalUserDomainsWithUSM, ) from atlas_um.extensions import celery from atlas_um.extensions import redis from atlas_um.logs import logger from atlas_um.pgdb import DNAAccount, ResourceGroup from atlas_um.pgdb import pgdb from atlas_um.settings import Settings from atlas_um.tokens.bearer_tokens import DNABearerToken @task_postrun.connect def close_session(*args, **kwargs): # Flask SQLAlchemy will automatically create new sessions from # a scoped session factory, given that we are maintaining the same app # context, this ensures tasks have a fresh session (e.g. session errors # won't propagate across tasks) if not celery.conf.get("task_always_eager"): pgdb.session.remove() @celery.task def sync_dna_account_with_usm(dna_account_id): dna_account = DNAAccount.query.get(dna_account_id) if dna_account is None: logger.bind(dna_account_id=dna_account_id).error( "Account doesnt exist for id" ) return SyncDNAAccountWithUSM.execute(dna_account=dna_account) @celery.task def import_auth0_users_by_product( product, user_ids=None, batch_from=None, batch_to=None ): try: ImportDNAAccountFromAuth0Service.import_all_by_product( product, user_ids, batch_from, batch_to ) except Exception as e: logger.bind(error=e).exception("Auth0 users import error") raise TaskError(f"Service logic exception: {e}") @celery.task def send_dna_invitation( dna_account_id, admin_dna_account_id, resource_group_id=None ): dna_account = DNAAccount.query.get(dna_account_id) if dna_account is None: logger.bind(dna_account_id=dna_account_id).error( "Account doesnt exist for id" ) return resource_group = ( ResourceGroup.query.get(resource_group_id) if resource_group_id else None ) admin_dna_account = ( DNAAccount.query.get(admin_dna_account_id) or dna_account ) if admin_dna_account is None: logger.bind(admin_dna_account_id=admin_dna_account_id).error( "Account doesnt exist for id" ) return SendDNAInvitationService.execute( dna_account=dna_account, admin_dna_account=admin_dna_account, resource_group=resource_group, ) @celery.task def sync_internal_domains_with_usm(): SyncInternalUserDomainsWithUSM.execute() @celery.task def task_orchestrate_update_token_length_for_dna_accounts(): for dna_account in DNAAccount.query.all(): task_update_token_length_for_dna_account.delay(dna_account.id) @celery.task def task_update_token_length_for_dna_account(dna_account_id): dna_account = DNAAccount.query.get(dna_account_id) token = DNABearerToken(dna_account) dna_account.token_length = token.token_length pgdb.session.add(dna_account) pgdb.session.commit() logger.bind(dna_account_id=dna_account_id).info( "Token length updated for account" ) @celery.task def task_orchestrate_send_access_expiration_notifications(): qs = DNAAccount.query.filter( DNAAccount.expiration_date != None, # noqa DNAAccount.expiration_date <= datetime.now().date() + timedelta(days=Settings.ACCOUNTS_EXPIRATION_NOTIFY_DAYS), DNAAccount.expiration_date > datetime.now().date(), ) for dna_account in qs.all(): key = ( f"{consts.ACCOUNTS_EXPIRATION_NOTIFY_REDIS_KEY}-" f"{dna_account.expiration_date.strftime('%y_%m_%d')}-" f"{dna_account.id}" ) result = redis.get(key) if not result: task_send_access_expiration_notification_by_dna_account.delay( dna_account.id ) @celery.task def task_send_access_expiration_notification_by_dna_account(dna_account_id): dna_account = DNAAccount.query.get(dna_account_id) SendDNAAccessExpirationNotificationService.execute(dna_account=dna_account) @celery.task def sync_dna_account_with_auth0(dna_account_id): dna_account = DNAAccount.query.get(dna_account_id) if dna_account is None: logger.bind(dna_account_id=dna_account_id).error( "Account doesnt exist for id" ) return SyncDNAAccountWithAuth0Service.execute(dna_account=dna_account) @celery.task(autoretry_for=(Exception,), retry_kwargs={"max_retries": 2}) def sync_auth0_accounts_task(*args, **kwargs): try: SyncAuth0Account.sync_all() except Exception as e: logger.bind(error=e).exception("Sync Auth0 accounts error") raise TaskError(f"Service logic exception: {e}") @celery.task(autoretry_for=(Exception,), retry_kwargs={"max_retries": 2}) def sync_auth0_accounts_external_state_task(*args, **kwargs): try: SyncAuth0AccountExternalStates.sync_all() except Exception as e: logger.bind(error=e).exception( "Sync Auth0 accounts external states error" ) raise TaskError(f"Service logic exception: {e}") @celery.task(autoretry_for=(Exception,), retry_kwargs={"max_retries": 2}) def sync_dna_accounts_external_state_task(*args, **kwargs): try: SyncDNAAccountExternalStates.sync_all() except Exception as e: logger.bind(error=e).exception( "Sync DNA accounts external states error" ) raise TaskError(f"Service logic exception: {e}") @celery.task def send_accounts_state_validation_notification_task(*args, **kwargs): NonActiveAccountsNotificationsService.execute() @celery.task def accounts_validation_task(*args, **kwargs): chain( sync_auth0_accounts_task.s(), sync_auth0_accounts_external_state_task.s(), sync_dna_accounts_external_state_task.s(), send_accounts_state_validation_notification_task.s(), ).delay()