diff --git a/docs/Security-Compliance/File-Transfer-TDRS/README.md b/docs/Security-Compliance/File-Transfer-TDRS/README.md deleted file mode 100644 index 0333257bed..0000000000 --- a/docs/Security-Compliance/File-Transfer-TDRS/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Boundary diagram with file transfer from TDP to TDRS - - -The [TDP boundary diagram](../boundary-diagram.md) has been _temporarily_ modified herein to reflect updates to the data flow. - - - - -### Updated Data flow - -Data files from grantees will, for the most part, follow the original flow: -- users with `OFA Admin` and (STT) `Data Analyst` roles will upload and submit files via the web application -- upon submission, files will be scanned for viruses via ClamAV. Infected files will be discarded, and clean files will be stored in cloud.gov AWS S3 buckets. - -#### _What's new?_ -Files stored in cloud.gov's encrypted AWS S3 buckets will be transferred via SFTP to the ACFTitan server, which lives within the legacy system's (TDRS) ATO boundary diagram, as shown below. A more complete visual of the TDRS architecture and ATO boundary can be found [here](https://hhsgov.sharepoint.com/sites/TANFDataPortalOFA-TestPrivateChannel/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2FTANFDataPortalOFA%2DTestPrivateChannel%2FShared%20Documents%2FTest%20Private%20Channel%2FExamples%2FTDRS%2FBoundary%20Diagram%2FTANF%20Network%20Diagram%2EJPG&parent=%2Fsites%2FTANFDataPortalOFA%2DTestPrivateChannel%2FShared%20Documents%2FTest%20Private%20Channel%2FExamples%2FTDRS%2FBoundary%20Diagram) :lock:. - -These files will be picked up by the ACF OCIO Ops team, who maintain TDRS, for data processing and dB storage. This transfer process is temporary until TDP reaches parity with TDRS in terms of data processing, validation, and dB storage. More background on TDRS functionality can be found [here](../../Background/Current-TDRS.md) - -![Boundary diagram](diagram.png) - -## Updating - -- Download latest version pdf diagram [draw.io](diagram.drawio) -- Edit this diagram with [draw.io](https://app.diagrams.net/) -- Update the image and point download link to correct file diff --git a/docs/Security-Compliance/File-Transfer-TDRS/diagram.drawio b/docs/Security-Compliance/File-Transfer-TDRS/diagram.drawio deleted file mode 100644 index 2c22f92ddd..0000000000 --- a/docs/Security-Compliance/File-Transfer-TDRS/diagram.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/docs/Security-Compliance/File-Transfer-TDRS/diagram.png b/docs/Security-Compliance/File-Transfer-TDRS/diagram.png deleted file mode 100644 index 476b82d338..0000000000 Binary files a/docs/Security-Compliance/File-Transfer-TDRS/diagram.png and /dev/null differ diff --git a/docs/Technical-Documentation/secret-key-rotation-steps.md b/docs/Technical-Documentation/secret-key-rotation-steps.md index bb10ad8808..b6f58c39ee 100644 --- a/docs/Technical-Documentation/secret-key-rotation-steps.md +++ b/docs/Technical-Documentation/secret-key-rotation-steps.md @@ -6,7 +6,6 @@ To maintain good security, we will periodically rotate the following secret keys - CF deployer keys (_for continuous delivery_) - JWT keys (_external user auth_) - ACF AMS keys (_internal user auth_) -- ACF Titan server keys (_for file transfers between TDP and TDRS_) - Django secret keys ([_cryptographic signing_](https://docs.djangoproject.com/en/4.0/topics/signing/#module-django.core.signing)) This document outlines the process for doing this for each set of keys. @@ -154,61 +153,6 @@ Service requests tickets must be submitted by Government-authorized personnel wi 2. Update environment variables in CircleCI and relevant cloud.gov backend applications after ticket completed by OCIO. [Restage applications](https://cloud.gov/docs/deployment/app-maintenance/#restaging-your-app). -**
ACF Titan Server Keys** -The ACF OCIO Ops team manages these credentials for all environments (dev, staging, and prod), so we will need to submit a service request ticket whenever we need keys rotated. - -Service requests tickets must be submitted by Government-authorized personnel with Government computers and PIV access (e.g. Raft tech lead for lower environments and TDP sys admins for production environment). Please follow the procedures below: - -1. Generate new public/private key pair - -Below is an example of how to generate new titan public/private key pair from _Git BASH for Windows_. Two files called `filename_where_newtitan_keypair_saved` are created: one is the _private_ key and the other is a _public_ key (the latter is saved with a _.pub_ extention). -(note: the info below is not associated with any real keys) - -``` -$ ssh-keygen -t rsa -b 4096 -Generating public/private rsa key pair. - -Enter file in which to save the key (/c/Users/username/.ssh/id_rsa): filename_where_newtitan_keypair_saved - -Enter passphrase (empty for no passphrase): - -Enter same passphrase again: - -Your identification has been saved in filename_where_newtitan_keypair_saved - -Your public key has been saved in filename_where_newtitan_keypair_saved.pub - -The key fingerprint is: -SHA256:BY6Nl0hCjIrI9yZMBGH2vbDFLCTq2DsFQXQTmLydwjI - -The key's randomart image is: -+---[RSA 4096]----+ -| X*B*.. . | -|+ O+=+ * o | -|=oo* *+ = . | -|Eo++B .. . | -|.+=oo. S | -| = o | -| o o | -| . | -| | -+----[SHA256]-----+ -``` - -2. Submit request tickets from government-issued email address and use the email template located on **page 2** of [this document.](https://hhsgov.sharepoint.com/:w:/r/sites/TANFDataPortalOFA/Shared%20Documents/compliance/Authentication%20%26%20Authorization/ACF%20AMS%20docs/OCIO%20OPERATIONS%20REQUEST%20TEMPLATES.docx?d=w5332585c1ecf49a4aeda17674f687154&csf=1&web=1&e=aQyIPz) cc OFA tech lead on lower environment requests. - -The request should include: -- the titan service account name (i.e. `tanfdp` for prod; `tanfdpdev` for dev/staging) -- the newly generated public key from `filename_where_newtitan_keypair_saved.pub` - -3. When OCIO confirms that the change has been made, add the private key from `filename_where_newtitan_keypair_saved` to CircleCI as an environment variable. The variable name is `ACFTITAN_KEY`. **Please note**: the value needs must be edited before adding to CircleCI. It should be a one-line string with underscores ("_") replacing the spaces at the end of every line. See example below: - -``` ------BEGIN OPENSSH PRIVATE KEY-----_somehashvalue_-----END OPENSSH PRIVATE KEY----- -``` - -4. Re-run the deployment workflow from CircleCI and confirm that the updated key value pair has been added to the relevant cloud.gov backend application. -
**
Django secret keys** diff --git a/scripts/deploy-backend.sh b/scripts/deploy-backend.sh index 3f53b6b594..24bef90d92 100755 --- a/scripts/deploy-backend.sh +++ b/scripts/deploy-backend.sh @@ -42,9 +42,6 @@ echo backend_app_name: "$backend_app_name" set_cf_envs() { var_list=( - "ACFTITAN_HOST" - "ACFTITAN_KEY" - "ACFTITAN_USERNAME" "AMS_CLIENT_ID" "AMS_CLIENT_SECRET" "AMS_CONFIGURATION_ENDPOINT" diff --git a/tdrs-backend/.env.example b/tdrs-backend/.env.example index af513c9290..5ffe271c1a 100644 --- a/tdrs-backend/.env.example +++ b/tdrs-backend/.env.example @@ -86,6 +86,3 @@ ELASTIC_HOST=elastic:9200 # testing CYPRESS_TOKEN=local-cypress-token - -# sftp -ACFTITAN_SFTP_PYTEST=local-acftitan-key diff --git a/tdrs-backend/Pipfile b/tdrs-backend/Pipfile index 7ab59800e1..117e86c75b 100644 --- a/tdrs-backend/Pipfile +++ b/tdrs-backend/Pipfile @@ -51,8 +51,6 @@ celery = "==5.2.6" redis = "==4.1.2" flower = "==1.1.0" django-celery-beat = "==2.2.1" -paramiko = "==2.11.0" -pytest_sftpserver = "==1.3.0" elasticsearch = "==7.13.4" # REQUIRED - v7.14.0 introduces breaking changes django-elasticsearch-dsl = "==7.3" django-elasticsearch-dsl-drf = "==0.22.5" diff --git a/tdrs-backend/Pipfile.lock b/tdrs-backend/Pipfile.lock index d62cb708b4..c2cdbfdc03 100644 --- a/tdrs-backend/Pipfile.lock +++ b/tdrs-backend/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e0b5d173936dd0ae24d434e543a96d139eed6ab962f1f92b1fe354d74d9a0599" + "sha256": "a082fb8d3118128843dec21e83b70a4ee5d9743a2e869918452d1b8c47533edc" }, "pipfile-spec": 6, "requires": { @@ -39,39 +39,6 @@ ], "version": "==2.4.1" }, - "bcrypt": { - "hashes": [ - "sha256:01746eb2c4299dd0ae1670234bf77704f581dd72cc180f444bfe74eb80495b64", - "sha256:037c5bf7c196a63dcce75545c8874610c600809d5d82c305dd327cd4969995bf", - "sha256:094fd31e08c2b102a14880ee5b3d09913ecf334cd604af27e1013c76831f7b05", - "sha256:0d4cf6ef1525f79255ef048b3489602868c47aea61f375377f0d00514fe4a78c", - "sha256:193bb49eeeb9c1e2db9ba65d09dc6384edd5608d9d672b4125e9320af9153a15", - "sha256:2505b54afb074627111b5a8dc9b6ae69d0f01fea65c2fcaea403448c503d3991", - "sha256:2ee15dd749f5952fe3f0430d0ff6b74082e159c50332a1413d51b5689cf06623", - "sha256:31adb9cbb8737a581a843e13df22ffb7c84638342de3708a98d5c986770f2834", - "sha256:3a5be252fef513363fe281bafc596c31b552cf81d04c5085bc5dac29670faa08", - "sha256:3d3b317050a9a711a5c7214bf04e28333cf528e0ed0ec9a4e55ba628d0f07c1a", - "sha256:48429c83292b57bf4af6ab75809f8f4daf52aa5d480632e53707805cc1ce9b74", - "sha256:4a8bea4c152b91fd8319fef4c6a790da5c07840421c2b785084989bf8bbb7455", - "sha256:4fb253d65da30d9269e0a6f4b0de32bd657a0208a6f4e43d3e645774fb5457f3", - "sha256:551b320396e1d05e49cc18dd77d970accd52b322441628aca04801bbd1d52a73", - "sha256:5f7cd3399fbc4ec290378b541b0cf3d4398e4737a65d0f938c7c0f9d5e686611", - "sha256:6004f5229b50f8493c49232b8e75726b568535fd300e5039e255d919fc3a07f2", - "sha256:6717543d2c110a155e6821ce5670c1f512f602eabb77dba95717ca76af79867d", - "sha256:6cac78a8d42f9d120b3987f82252bdbeb7e6e900a5e1ba37f6be6fe4e3848286", - "sha256:8a893d192dfb7c8e883c4576813bf18bb9d59e2cfd88b68b725990f033f1b978", - "sha256:8cbb119267068c2581ae38790e0d1fbae65d0725247a930fc9900c285d95725d", - "sha256:9f8ea645eb94fb6e7bea0cf4ba121c07a3a182ac52876493870033141aa687bc", - "sha256:c4c8d9b3e97209dd7111bf726e79f638ad9224b4691d1c7cfefa571a09b1b2d6", - "sha256:cb9c707c10bddaf9e5ba7cdb769f3e889e60b7d4fea22834b261f51ca2b89fed", - "sha256:d84702adb8f2798d813b17d8187d27076cca3cd52fe3686bb07a9083930ce650", - "sha256:ec3c2e1ca3e5c4b9edb94290b356d082b721f3f50758bce7cce11d8a7c89ce84", - "sha256:f44a97780677e7ac0ca393bd7982b19dbbd8d7228c1afe10b128fd9550eef5f1", - "sha256:f5698ce5292a4e4b9e5861f7e53b1d89242ad39d54c3da451a93cac17b61921a" - ], - "markers": "python_version >= '3.7'", - "version": "==4.1.3" - }, "billiard": { "hashes": [ "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547", @@ -694,14 +661,6 @@ "markers": "python_version >= '3.7'", "version": "==24.0" }, - "paramiko": { - "hashes": [ - "sha256:003e6bee7c034c21fbb051bf83dc0a9ee4106204dd3c53054c71452cc4ec3938", - "sha256:655f25dc8baf763277b933dfcea101d636581df8d6b9774d1fb653426b72c270" - ], - "index": "pypi", - "version": "==2.11.0" - }, "parso": { "hashes": [ "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", @@ -868,36 +827,11 @@ "markers": "python_version >= '3.6'", "version": "==2.4.0" }, - "pynacl": { - "hashes": [ - "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", - "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", - "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", - "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", - "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", - "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", - "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", - "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", - "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", - "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543" - ], - "markers": "python_version >= '3.6'", - "version": "==1.5.0" - }, - "pytest-sftpserver": { - "hashes": [ - "sha256:b7ac34a23f63d77e27f67b6a81c9418243733f027eeb8a3061d965b2da7e5cab", - "sha256:c5e8a37049866d4eabc711db9f1c09e1c02ab72ba290f5fd244939c9a188042f" - ], - "index": "pypi", - "version": "==1.3.0" - }, "python-crontab": { "hashes": [ - "sha256:6d5ba3c190ec76e4d252989a1644fcb233dbf53fbc8fceeb9febe1657b9fb1d4", - "sha256:79fb7465039ddfd4fb93d072d6ee0d45c1ac8bf1597f0686ea14fd4361dba379" + "sha256:f4ea1605d24533b67fa7a634ef26cb59a5f2e7954f6e677d2d7a2229959a2fc8" ], - "version": "==3.0.0" + "version": "==3.1.0" }, "python-dateutil": { "hashes": [ @@ -1040,11 +974,11 @@ }, "setuptools": { "hashes": [ - "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987", - "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32" + "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4", + "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0" ], "markers": "python_version >= '3.8'", - "version": "==69.5.1" + "version": "==70.0.0" }, "six": { "hashes": [ @@ -1381,11 +1315,11 @@ }, "faker": { "hashes": [ - "sha256:6737cc6d591cd83421fdc5e494f6e2c1a6e32266214f158b745aa9fa15687c98", - "sha256:c153505618801f1704807b258a6010ea8cabf91f66f4788939bfdba83b887e76" + "sha256:45b84f47ff1ef86e3d1a8d11583ca871ecf6730fad0660edadc02576583a2423", + "sha256:cfe97c4857c4c36ee32ea4aaabef884895992e209bae4cbd26807cf3e05c6918" ], "markers": "python_version >= '3.8'", - "version": "==25.0.1" + "version": "==25.2.0" }, "flake8": { "hashes": [ diff --git a/tdrs-backend/docker-compose.local.yml b/tdrs-backend/docker-compose.local.yml index d2cd5289c6..2de355c9c4 100644 --- a/tdrs-backend/docker-compose.local.yml +++ b/tdrs-backend/docker-compose.local.yml @@ -68,12 +68,8 @@ services: - AMS_CLIENT_ID - AMS_CLIENT_SECRET - AMS_CONFIGURATION_ENDPOINT - - ACFTITAN_HOST - - ACFTITAN_KEY - - ACFTITAN_USERNAME - REDIS_URI=redis://redis-server:6379 - REDIS_SERVER_LOCAL=TRUE - - ACFTITAN_SFTP_PYTEST - SENDGRID_API_KEY volumes: - .:/tdpapp diff --git a/tdrs-backend/docker-compose.yml b/tdrs-backend/docker-compose.yml index dba1be5deb..07d014fb51 100644 --- a/tdrs-backend/docker-compose.yml +++ b/tdrs-backend/docker-compose.yml @@ -91,12 +91,8 @@ services: - AMS_CLIENT_ID - AMS_CLIENT_SECRET - AMS_CONFIGURATION_ENDPOINT - - ACFTITAN_HOST - - ACFTITAN_KEY - - ACFTITAN_USERNAME - REDIS_URI=redis://redis-server:6379 - REDIS_SERVER_LOCAL=TRUE - - ACFTITAN_SFTP_PYTEST - CYPRESS_TOKEN - DJANGO_DEBUG - SENDGRID_API_KEY diff --git a/tdrs-backend/tdpservice/data_files/views.py b/tdrs-backend/tdpservice/data_files/views.py index dfcc71416b..3f67d7cb3f 100644 --- a/tdrs-backend/tdpservice/data_files/views.py +++ b/tdrs-backend/tdpservice/data_files/views.py @@ -18,7 +18,7 @@ from tdpservice.data_files.util import get_xls_serialized_file from tdpservice.data_files.models import DataFile, get_s3_upload_path from tdpservice.users.permissions import DataFilePermissions, IsApprovedPermission -from tdpservice.scheduling import sftp_task, parser_task +from tdpservice.scheduling import parser_task from tdpservice.data_files.s3_client import S3Client from tdpservice.parsers.models import ParserError from tdpservice.parsers.serializers import ParsingErrorSerializer @@ -59,7 +59,6 @@ def create(self, request, *args, **kwargs): # only if file is passed the virus scan and created successfully will we perform side-effects: # * Send to parsing - # * Upload to ACF-TITAN # * Send email to user logger.debug(f"{self.__class__.__name__}: status: {response.status_code}") @@ -74,15 +73,6 @@ def create(self, request, *args, **kwargs): parser_task.parse.delay(data_file_id) logger.info("Submitted parse task to queue for datafile %s.", data_file_id) - sftp_task.upload.delay( - data_file_pk=data_file_id, - server_address=settings.ACFTITAN_SERVER_ADDRESS, - local_key=settings.ACFTITAN_LOCAL_KEY, - username=settings.ACFTITAN_USERNAME, - port=22 - ) - logger.info("Submitted upload task to redis for datafile %s.", data_file_id) - app_name = settings.APP_NAME + '/' key = app_name + get_s3_upload_path(data_file, '') version_id = self.get_s3_versioning_id(response.data.get('original_filename'), key) diff --git a/tdrs-backend/tdpservice/scheduling/sftp_task.py b/tdrs-backend/tdpservice/scheduling/sftp_task.py deleted file mode 100644 index d4807ac885..0000000000 --- a/tdrs-backend/tdpservice/scheduling/sftp_task.py +++ /dev/null @@ -1,135 +0,0 @@ -"""schedule tasks.""" - -from __future__ import absolute_import - -# The tasks - -import hashlib -import os - -from celery import shared_task -from django.conf import settings -import datetime -import paramiko -import logging -from tdpservice.data_files.models import DataFile, LegacyFileTransfer - -logger = logging.getLogger(__name__) - - -@shared_task(acks_late=True, worker_prefetch_multiplier=1) -def upload( - data_file_pk, - server_address=settings.ACFTITAN_SERVER_ADDRESS, - local_key=settings.ACFTITAN_LOCAL_KEY, - username=settings.ACFTITAN_USERNAME, - port=22, -): - """ - Upload to SFTP server. - - This task uploads the file in DataFile object with pk = data_file_pk - to sftp server as defined in Settings file - """ - # Upload file - data_file = DataFile.objects.get(id=data_file_pk) - file_transfer_record = LegacyFileTransfer( - data_file=data_file, - uploaded_by=data_file.user, - file_name=data_file.filename if data_file.filename is not None else "None", - ) - - def write_key_to_file(private_key): - """Paramiko require the key in file object format.""" - with open("temp_key_file", "w") as f: - f.write(private_key) - f.close() - return "temp_key_file" - - def create_dir(directory_name, sftp_server): - """Code snippet to create directory in SFTP server.""" - try: - sftp_server.chdir(directory_name) # Test if remote_path exists - except IOError: - sftp_server.mkdir(directory_name) # Create remote_path - sftp_server.chdir(directory_name) - - for attempt in range(1, 4): - logger.info("Attempt {} to upload file {}".format(attempt, data_file.filename)) - try: - # Create directory names for ACF titan - destination = str(data_file.filename) - today_date = datetime.datetime.today() - upper_directory_name = today_date.strftime("%Y%m%d") - lower_directory_name = today_date.strftime( - str(data_file.year) + "-" + str(data_file.quarter) - ) - - # Paramiko need local file - paramiko_local_file = data_file.file.read() - with open(destination, "wb") as f1: - f1.write(paramiko_local_file) - file_transfer_record.file_size = f1.tell() - file_transfer_record.file_shasum = hashlib.sha256( - paramiko_local_file - ).hexdigest() - f1.close() - - # Paramiko SSH connection requires private key as file - temp_key_file = write_key_to_file(local_key) - os.chmod(temp_key_file, 0o600) - - # Create SFTP/SSH connection - transport = paramiko.SSHClient() - transport.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - pkey = paramiko.RSAKey.from_private_key_file(temp_key_file) - transport.connect( - server_address, - pkey=pkey, - username=username, - port=port, - look_for_keys=False, - banner_timeout=30, - disabled_algorithms={"pubkeys": ["rsa-sha2-512", "rsa-sha2-256"]}, - ) - # remove temp key file - os.remove(temp_key_file) - sftp = transport.open_sftp() - - # Create remote directory - create_dir(settings.ACFTITAN_DIRECTORY, sftp_server=sftp) - create_dir(upper_directory_name, sftp_server=sftp) - create_dir(lower_directory_name, sftp_server=sftp) - - # Put the file in SFTP server - sftp.put(destination, destination) - - # Delete temp file - os.remove(destination) - logger.info( - "File {} has been successfully uploaded to {}".format( - destination, server_address - ) - ) - - # Add the log LegacyFileTransfer - file_transfer_record.result = LegacyFileTransfer.Result.COMPLETED - file_transfer_record.save() - transport.close() - return True - - except Exception as e: - logger.error( - "Attempt {} failed to upload {} with error:{}".format( - attempt, destination, e - ) - ) - transport.close() - else: - # All attempts failed - logger.error("Failed to upload {} after 3 attempts".format(destination)) - file_transfer_record.file_size = 0 - file_transfer_record.result = LegacyFileTransfer.Result.ERROR - file_transfer_record.save() - transport.close() - return False diff --git a/tdrs-backend/tdpservice/scheduling/test/test_file_upload.py b/tdrs-backend/tdpservice/scheduling/test/test_file_upload.py deleted file mode 100644 index 140ec6d643..0000000000 --- a/tdrs-backend/tdpservice/scheduling/test/test_file_upload.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Scheduling tests.""" - -from datetime import datetime - -import pytest -from paramiko import Transport -from paramiko.sftp_client import SFTPClient -from tdpservice.scheduling.sftp_task import upload -from tdpservice.data_files.test.factories import DataFileFactory -from tdpservice.stts.models import STT -from django.conf import settings - -""" -To mock sftp server, pytest_sftpserver (https://github.com/ulope/pytest-sftpserver) is used. -The package provides two main fixtures for testing: sftpserver and sftpclient. -""" - -@pytest.fixture -def stt_instance(region): - """Return an STT.""" - stt, _ = STT.objects.get_or_create( - name="first", - region=region, - postal_code="AR", - stt_code='234', - filenames={ - 'Aggregate Data': 'ADS.E2J.NDM3.TS22', - 'Active Case Data': 'test', - 'Closed Case Data': 'ADS.E2J.NDM2.TS22'} - ) - return stt - -@pytest.fixture -def data_file_instance(stt_instance): - """Prepare data file fixture instance for testing datafile.""" - return DataFileFactory.create( - created_at=datetime.now(), - stt=stt_instance - ) - - -@pytest.fixture -def sftp_connection_values(sftpserver): - """SFTP connection values for local sftp server.""" - server_address = sftpserver.host - local_key = settings.ACFTITAN_SFTP_PYTEST - username = "user" - port = sftpserver.port - return { - 'server_address': server_address, - 'username': username, - 'local_key': local_key, - 'port': port - } - - -@pytest.fixture(scope="session") -def sftpclient(sftpserver): - """SFTP client for local sftp server.""" - transport = Transport((sftpserver.host, sftpserver.port)) - transport.connect(username="a", password="b") - sftpclient = SFTPClient.from_transport(transport) - yield sftpclient - sftpclient.close() - transport.close() - - -@pytest.mark.django_db -def test_new_data_file(sftpserver, data_file_instance, sftp_connection_values, sftpclient): - """Datafile object for testing the file.""" - data_file_instance.save() - - """ - Need .serve_content to keep the communication alive - Here we put a dummy file somefile.txt in a_dir to keep the port open - """ - with sftpserver.serve_content({'a_dir': {'somefile.txt': "File content"}}): - upload(data_file_instance.pk, - server_address=sftp_connection_values['server_address'], - local_key=sftp_connection_values['local_key'], - username=sftp_connection_values['username'], - port=sftp_connection_values['port']) - - # Create directory structure as needed for ACF_TITAN to assert correct directory name - today_date = datetime.today() - upper_directory_name = today_date.strftime('%Y%m%d') - lower_directory_name = today_date.strftime(str(data_file_instance.year) + - '-' + - str(data_file_instance.quarter)) - assert sftpclient.listdir(upper_directory_name+'/'+lower_directory_name)[0] == \ - data_file_instance.filename diff --git a/tdrs-backend/tdpservice/settings/common.py b/tdrs-backend/tdpservice/settings/common.py index 369cb5a32d..525ac33cff 100644 --- a/tdrs-backend/tdpservice/settings/common.py +++ b/tdrs-backend/tdpservice/settings/common.py @@ -442,17 +442,6 @@ class Common(Configuration): '' ) - # ------- SFTP CONFIG - ACFTITAN_SERVER_ADDRESS = os.getenv('ACFTITAN_HOST', '') - """ - To be able to fit the PRIVATE KEY in one line as environment variable, we replace the EOL - with an underscore char. - The next line replaces the _ with EOL before using the PRIVATE KEY - """ - ACFTITAN_LOCAL_KEY = os.getenv('ACFTITAN_KEY', '').replace('_', '\n') - ACFTITAN_USERNAME = os.getenv('ACFTITAN_USERNAME', '') - ACFTITAN_DIRECTORY = os.getenv('ACFTITAN_DIRECTORY', '') - # -------- CELERY CONFIG REDIS_URI = os.getenv( 'REDIS_URI', diff --git a/tdrs-backend/tdpservice/settings/local.py b/tdrs-backend/tdpservice/settings/local.py index ae22cbcab3..171608fe54 100644 --- a/tdrs-backend/tdpservice/settings/local.py +++ b/tdrs-backend/tdpservice/settings/local.py @@ -43,12 +43,3 @@ class Local(Common): } REDIS_SERVER_LOCAL = bool(strtobool(os.getenv("REDIS_SERVER_LOCAL", "TRUE"))) - - # SFTP TEST KEY - """ - To be able to fit the PRIVATE KEY in one line as environment variable, we replace the EOL - with an underscore char. - The next line replaces the _ with EOL before using the PRIVATE KEY - """ - ACFTITAN_SFTP_PYTEST = os.getenv("ACFTITAN_SFTP_PYTEST").replace('_', '\n') - APP_NAME = "tdrs-backend-local"