From dad53f2a736a6d725f6709bff258d71b348a3cbf Mon Sep 17 00:00:00 2001 From: Bomi Lee Date: Fri, 22 Apr 2022 09:53:28 -0400 Subject: [PATCH] v5.19 --- .../lambda_functions/sfExecuteAWSService.py | 115 ----------- .../sfGenerateAudioRecordingStreamingURL.py | 93 --------- .../lambda_functions/sfSig4RequestToS3.zip | Bin 2027 -> 0 bytes sam-app/lambda_functions/template.yaml | 178 ------------------ 4 files changed, 386 deletions(-) delete mode 100644 sam-app/lambda_functions/sfGenerateAudioRecordingStreamingURL.py delete mode 100644 sam-app/lambda_functions/sfSig4RequestToS3.zip diff --git a/sam-app/lambda_functions/sfExecuteAWSService.py b/sam-app/lambda_functions/sfExecuteAWSService.py index 55ea11f..c230058 100644 --- a/sam-app/lambda_functions/sfExecuteAWSService.py +++ b/sam-app/lambda_functions/sfExecuteAWSService.py @@ -56,12 +56,8 @@ def lambda_handler(event, context): result = connect_associate_approved_origin(**params) elif method == "retrieve_lambda_parameters": result = retrieve_lambda_parameters(**params) - elif method == "setup_audio_recording": - result = setup_audio_recording(**params) elif method == "get_aws_region": result = get_aws_region() - elif method == "generate_audio_recording_url": - result = generate_audio_recording_url(params) else: raise Exception("Invalid method: " + method) return { @@ -229,120 +225,9 @@ def retrieve_lambda_parameters(ConnectInstanceAlias): logger.info("result: %s" % json.dumps(result)) return result -def setup_audio_recording(CloudfrontPublicKey): - s3_client = boto3.client("s3") - bucket_name = os.environ["RECORDING_BUCKET_NAME"] - bucket_cors_rules = [] - try: - bucket_cors_rules = s3_client.get_bucket_cors(Bucket=bucket_name)["CORSRules"] - except botocore.exceptions.ClientError as e: - if e.response['Error']['Code'] != 'NoSuchCORSConfiguration': - raise e - - sf_host = os.environ["SALESFORCE_HOST"] - lightning_url = sf_host[:sf_host.index(".my.salesforce.com")] + ".lightning.force.com" - visualforce_url = sf_host[:sf_host.index(".my.salesforce.com")] - if os.environ["NAMESPACE"]: - visualforce_url = visualforce_url + "--" + os.environ["NAMESPACE"] + ".visualforce.com" - else: - visualforce_url = visualforce_url + "--c.visualforce.com" - - for rule in bucket_cors_rules: - if lightning_url in rule["AllowedOrigins"]: - bucket_cors_rules.remove(rule) - - bucket_cors_rules.append({ - "AllowedHeaders": ["Access-Control-Allow-Origin"], - "AllowedMethods": ["GET"], - "AllowedOrigins": [lightning_url, visualforce_url] - }) - s3_client.put_bucket_cors( - Bucket=bucket_name, - CORSConfiguration={ - "CORSRules": bucket_cors_rules - } - ) - - cloudfront_client = boto3.client("cloudfront") - create_public_key_response = cloudfront_client.create_public_key( - PublicKeyConfig={ - 'CallerReference': str(uuid.uuid4()), - 'Name': 'AmazonConnectSalesforceCTIAdapterContactLens', - 'EncodedKey': CloudfrontPublicKey - } - ) - create_key_group_response = cloudfront_client.create_key_group( - KeyGroupConfig={ - 'Name': 'AmazonConnectSalesforceCTIAdapterContactLens', - 'Items': [ - create_public_key_response["PublicKey"]["Id"] - ] - } - ) - - # edge lambdas must be created in us-east-1 - lambda_client = boto3.client("lambda", region_name='us-east-1') - cloudformation_stack_name = os.environ["CLOUDFORMATION_STACK_NAME"] - MAX_LAMBDA_NAME_LENGTH = 64 - function_name_end = '-sfSig4RequestToS3' - function_name_start = cloudformation_stack_name[:MAX_LAMBDA_NAME_LENGTH - len(function_name_end)] - function_name = function_name_start + function_name_end - create_function_response = lambda_client.create_function( - FunctionName=function_name, - Runtime='nodejs12.x', - Role=os.environ["SIG4_LAMBDA_ROLE_ARN"], - Handler='sfSig4RequestToS3.handler', - Code={ - 'ZipFile': open('./sfSig4RequestToS3.zip', 'rb').read() - } - ) - publish_version_response = lambda_client.publish_version( - FunctionName=function_name - ) - - get_distribution_config_response = cloudfront_client.get_distribution_config( - Id=os.environ["CLOUDFRONT_DISTRIBUTION_ID"] - ) - - distribution_config = get_distribution_config_response["DistributionConfig"] - distribution_config["DefaultCacheBehavior"]["LambdaFunctionAssociations"] = { - 'Quantity': 1, - 'Items': [ - { - 'LambdaFunctionARN': publish_version_response["FunctionArn"], - 'EventType': 'origin-request', - 'IncludeBody': False - }, - ] - } - distribution_config["DefaultCacheBehavior"]["TrustedSigners"] = { - 'Enabled': False, - 'Quantity': 0 - } - distribution_config["DefaultCacheBehavior"]["TrustedKeyGroups"] = { - 'Enabled': True, - 'Quantity': 1, - 'Items': [ create_key_group_response["KeyGroup"]["Id"] ] - } - - return format_datetime_values(cloudfront_client.update_distribution( - DistributionConfig=distribution_config, - Id=os.environ["CLOUDFRONT_DISTRIBUTION_ID"], - IfMatch=get_distribution_config_response["ETag"] - )) - def get_aws_region(): return os.environ["AWS_REGION"] -def generate_audio_recording_url(params): - lambda_client = boto3.client('lambda') - resp = lambda_client.invoke(FunctionName=os.environ["GENERATE_AUDIO_RECORDING_LAMBDA"], InvocationType='RequestResponse', Payload=json.dumps(params)) - logger.info(resp) - lambda_result = resp["Payload"].read().decode("utf-8") - if resp["StatusCode"] < 200 or resp["StatusCode"] >= 300: - raise Exception("ERROR: GENERATE_AUDIO_RECORDING_LAMBDA failed with " + lambda_result) - return lambda_result - def getConnectInstanceIdFromInstanceAlias(ConnectInstanceAlias, connect_client): list_instances_result = connect_client.list_instances(MaxResults=20) instance_list = list_instances_result["InstanceSummaryList"] diff --git a/sam-app/lambda_functions/sfGenerateAudioRecordingStreamingURL.py b/sam-app/lambda_functions/sfGenerateAudioRecordingStreamingURL.py deleted file mode 100644 index eb3e387..0000000 --- a/sam-app/lambda_functions/sfGenerateAudioRecordingStreamingURL.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -You must have an AWS account to use the Amazon Connect CTI Adapter. -Downloading and/or using the Amazon Connect CTI Adapter is subject to the terms of the AWS Customer Agreement, -AWS Service Terms, and AWS Privacy Notice. - -© 2017, Amazon Web Services, Inc. or its affiliates. All rights reserved. - -NOTE: Other license terms may apply to certain, identified software components -contained within or distributed with the Amazon Connect CTI Adapter if such terms are -included in the LibPhoneNumber-js and Salesforce Open CTI. For such identified components, -such other license terms will then apply in lieu of the terms above. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import datetime -import boto3 -import base64 -import json, logging, os - -from sf_util import get_arg -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import padding -from botocore.signers import CloudFrontSigner -import logging -logger = logging.getLogger() -logger.setLevel(logging.getLevelName(os.environ["LOGGING_LEVEL"])) - -PRIVATE_KEY_HEADER = '-----BEGIN RSA PRIVATE KEY-----' -PRIVATE_KEY_FOOTER = '-----END RSA PRIVATE KEY-----\r\n' - -def lambda_handler(event, context): - if 'recordingPath' not in event or not event['recordingPath'] or event['recordingPath'] == 'null': - logger.info("No recordingPath in event; returning.") - return None - # retrieve secrets - logger.info("Retrieving cloudfront credentials") - session = boto3.session.Session() - client = session.client(service_name='secretsmanager') - sf_credentials_secrets_manager_arn = get_arg(os.environ, - 'SF_CREDENTIALS_SECRETS_MANAGER_ARN') - secrets = json.loads(client.get_secret_value(SecretId=sf_credentials_secrets_manager_arn)['SecretString']) - private_key = secrets['CloudFrontPrivateKey'] - access_key_id = secrets['CloudFrontAccessKeyID'] - logger.info("Cloudfront credentials retrieved") - - # construct url to audio recording - recordingPath = event['recordingPath'] # need to remove bucket name, connect dir from path - if("/connect/" in recordingPath): - recordingPath = "connect/" + recordingPath.split("/connect/", 1)[1] - elif("/Analysis/" in recordingPath): - recordingPath = "Analysis/" + recordingPath.split("/Analysis/", 1)[1] - cloudfront_domain = get_arg(os.environ, 'CLOUDFRONT_DISTRIBUTION_DOMAIN_NAME') - url = 'https://' + cloudfront_domain + '/' + recordingPath - logger.info('Unsigned audio recording url: %s' % url) - - # sign url - expire_date = datetime.datetime.utcnow() + datetime.timedelta(minutes=60) - cloudfront_signer = CloudFrontSigner(access_key_id, rsa_signer(private_key)) - signed_url = cloudfront_signer.generate_presigned_url( - url, date_less_than=expire_date) - logger.info('Signed audio recording url: %s' % signed_url) - return signed_url - -def rsa_signer(key): - def rsa_signer_with_key(message): - private_key = serialization.load_pem_private_key( - format_private_key(key), - password=None, - backend=default_backend() - ) - return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1()) - return rsa_signer_with_key - -def format_private_key(private_key): - header_len = len(PRIVATE_KEY_HEADER) - footer_len = len(PRIVATE_KEY_FOOTER) - rsa_key = PRIVATE_KEY_HEADER \ - + private_key[header_len:-(footer_len-2)].replace(' ', '\r\n') \ - + PRIVATE_KEY_FOOTER - return rsa_key.encode('utf8') \ No newline at end of file diff --git a/sam-app/lambda_functions/sfSig4RequestToS3.zip b/sam-app/lambda_functions/sfSig4RequestToS3.zip deleted file mode 100644 index 776c2d45069f6cd85668f29aa18e84d3629a3e43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2027 zcmbW2dpHx08pk*HFu7lnOOc;T=02BJ$y&;mvCYDeV*JeFw_GxJa}UR*YQK4fwZ+ zasYrLKpPMifC&n;L=(sngs^aID8>Rz40FSR0OB_8BffUOYp+K{NdbT&qgFWg-$?*K z;RTP-If!gLo8c>^SipSLlzH1SGOr?y^S(7kBx6|WopzbQ1X?ExeD26<$okYV7`kW< z?s`&JKG0^tI=v!eFeSti^$LC6>fGbWYO>;xqtdX|_R;HknGsE|dYs^*oLq;=z@A!E zRwes%Ii*~%kKHH^Zpoir17D})~uDfOzxR`h{#tm={irnj;61KTiSVm&p1A#Y3XZeuFbsut4S4 zk~TCmn?5o}>sefFxNr_06Lpmp)LrH9y2j<#N)3v0S3r8lE?9a8x3Bv4dGoo39yV;F z+Qx`LZ;3PhN}0J`9OXqew%F;)SvYV|w&0C%G3rEOClp9g>sOLPJgYw{MYnM6k{X-O zhh^jBD

Sq#WpcG>VgzvSW$B$s&=ebN;84Op9+Bo2i32#b|!DgSq2ndWJkDbF{2C zYdGntkajp&{#9nmw0n3@@?h#0sp2iosq^#^kAqX~x3($P2?{r9QIPf$?-u;Z|GbmZ&m2D++JRy}O?U<4H z-P41EPK+8LeDa9P(izPhcw35;>kO6atel3L4Lr0LCv0qHpdU~Zp>#Ebt20#Fz+VqK zjmkS;*X_i%EhpklgN1E(^J9SwiLFSoFMPPId7_d@=+#L*Eun8}>9E>uqq#rZRDFAi z`(#Ynhpgvc5#lqIMpCm|@}MP18jO~@tRZ55=4bR=EKMpw-h)i2v4<#@u&Rna1!)Ui z9TNo!_kc@Ef%nrdRoPbS;=z7K{H>w8_6EqJZHkab!SkfjD+df6%Z@?OnG=Sr zPiKPaqcsYGQ|fL6$Ul*LLoN4~9B@*PTfSuEE1A>z;ZNbkFl3IP?uj$p{Gs%|FEK77 z*4TTXF*?}4YWT9o;heVGOqZShb{r)azr=S`o88fv8aTzfW4L4r!{>XUYK`U?qvHb_ z7$#>teoq^d*#Ysv)%fZt-<$XWd}ypRsCC$NPyNu?(XmmpdHrOkPd~X?8L7KPd@V*BQJCrg ziXJP{w|{t-ui{upECtos$43*o9ou}C`Ak|1*OEHY9^f_Rw?i*j=3-uA`w)C{HHL(p zYPj`>GMU9*ZA;llR<3S@_f2*cW(w*lLm>KLtnGvHo#n*RA42IK^-duEG5@Fq)6Z^( zw2<+-=HMhkh_;B;$(4(-H<5zX9+5nN^LPrcdcW^$Oiib{$zbW)AB>hMSV^P#J?4!C zBkvR57%orXNBXAyLeTJ5ZQ|{($m}I;*0UBO-*{`?E}xnli8ah6noLRtF~zRrGK;>@ zV9m1Q>X(ph7Q0))K97GWKgXrSGWV^{LdLVJ!CP%li#Vs(ai1iiSK3IRJn4;H;?)wF z(6_DvIKbj9IOWUg&`O^((&Ag&K*4m*MruuQ%4A0zOJ|Oo{7uxP*-bXso{3lvQR(`A z@nh}89p6U~Vfy=9PQ~oLQ$oi-IIAy&rwC zjS{njD5^6&7csZp3cI&lo6Qao^U{#26O~6+c;9aAhFsGUSTvv^5wZ^ zD)iUmR*gdU)D3s9T^OUr>z;L2`$B^Lu8IpnTteMeL0L{50N4Yo3j!#lDEwdO{_k!N ey8lsS(%$s{AlL;VDz