Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- import argparse
- import getpass
- import re
- import requests
- from datetime import date, datetime
- # argparse stuff
- parser = argparse.ArgumentParser(description =
- "A CSV fetcher for PSCU-based credit cards")
- parser.add_argument('csvfile', help='the csv output file',
- metavar='output.csv')
- parser.add_argument('--start', nargs=1, metavar='date',
- help='csv start date in ISO 8601 (YYYY-MM-DD)',
- type=lambda s: datetime.strptime(s, '%Y-%m-%d'))
- parser.add_argument('--end', nargs=1, metavar='date',
- help='csv end date in ISO 8601 (YYYY-MM-DD)',
- type=lambda s: datetime.strptime(s, '%Y-%m-%d'))
- args = parser.parse_args()
- username = getpass.getpass("Username: ")
- #TODO: add wicket:interface=:NUM tracking
- # Easiest way to get a session cookie is to GET the login page
- cookie_url = ("https://apstp.pscu.com/AP/APCardholder/pages/dsologin"
- "?clientId=4356&siteFlag=true")
- # We post the username to this URL
- # the jsessionid cookie is encoded in the URL for no good reason
- auth_user_url = ("https://apstp.pscu.com/AP/APCardholder/;jsessionid="
- "<SESSIONCOOKIE>?wicket:interface=:0:Components/"
- "CorePageContainer:Templates/CorePageTemplate:Components/"
- "CorePageComponentContainer:Components/DsoLogin:login:"
- "dsoLoginButton::IActivePageBehaviorListener:0:1"
- "&wicket:ignoreIfNotActive=true")
- session_cookie_name = "JSESSIONID"
- auth_user_payload = {"usernameDecorator:dataModel.userName": username,
- "dsoLoginButton": "1"}
- # auth stage 2 is the security question
- auth2_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:1:"
- "Components/CorePageContainer:Templates/CorePageTemplate:"
- "Components/CorePageComponentContainer:Components/MfaQuestion:"
- "validateQuestion:submitButton::IActivePageBehaviorListener:0:"
- "&wicket:ignoreIfNotActive=true")
- sec_question_pattern = "<label for=\"answer\" [:=\"\w\s]+>(.+)</label>"
- sec_question_payload = {"submitButton": "1"}
- sec_question_payload_key = "answerDecorator:answer"
- # auth stage 3 is the password
- auth3_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:2:"
- "Components/CorePageContainer:Templates/CorePageTemplate:"
- "Components/CorePageComponentContainer:Components/Password:"
- "validatePassword:submitButton::IActivePageBehaviorListener:0:"
- "&wicket:ignoreIfNotActive=true")
- password_payload = {"Wicket-FocusedElementId": "passwordSubmit",
- "group": "radio0", "submitButton": "1"}
- password_payload_password_key = "passwordDecorator:password"
- # transaction page / csv stuff
- transaction_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:"
- "3:Components/CorePageContainer:Templates/"
- "AccountSummaryTemplate:templateForm:Components/"
- "AccountSummaryDetails:listview:0:viewTxLink"
- "::ILinkListener::")
- # the "API" sends POST requests for interface changes...
- # so here are a long list of URLs we have to POST to
- rangeselect_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface="
- ":4:Components/CorePageContainer:Templates/CorePage"
- "Template:Components/CorePageComponentContainer:Components"
- "/ViewTransactions:transactionDetailsForm:transactions"
- "Container:viewTransactionHistoryContainer:rangeSelection"
- "Container:selectPeriodDropDown::IBehaviorListener:0:")
- rangeselect_payload = {("transactionsContainer:viewTransactionHistory"
- "Container:rangeSelectionContainer:"
- "selectPeriodDropDown"): "4"}
- rangeselect_headers = {"Wicket-Ajax": "true",
- "Wicket-FocusedElementId": "period"}
- date_format = "%m/%d/%Y"
- dateselect_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:4"
- ":Components/CorePageContainer:Templates/CorePageTemplate:"
- "Components/CorePageComponentContainer:Components/View"
- "Transactions:transactionDetailsForm:transactionsContainer:"
- "viewTransactionHistoryContainer:rangeSelectionContainer:"
- "viewTransactionsHistorySelectionContainer:submitBtnLink::"
- "IActivePageBehaviorListener:0:-1"
- "&wicket:ignoreIfNotActive=true")
- dateselect_payload = {("transactionsContainer:viewTransactionHistoryContainer:"
- "rangeSelectionContainer:selectPeriodDropDown"): "4",
- ("transactionsContainer:allTabContainer:Components/View"
- "AllTabTransactions:viewAllTabContainer:"
- "iwanttodropdown"): "0",
- ("transactionsContainer:viewTransactionHistoryContainer:"
- "rangeSelectionContainer:viewTransactionsHistory"
- "SelectionContainer:submitBtnLink"): "1"}
- dateselect_headers={"Wicket-Ajax": "true", "Wicket-FocusedElementId": "id460"}
- dateselect_payload_sdate_key = ("transactionsContainer:viewTransactionHistory"
- "Container:rangeSelectionContainer:"
- "viewTransactionsHistorySelectionContainer:"
- "fromDateDecorator:fromDate")
- dateselect_payload_edate_key = ("transactionsContainer:viewTransactionHistory"
- "Container:rangeSelectionContainer:"
- "viewTransactionsHistorySelectionContainer:"
- "toDateDecorator:toDate")
- dropdown_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:4:"
- "Components/CorePageContainer:Templates/CorePageTemplate:"
- "Components/CorePageComponentContainer:Components/View"
- "Transactions:transactionDetailsForm:transactionsContainer:"
- "allTabContainer:Components/ViewAllTabTransactions:viewAllTab"
- "Container:iwanttodropdown::IBehaviorListener:0:-1")
- dropdown_payload = {("transactionsContainer:allTabContainer:Components/View"
- "AllTabTransactions:viewAllTabContainer:iwanttodropdown"):
- "2"}
- dropdown_headers={"Wicket-Ajax": "true", "Wicket-FocusedElementId": "id462"}
- csv_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:4:"
- "Components/CorePageContainer:Templates/CorePageTemplate:Components"
- "/CorePageComponentContainer:Components/ViewTransactions:"
- "transactionDetailsForm:transactionsContainer:allTabContainer:"
- "Components/ViewAllTabTransactions:viewAllTabContainer:Components/"
- "TransactionDownload::IActivePageBehaviorListener:2:-1"
- "&wicket:ignoreIfNotActive=true")
- # validate user date input
- # TODO make this prettier
- def get_date(name):
- while True:
- try:
- d = input("Input {} date in YYYY-MM-DD format: ".format(name))
- d = datetime.strptime(d, "%Y-%m-%d")
- return d
- except:
- pass
- # requests session
- # have to use this because pscu API is shit and sends us lots of cookies
- s = requests.Session()
- # get session cookies
- page1 = s.get(cookie_url)
- session_cookie = page1.cookies[session_cookie_name]
- # add session cookie to auth_user_url (wtf?)
- auth_user_url = auth_user_url.replace("<SESSIONCOOKIE>", session_cookie)
- # auth the user
- auth1 = s.post(auth_user_url, data=auth_user_payload)
- print("Getting security question...")
- # find security questions
- # TODO: use beautifulsoup for parsing instead of a regex?
- sec_question = re.findall(sec_question_pattern, auth1.text)[0]
- sec_question_answer = input(sec_question + " ")
- sec_question_payload[sec_question_payload_key] = sec_question_answer
- # send security question
- # TODO: handle failure case
- print("Submitting security question")
- auth2 = s.post(auth2_url, data=sec_question_payload)
- # get password from user
- password = getpass.getpass()
- print("Logging in...")
- # auth the password
- password_payload[password_payload_password_key] = password
- auth3 = s.post(auth3_url, data=password_payload)
- # get a date range
- start_date = args.start[0] if args.start else get_date("start")
- start_date = start_date.strftime(date_format)
- end_date = args.end[0] if args.end else get_date("end")
- end_date = end_date.strftime(date_format)
- dateselect_payload[dateselect_payload_sdate_key] = start_date
- dateselect_payload[dateselect_payload_edate_key] = end_date
- # Getting the CSV is done in 4 stages because the site works by
- # tracking the active HTML elements serverside, so we have to
- # update them individually with post requests. In order, they are:
- # 1. Get the transaction page to activate its DOM elements
- # 2. Send a POST to switch the range select to "date" mode
- # 3. Send a POST to switch the date range to the user's selection
- # 4. Send a POST to active the dropdown to download the CSV
- # Then we send a GET request for the CSV, and the server uses our
- # current DOM state to send the correct CSV
- # fucking insanity
- print("Downloading csv...")
- # need to get transaction page to initialize form so POST can update it
- t_page = s.get(transaction_url)
- # post the rangeselect, then dateselect, then dropdown
- s.post(rangeselect_url, data=rangeselect_payload, headers=rangeselect_headers)
- s.post(dateselect_url, data=dateselect_payload, headers=dateselect_headers)
- s.post(dropdown_url, data=dropdown_payload, headers=dropdown_headers)
- # grab the csv
- csv = s.get(csv_url)
- with open(args.csvfile, 'w') as f:
- f.write(csv.text)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement