Example #1
0
async def connect_institution(
    institution: ConnectInstitution
) -> ConnectInstitutionResponse:

    client = OFXClient(
        url=institution.url,
        appid=institution.app_id,
        appver=institution.app_ver,
        language=institution.language,
        userid=institution.username,
        org=institution.fi_org,
        fid=institution.fi_id,
        version=institution.ofx_version,
        bankid=institution.bank_id,
        brokerid=institution.broker_id,
        clientuid=institution.client_uuid,
        close_elements=institution.close_elements
    )

    data = (client.request_accounts(
                password=institution.password,
                dtacctup=institution.datetime_request
            ).read()).decode('UTF-8')

    return ConnectInstitutionResponse(ofx_data=data)
Example #2
0
 def testSignonVersion102(self):
     # CLIENTUID wasn't defined until OFX version 1.0.3,
     # so it returns None if if initialized.
     client = OFXClient("http://example.com",
                        userid="porkypig",
                        clientuid="DEADBEEF",
                        version=102)
     signon = client.signon("t0ps3kr1t")
     self.assertIsInstance(signon, SIGNONMSGSRQV1)
     signon = signon.sonrq
     self.assertIsNone(signon.clientuid)
Example #3
0
def fetch(config, fileobj):
    username, password, accts = config
    client = OFXClient('https://ofx.fidelity.com/ftgw/OFX/clients/download',
                       userid=username.value,
                       org='fidelity.com',
                       fid='7776',
                       brokerid='fidelity.com')
    accts = accts.value.split(',')
    resp = client.request_statements(
        password.value, *[InvStmtRq(acctid=acct) for acct in accts])
    fileobj.write(resp.read().decode())
Example #4
0
    def testRequestStatementsPrettyprint(self):
        client = OFXClient(
            "https://example.com/ofx",
            userid="elmerfudd",
            org="FIORG",
            fid="FID",
            version=103,
            prettyprint=True,
            bankid="123456789",
            brokerid="example.com",
        )
        data = self._testRequest(client.request_statements, "t0ps3kr1t",
                                 self.stmtRq0)

        request = ("OFXHEADER:100\r\n"
                   "DATA:OFXSGML\r\n"
                   "VERSION:103\r\n"
                   "SECURITY:NONE\r\n"
                   "ENCODING:USASCII\r\n"
                   "CHARSET:NONE\r\n"
                   "COMPRESSION:NONE\r\n"
                   "OLDFILEUID:NONE\r\n"
                   "NEWFILEUID:DEADBEEF\r\n"
                   "\r\n"
                   "<OFX>\n"
                   "  <SIGNONMSGSRQV1>\n"
                   "    <SONRQ>\n"
                   "      <DTCLIENT>20170401000000.000[0:GMT]</DTCLIENT>\n"
                   "      <USERID>elmerfudd</USERID>\n"
                   "      <USERPASS>t0ps3kr1t</USERPASS>\n"
                   "      <LANGUAGE>ENG</LANGUAGE>\n"
                   "      <FI>\n"
                   "        <ORG>FIORG</ORG>\n"
                   "        <FID>FID</FID>\n"
                   "      </FI>\n"
                   "      <APPID>{appid}</APPID>\n"
                   "      <APPVER>{appver}</APPVER>\n"
                   "    </SONRQ>\n"
                   "  </SIGNONMSGSRQV1>\n"
                   "  <BANKMSGSRQV1>\n"
                   "    <STMTTRNRQ>\n"
                   "      <TRNUID>DEADBEEF</TRNUID>\n"
                   "      <STMTRQ>\n"
                   "        <BANKACCTFROM>\n"
                   "          <BANKID>123456789</BANKID>\n"
                   "          <ACCTID>111111</ACCTID>\n"
                   "          <ACCTTYPE>CHECKING</ACCTTYPE>\n"
                   "        </BANKACCTFROM>\n"
                   "        <INCTRAN>\n"
                   "          <DTSTART>20170101000000.000[0:GMT]</DTSTART>\n"
                   "          <DTEND>20170331000000.000[0:GMT]</DTEND>\n"
                   "          <INCLUDE>Y</INCLUDE>\n"
                   "        </INCTRAN>\n"
                   "      </STMTRQ>\n"
                   "    </STMTTRNRQ>\n"
                   "  </BANKMSGSRQV1>\n"
                   "</OFX>\n").format(appid=DEFAULT_APPID,
                                      appver=DEFAULT_APPVER)

        self.assertEqual(data, request)
