Exemple #1
0
def update_token():
    print("Requesting an access token via JWT grant...", end='')
    private_key_bytes = str.encode(ds_config("DS_PRIVATE_KEY"))
    token = api_client.request_jwt_user_token(
        ds_config("DS_CLIENT_ID"), ds_config("DS_IMPERSONATED_USER_GUID"),
        aud(), private_key_bytes, TOKEN_EXPIRATION_IN_SECONDS)
    global account
    if account is None:
        account = get_account_info(api_client)
    base_uri = account['base_uri'] + '/restapi'
    api_client.host = base_uri
    api_client.token = token.access_token
    _token_received = True
    expiresTimestamp = (int(round(time.time())) + TOKEN_EXPIRATION_IN_SECONDS)
    print("\nDone. Continuing...")
Exemple #2
0
def testToken():
    try:
        if(ds_config("DS_CLIENT_ID") == "{CLIENT_ID}"):
            print(date() + "Problem: you need to configure this example, either via environment variables (recommended)\n"
                "or via the ds_configuration.js file.\n"
                "See the README file for more information\n")

        check_token()

    # An API problem
    except docusign.ApiException as e:
        print("\n\nDocuSign Exception!")
        # Special handling for consent_required
        body = e.body.decode('utf8')
        if("consent_required" in body):
            consent_scopes = "signature%20impersonation"
            consent_redirect_URL = "https://www.docusign.com"
            consent_url = "{}/oauth/auth?response_type=code&scope={}&client_id={}&redirect_uri={}".format(ds_config("DS_AUTH_SERVER"), consent_scopes, ds_config("DS_CLIENT_ID"),consent_redirect_URL)
            print(f"""\nC O N S E N T   R E Q U I R E D
            Ask the user who will be impersonated to run the following url:
            {consent_url}
            It will ask the user to login and to approve access by your application.
            Alternatively, an Administrator can use Organization Administration to
            pre-approve one or more users.""")
            sys.exit(0)

        else:
            # Some other DocuSign API problem
            print (f"   Reason: {e.reason}")
            print (f"   Error response: {e.body.decode('utf8')}")
            sys.exit(0)

    # Not an API problem
    except Exception as e:
        print(date() + e)
def saveDoc(envelopeId, orderNumber):
    try:
        # api_client object created when checkToken() function was called in aws_worker
        api_client.set_default_header("Authorization", "Bearer " + api_client.token)
        accountID = get_account_id()
        envelope_api = EnvelopesApi(api_client)
        
        results_file = envelope_api.get_document(accountID , "combined" , envelopeId)

        # Create the output directory if needed
        output_directory = os.path.join(current_directory, r'output')
        if not os.path.exists(output_directory):
            os.makedirs(output_directory)
            if(not os.path.exists(output_directory)):
                print(date() + "Failed to create directory")

        filePath = os.path.join(current_directory, "output",  ds_config("OUTPUT_FILE_PREFIX") + orderNumber + ".pdf")
        # Cannot create a file when file with the same name already exists
        if(os.path.exists(filePath)):
            # Remove the existing file
            os.remove(filePath)
        # Save the results file in the output directory and change the name of the file
        os.rename(results_file,filePath)
        
    # Create a file
    except ApiException as e:
        print(date() + "API exception: {}. saveDoc error".format(e))

        # Catch exception while fetching and saving docs for envelope
    except Exception as e:
        print(date() + "Error while fetching and saving docs for envelope {}, order {}".format(envelopeId, orderNumber))
        print(date() + "saveDoc error {}".format(e))
def processTest(test):
    # Exit the program if BREAK_TEST equals to true or if orderNumber contains "/break"
    if(ds_config("ENABLE_BREAK_TEST") == "True" and "/break" in ("" + test)):
        print(date() +"BREAKING worker test!")
        sys.exit(2)

    print(date() + "Processing test value {}".format(test))

    # Create the test directory if needed
    test_directory = os.path.join(current_directory, r'test_messages')
    if not os.path.exists(test_directory):
        os.makedirs(test_directory)
        if(not os.path.exists(test_directory)):
            print(date() + "Failed to create directory")

    # First shuffle test1 to test2 (if it exists) and so on
    for i in range(9,0,-1):
        old_File_path = os.path.join(test_directory, "test" + str(i) + ".txt")
        new_File_path = os.path.join(test_directory, "test" + str(i+1) + ".txt")
        # If the old file exists
        if(os.path.exists(old_File_path)):
            # If the new file exists - remove it 
            if(os.path.exists(new_File_path)):
                os.remove(new_File_path)
            # Rename the file name - only works if new_File_path does not exist 
            os.rename(old_File_path, new_File_path)

    # The new test message will be placed in test1 - creating new file
    newFile= open(os.path.join(test_directory, "test1.txt"), "w+")
    newFile.write(test)
    print(date() + "New file created")
    newFile.close