Example #5
0
 def client(self):
     client = OFXClient(
         "https://example.com/ofx",
         userid="elmerfudd",
         org="FIORG",
         fid="FID",
         version=103,
         bankid="123456789",
         brokerid="example.com",
         persist_cookies=
         False,  # TESTME - need to test `persist_cookies`=True
     )
     # Mock out OFXClient._get_service_urls(), which hits the Internet.
     client._get_service_urls = Mock(
         return_value={StmtRq: "https://example.com/ofx"})
     return client
Example #6
0
def _scan_profile(
    url: str,
    org: Optional[str],
    fid: Optional[str],
    useragent: Optional[str],
    gen_newfileuid: Optional[bool],
    max_workers: Optional[int] = None,
    timeout: Optional[float] = None,
) -> ScanResults:
    """
    Report permutations of OFX version/prettyprint/unclosedelements that
    successfully download OFX profile from server.

    Returns a 3-tuple of (OFXv1 results, OFXv2 results, signoninfo), each
    type(dict).  OFX results provide ``ofxget`` configs that will work to
    make a basic OFX connection. SIGNONINFO reports further information
    that may be helpful to authenticate successfully.
    """
    logger.info((f"Scanning url={url} org={org} fid={fid} "
                 f"max_workers={max_workers} timeout={timeout}"))
    client = OFXClient(url, org=org, fid=fid, useragent=useragent)
    futures = _queue_scans(client, gen_newfileuid, max_workers, timeout)

    # The primary data we keep is actually the metadata (i.e. connection
    # parameters - OFX version; prettyprint; unclosedelements) tagged on
    # the Future by _queue_scans() that gave us a successful OFX connection.
    success_params: FormatMap = defaultdict(list)
    # If possible, we also parse out some data from SIGNONINFO included in
    # the PROFRS.
    signoninfo: SignoninfoReport = {}

    # Assume that SIGNONINFO is the same for each successful OFX PROFRS.
    # Tell _read_scan_response() to stop parsing out SIGNONINFO once
    # it's successfully extracted one.
    for future in concurrent.futures.as_completed(futures):
        version, format = futures[future]
        valid, signoninfo_ = _read_scan_response(future, not signoninfo)

        if not valid:
            continue
        if not signoninfo and signoninfo_:
            signoninfo = signoninfo_

        logger.debug(
            (f"OFX connection success, version={version}, format={format}"))
        success_params[version].append(format)

    v1_result, v2_result = [
        collate_scan_results(ver) for ver in utils.partition(
            lambda it: it[0] >= 200, success_params.items())
    ]

    # V2 always has closing tags for elements; just report prettyprint
    for fmt in v2_result["formats"]:
        assert not fmt["unclosedelements"]
        del fmt["unclosedelements"]

    results = (v1_result, v2_result, signoninfo)
    logger.info(f"Scan results: {results}")
    return results
Example #7
0
 def ofx_client(self):
     return OFXClient(self.ofx_endpoint,
                      userid=self.user_id,
                      org=self.org,
                      fid=self.fid,
                      version=self.version,
                      bankid=self.bank_id)
Example #8
0
 def testSignonEmptyFIORG(self):
     client = OFXClient("http://example.com", userid="porkypig")
     with patch("ofxtools.Client.OFXClient.dtclient") as mock_dtclient:
         mock_dtclient.return_value = datetime(2017, 4, 1, tzinfo=UTC)
         signon = client.signon("t0ps3kr1t")
         self.assertIsInstance(signon, SIGNONMSGSRQV1)
         signon = signon.sonrq
         self.assertIsInstance(signon, SONRQ)
         self.assertEqual(signon.dtclient, datetime(2017, 4, 1, tzinfo=UTC))
         self.assertEqual(signon.userid, "porkypig")
         self.assertEqual(signon.userpass, "t0ps3kr1t")
         self.assertEqual(signon.language, "ENG")
         self.assertIsNone(signon.fi)
         self.assertIsNone(signon.sesscookie)
         self.assertEqual(signon.appid, client.appid)
         self.assertEqual(signon.appver, client.appver)
         self.assertIsNone(signon.clientuid)
Example #9
0
 def testSignonEmptyFIORG(self):
     client = OFXClient("http://example.com", userid="porkypig")
     with patch("ofxtools.Client.OFXClient.dtclient") as mock_dtclient:
         mock_dtclient.return_value = datetime(2017, 4, 1, tzinfo=UTC)
         signon = client.signon("t0ps3kr1t")
         self.assertIsInstance(signon, SIGNONMSGSRQV1)
         signon = signon.sonrq
         self.assertIsInstance(signon, SONRQ)
         self.assertEqual(signon.dtclient, datetime(2017, 4, 1, tzinfo=UTC))
         self.assertEqual(signon.userid, "porkypig")
         self.assertEqual(signon.userpass, "t0ps3kr1t")
         self.assertEqual(signon.language, "ENG")
         self.assertIsNone(signon.fi)
         self.assertIsNone(signon.sesscookie)
         self.assertEqual(signon.appid, client.appid)
         self.assertEqual(signon.appver, client.appver)
         self.assertIsNone(signon.clientuid)
Example #10
0
 def client(self):
     return OFXClient(
         "https://example.com/ofx",
         org="FIORG",
         fid="FID",
         version=103,
         bankid="123456789",
         brokerid="example.com",
     )
Example #11
0
 def testRequestStatementsUnclosedTagsOFXv2(self):
     # OFX version 2 (XML) doesn't allow unclosed tags.
     # This is already checked by OFXClient.__init__(), so to test this
     # illegal state we have to be sneaky and change the attribute after
     # instantiation
     client = OFXClient(
         "https://example.com/ofx",
         userid="elmerfudd",
         org="FIORG",
         fid="FID",
         version=203,
         close_elements=True,
         bankid="123456789",
         brokerid="example.com",
     )
     client.close_elements = False
     with self.assertRaises(ValueError):
         self._testRequest(client.request_statements, "t0ps3kr1t",
                           self.stmtRq0)
Example #12
0
 def testUnclosedTagsOFXv2(self):
     """ OFXv2 (XML) doesn't support unclosed tags """
     with self.assertRaises(ValueError):
         OFXClient(
             "https://example.com/ofx",
             userid="elmerfudd",
             org="FIORG",
             fid="FID",
             version=203,
             close_elements=False,
             bankid="123456789",
             brokerid="example.com",
         )
Example #13
0
    def testRequestProfile(self):
        client = OFXClient(
            "https://example.com/ofx",
            org="FIORG",
            fid="FID",
            version=103,
            bankid="123456789",
            brokerid="example.com",
        )
        data = self._testRequest(client.request_profile)

        request = (
            "OFXHEADER:100\r\n"
            "DATA:OFXSGML\r\n"
            "VERSION:103\r\n"
            "SECURITY:NONE\r\n"
            "ENCODING:USASCII\r\n"
            "CHARSET:NONE\r\n"
            "COMPRESSION:NONE\r\n"
            "OLDFILEUID:NONE\r\n"
            "NEWFILEUID:DEADBEEF\r\n"
            "\r\n"
            "<OFX>"
            "<SIGNONMSGSRQV1>"
            "<SONRQ>"
            "<DTCLIENT>20170401000000.000[0:GMT]</DTCLIENT>"
            "<USERID>anonymous00000000000000000000000</USERID>"
            "<USERPASS>anonymous00000000000000000000000</USERPASS>"
            "<LANGUAGE>ENG</LANGUAGE>"
            "<FI>"
            "<ORG>FIORG</ORG>"
            "<FID>FID</FID>"
            "</FI>"
            "<APPID>{appid}</APPID>"
            "<APPVER>{appver}</APPVER>"
            "</SONRQ>"
            "</SIGNONMSGSRQV1>"
            "<PROFMSGSRQV1>"
            "<PROFTRNRQ>"
            "<TRNUID>DEADBEEF</TRNUID>"
            "<PROFRQ>"
            "<CLIENTROUTING>NONE</CLIENTROUTING>"
            "<DTPROFUP>19900101000000.000[0:GMT]</DTPROFUP>"
            "</PROFRQ>"
            "</PROFTRNRQ>"
            "</PROFMSGSRQV1>"
            "</OFX>"
        ).format(appid=DEFAULT_APPID, appver=DEFAULT_APPVER)

        self.assertEqual(data, request)