Exemple #5
0
def messageHandler(message):
    if(ds_config("DEBUG") == "True"):
        print(date() + "Processing message id: {}".format(message["MessageId"]))

    try:
        # Creates a Json object from the message body
        body = json.loads(message["Body"])
    except Exception as e:
        body = False

    if(body):
        # Parse the information from message body. the information contains contains fields like test and xml
        test = body["test"]
        xml = body["xml"]
        process(test, xml)   
    else:
        print(date() + "Null or bad body in message id {}. Ignoring.".format(message["MessageId"]))
        
    # Delete received message from queue
    sqs.delete_message(QueueUrl=ds_config("QUEUE_URL"),ReceiptHandle=message["ReceiptHandle"])
Exemple #6
0
def get_account_info(client):
    client.host = ds_config("DS_AUTH_SERVER")
    response = client.call_api("/oauth/userinfo",
                               "GET",
                               response_type="object")

    if len(response) > 1 and 200 > response[1] > 300:
        raise Exception("can not get user info:")  # %d".format(response[1])

    accounts = response[0]['accounts']
    target = ds_config("DS_TARGET_ACCOUNT_ID")

    if target is None or target == "FALSE":
        # Look for default
        for acct in accounts:
            if acct['is_default']:
                return acct

    # Look for specific account
    for acct in accounts:
        if acct['account_id'] == target:
            return acct

    raise Exception(f"\n\nUser does not have access to account {target}\n\n")
Exemple #7
0
def startQueue():
    
    # Maintain the array checkLogQ as a FIFO buffer with length 4.
    # When a new entry is added, remove oldest entry and shuffle.
    def addCheckLogQ(message):
        length = 4
        # If checkLogQ size is smaller than 4 add the message
        if(checkLogQ.qsize() < length):
            checkLogQ.put(message)
        # If checkLogQ size is bigger than 4    
        else:
            # Remove the oldest message and add the new one
            checkLogQ.get()
            checkLogQ.put(message)

    # Prints all checkLogQ messages to the console  
    def printCheckLogQ():
    # Prints and Deletes all the elements in the checkLogQ
        for index in range(checkLogQ.qsize()):
            print(checkLogQ.get())

    try:
        while(True):
            # Receive messages from queue, maximum waits for 20 seconds for message
            # receive_request - contain all the queue messages
            receive_request = (sqs.receive_message(QueueUrl=ds_config("QUEUE_URL"), WaitTimeSeconds=20, MaxNumberOfMessages=10)).get("Messages")
            addCheckLogQ(date() +"Awaiting a message...")
            # If receive_request is not None (when message is received)
            if(receive_request is not None):
                msgCount = len(receive_request)
            else:
                msgCount=0
            addCheckLogQ(date() +"found {} message(s)".format(msgCount))
            # If at least one message has been received
            if(msgCount):
                printCheckLogQ()
                for message in receive_request:
                    messageHandler(message)

    # Catches all types of errors that may occur during the program
    except Exception as e:
        printCheckLogQ()
        print(date() + "Queue receive error: {}".format(e))
        time.sleep(5)
        # Restart the program
        global restart
        restart = True
    def send1(cls, test):
        try:
            time.sleep(0.5)
            url = ds_config("ENQUEUE_URL") + "?test=" + test
            request = urllib.request.Request(url)
            request.method = "GET"
            auth = RunTest.authObject()
            if(auth):
                base64string = base64.b64encode (bytes(auth, "utf-8"))
                request.add_header("Authorization", "Basic %s" % base64string.decode("utf-8")) 
            response = urllib.request.urlopen(request)
            if(response.getcode() is not 200):
                print("send1: GET not worked, StatusCode= {}".format(response.getcode()))
            response.close()

        except Exception as e:
            print(f"send1: https error: {e}")
def process(test, xml):
    # Guarding against injection attacks
    # Check the incoming test variable to ensure that it ONLY contains the expected data (empty string "", "/break" or integers string)
    # matcher equals true when it finds wrong input
    pattern = "[^0-9]"
    matcher = re.search(pattern, test)
    validInput = test == "" or test == "/break" or not matcher
    if(validInput):
        if(not test == ""):
            # Message from test mode
            processTest(test)
    else:
        print(date() +"Wrong test value: {}".format(test))
        print("test can only be: /break, empty string or integers string")

    # In test mode there is no xml sting, should be checked before trying to parse it
    if(not xml == ""):
        # Step 1. parse the xml
        root = ET.fromstring(xml)
        # get the namespace from the xml
        def get_namespace(element):
            ns = re.match(r'\{.*\}', element.tag)
            return ns.group(0) if ns else ''
        nameSpace = get_namespace(root)

        # Extract from the XML the fields values
        envelopeId = root.find(f'{nameSpace}EnvelopeStatus/{nameSpace}EnvelopeID').text
        subject = root.find(f'{nameSpace}EnvelopeStatus/{nameSpace}Subject').text
        senderName = root.find(f'{nameSpace}EnvelopeStatus/{nameSpace}UserName').text
        senderEmail = root.find(f'{nameSpace}EnvelopeStatus/{nameSpace}Email').text
        status = root.find(f'{nameSpace}EnvelopeStatus/{nameSpace}Status').text
        created =  root.find(f'{nameSpace}EnvelopeStatus/{nameSpace}Created').text
        orderNumber = root.find(f'{nameSpace}EnvelopeStatus/{nameSpace}CustomFields/{nameSpace}CustomField/{nameSpace}Value').text

        if(status == "Completed"):
            completedMsg = "Completed: True"
        else:
            completedMsg = ""

        # For debugging, you can print the entire notification
        print("EnvelopeId: {}".format(envelopeId))
        print("Subject: {}".format(subject))
        print("Sender: {}, {}".format(senderName, senderEmail))
        print("Order Number: {}".format(orderNumber))
        print("Status: {}".format(status))
        print("Sent: {}, {}".format(created, completedMsg))

        # Step 2. Filter the notifications
        ignore = False
        # Guarding against injection attacks
        # Check the incoming orderNumber variable to ensure that it ONLY contains the expected data ("Test_Mode" or integers string)
        # Envelope might not have Custom field when orderNumber == None
        # matcher equals true when it finds wrong input
        matcher = re.search(pattern, orderNumber)
        validInput = orderNumber == "Test_Mode" or orderNumber == None or not matcher
        if(validInput):
            # Check if the envelope was sent from the test mode
            # If sent from test mode - ok to continue even if the status not equals to Completed
            if(not orderNumber == "Test_Mode"):
                if(not status == "Completed"):
                    ignore = True
                    if(ds_config("DEBUG") == "True"):
                        print(date() +"IGNORED: envelope status is {}".format(status))
            
            if(orderNumber == None or orderNumber == ""):
                ignore = True
                if(ds_config("DEBUG") == "True"):
                    print(date() +"IGNORED: envelope does not have a {} envelope custom field.".format(ds_config("ENVELOPE_CUSTOM_FIELD")))
        else:
            ignore = True
            print(date() + "Wrong orderNumber value: {}".format(orderNumber))
            print("orderNumber can only be: Test_Mode or integers string")
        # Step 3. (Future) Check that this is not a duplicate notification
        # The queuing system delivers on an "at least once" basis. So there is a 
        # chance that we have already processes this notification.
        #
        # For this example, we'll just repeat the document fetch if it is duplicate notification

        # Step 4 save the document - it can raise an exception which will be caught at startQueue 
        if(not ignore):
            saveDoc(envelopeId, orderNumber)
 def authObject(cls):
     if(not ds_config("BASIC_AUTH_NAME") is None and not ds_config("BASIC_AUTH_NAME") == "{BASIC_AUTH_NAME}" 
         and not ds_config("BASIC_AUTH_PW") is None and not ds_config("BASIC_AUTH_PW") == "{BASIC_AUTH_PS}"):
         return ds_config("BASIC_AUTH_NAME") + ":" + ds_config("BASIC_AUTH_PW")
     else:
         return False
Exemple #11
0
import docusign_esign as docusign
import time
import boto3
import json
import queue
import sys
from date_pretty import date
from process_notification import process
from jwt_auth import *
from ds_config_files import ds_config
sqs = boto3.client('sqs', region_name=ds_config("QUEUE_REGION"), aws_access_key_id = ds_config("AWS_ACCOUNT"), aws_secret_access_key = ds_config("AWS_SECRET"))
checkLogQ = queue.Queue()
restart = True

def main():
    listenForever()

# The function will listen forever, dispatching incoming notifications
# to the processNotification library. 
# See https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/sqs-examples-send-receive-messages.html#sqs-examples-send-receive-messages-receiving 
def listenForever():
    # Check that we can get a DocuSign token
    testToken()
    while(True):
        global restart
        if(restart):
            print(date() + "Starting queue worker")
            restart = False
            # Start the queue worker
            startQueue()
        time.sleep(5)