Example #14
0
    def download(self, config):
        """Setup account info and credentials."""

        client = OFXClient(config['url'],
                           config['org'],
                           config['fid'],
                           version=config['version'],
                           appid=config['appid'],
                           appver=config['appver'])

        account = [BankAcct(config['fid'], config['acctnum'], config['type'])]
        kwargs = make_date_kwargs(config)

        request = client.statement_request(config['ofxuser'],
                                           config['ofxpswd'], account,
                                           **kwargs)

        response = client.download(request)
        fname = '{}_{}.ofx'.format(config['fid'], config['acctnum'])
        with open(fname, 'w') as ofxfile:
            print(response.text, file=ofxfile)

        return fname
Example #15
0
def init_client(args: ArgsType) -> OFXClient:
    """
    Initialize OFXClient with connection info from args
    """
    client = OFXClient(
        args["url"],
        userid=args["user"] or None,
        clientuid=args["clientuid"] or None,
        org=args["org"] or None,
        fid=args["fid"] or None,
        version=args["version"],
        appid=args["appid"] or None,
        appver=args["appver"] or None,
        language=args["language"] or None,
        prettyprint=args["pretty"],
        close_elements=not args["unclosedelements"],
        bankid=args["bankid"] or None,
        brokerid=args["brokerid"] or None,
    )
    logger.debug(f"Initialized {client}")
    return client
Example #16
0
    def testRequestStatementsUnclosedTags(self):
        client = OFXClient(
            "https://example.com/ofx",
            userid="elmerfudd",
            org="FIORG",
            fid="FID",
            version=103,
            close_elements=False,
            bankid="123456789",
            brokerid="example.com",
            persist_cookies=False,
        )
        # Mock out OFXClient._get_service_urls(), which hits the Internet.
        client._get_service_urls = Mock(
            return_value={StmtRq: "https://example.com/ofx"})
        data = self._testRequest(client.request_statements, "t0ps3kr1t",
                                 self.stmtRq0)

        request = ("OFXHEADER:100\r\n"
                   "DATA:OFXSGML\r\n"
                   "VERSION:103\r\n"
                   "SECURITY:NONE\r\n"
                   "ENCODING:USASCII\r\n"
                   "CHARSET:NONE\r\n"
                   "COMPRESSION:NONE\r\n"
                   "OLDFILEUID:NONE\r\n"
                   "NEWFILEUID:DEADBEEF\r\n"
                   "\r\n"
                   "<OFX>"
                   "<SIGNONMSGSRQV1>"
                   "<SONRQ>"
                   "<DTCLIENT>20170401000000.000[0:GMT]"
                   "<USERID>elmerfudd"
                   "<USERPASS>t0ps3kr1t"
                   "<LANGUAGE>ENG"
                   "<FI>"
                   "<ORG>FIORG"
                   "<FID>FID"
                   "</FI>"
                   "<APPID>{appid}"
                   "<APPVER>{appver}"
                   "</SONRQ>"
                   "</SIGNONMSGSRQV1>"
                   "<BANKMSGSRQV1>"
                   "<STMTTRNRQ>"
                   "<TRNUID>DEADBEEF"
                   "<STMTRQ>"
                   "<BANKACCTFROM>"
                   "<BANKID>123456789"
                   "<ACCTID>111111"
                   "<ACCTTYPE>CHECKING"
                   "</BANKACCTFROM>"
                   "<INCTRAN>"
                   "<DTSTART>20170101000000.000[0:GMT]"
                   "<DTEND>20170331000000.000[0:GMT]"
                   "<INCLUDE>Y"
                   "</INCTRAN>"
                   "</STMTRQ>"
                   "</STMTTRNRQ>"
                   "</BANKMSGSRQV1>"
                   "</OFX>").format(appid=DEFAULT_APPID, appver=DEFAULT_APPVER)

        self.assertEqual(data, request)