I want use multiple aws ses account in one django project...
for some customers, the email must sent from one account and the remaining users must get emails from another account..
You can use boto3 and connect to AWS using different credentials.
Install the boto3 package.
pip install boto3
Then connect to your AWS account and send an email (or more).
import boto3
from botocore.exceptions import ClientError
SENDER = "Sender Name <sender#example.com>"
RECIPIENT = "recipient#example.com"
CONFIGURATION_SET = "ConfigSet"
SUBJECT = "Amazon SES Test (SDK for Python)"
BODY_TEXT = ("Test")
BODY_HTML = """<html><head></head><body><h1>Test</h1></body></html>"""
CHARSET = "UTF-8"
ACCESS_KEY = 'foo'
SECRET_KEY = 'foo'
SESSION_TOKEN = 'foo'
client = boto3.client(
'ses',
region_name="us-west-2",
aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=SECRET_KEY,
aws_session_token=SESSION_TOKEN,
)
try:
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
# If you are not using a configuration set, comment or delete the
# following line
ConfigurationSetName=CONFIGURATION_SET,
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
Sources:
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-using-sdk-python.html
https://boto3.readthedocs.io/en/latest/guide/quickstart.html#installation
Related
I have the following setup:
Python Flask API with boto3 installed. I create a boto3 client like so:
client = boto3.client(
"ses",
region_name='eu-west-1',
aws_access_key_id='myAccessKeyID',
aws_secret_access_key='mySecretAccessKey'
)
Then I try to send an email like so:
try:
client.send_email(
Source='VerifiedSourceEmail',
Destination={
'ToAddresses': ['VerifiedRecipientEmail'],
'CcAddresses': [],
'BccAddresses': [],
},
Message={
'Subject': {'Data': 'Test'},
'Body': {'Text': {'Data': 'Test'},
}
except ClientError as e:
return {
'ErrorCode': e.response['Error']['Code'],
'ErrorMessage': e.response['Error']['Message'],
}
When I try to do this, I get:
ErrorCode: SignatureDoesNotMatch
ErrorMessage: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
I have used the exact same client creation process when connecting to my S3 bucket:
client = boto3.client(
"s3",
region_name='eu-west-1',
aws_access_key_id='myS3AccessKeyID',
aws_secret_access_key='myS3SecretAccessKey'
)
...and this client works fine, I have tested both gets, uploads and deletes with no errors.
I have tested sending a mail directly in AWS, this works. What am I doing wrong?
From this answer:
The keys to be provided to send Emails are not "SMTP Credentials" .
The keys are instead Global access key which can be retrieved
http://docs.amazonwebservices.com/ses/latest/GettingStartedGuide/GetAccessIDs.html.
Trying to update Glue database JDBC connection username and keep failing. choices are CLI or boto3.
CLI docs are so limited.
https://docs.aws.amazon.com/cli/latest/reference/glue/update-connection.html
update-connection
[--catalog-id <value>]
--name <value>
--connection-input <value>
[--cli-input-json <value>]
[--generate-cli-skeleton <value>]
Can someone guide, how to pass username to update here. Also similar in boto3. Throwing exception of invalid parameter.
response = client.update_connection(
Name='test-db',
ConnectionInput={
'Name': 'test-db',
'ConnectionType': 'JDBC' ,
'ConnectionProperties': {
'Username': username
}
}
)
Try:
'ConnectionProperties': {
'USER_NAME': 'your_user_name',
'PASSWORD' : 'your_user_password'
}
Caution: Above is not tested. Its based on Glue Boto3 documentation from here.
So it supposed to be like this.
'USERNAME': username,
'PASSWORD': password
},
'PhysicalConnectionRequirements': PhysicalConnectionRequirements
}
)
You have to add all of the details for the update_connection:
glue_client = boto3.client('glue')
glue_client.update_connection(
Name='Redshift-Rosko',
ConnectionInput={
'Name': 'Redshift-Rosko',
'ConnectionType': 'JDBC',
'ConnectionProperties': {
"USERNAME": username,
"PASSWORD": password,
"JDBC_ENFORCE_SSL": "true",
"JDBC_CONNECTION_URL": "jdbc:redshift://...:5439/rosko_db",
"KAFKA_SSL_ENABLED": "false"
},
'PhysicalConnectionRequirements': {
'SubnetId': 'subnet-......',
'SecurityGroupIdList': [
'sg-......',
'sg-......',
],
"AvailabilityZone": "eu-central-1a"
}
}
)
I have a static serverless website that allows authentication with Javascript using an AWS Cognito User Pool.
Now I'm trying to enable some programmatic access so I need to do this same authentication via a Python script. Is this possible? The docs don't provide any code examples for Python.
I'm just trying to find some way for Python to issue a GET or POST request against an AWS URL, passing it a username and login, and getting back the signed cookies verifying authentication.
The closest example I've found is this code, which references the cognito-idp API. I've modified to:
import boto3
client_id = '<my_app_client_id>'
region_name = 'us-east-1'
auth_data = { 'USERNAME':'myusername' , 'PASSWORD':'mypassword' }
provider_client = boto3.client('cognito-idp', region_name=region_name)
resp = provider_client.initiate_auth(AuthFlow='USER_PASSWORD_AUTH', AuthParameters=auth_data, ClientId=client_id)
print('resp:', resp)
However, even though I use the same credentials as through the Javascript API, this fails to authenticate and simply returns the error:
botocore.exceptions.NoCredentialsError: Unable to locate credentials
This this the correct Python equivalent as the Javascript Cognito API?
The following snippet shows a complete authentication workflow with Cognito using boto3.
def get_secret_hash(username):
msg = username + CLIENT_ID
dig = hmac.new(
str(CLIENT_SECRET).encode('utf-8'),
msg=str(msg).encode('utf-8'),
digestmod=hashlib.sha256
).digest()
d2 = base64.b64encode(dig).decode()
return d2
def initiate_auth(client, username, password):
secret_hash = get_secret_hash(username)
try:
resp = client.admin_initiate_auth(
UserPoolId=USER_POOL_ID,
ClientId=CLIENT_ID,
AuthFlow='ADMIN_NO_SRP_AUTH',
AuthParameters={
'USERNAME': username,
'SECRET_HASH': secret_hash,
'PASSWORD': password,
},
ClientMetadata={
'username': username,
'password': password, })
except client.exceptions.NotAuthorizedException:
return None, "The username or password is incorrect"
except client.exceptions.UserNotConfirmedException:
return None, "User is not confirmed"
except Exception as e:
return None, e.__str__()
return resp, None
#app.route('/auth/login', methods=['POST'])
def login():
event = auth.current_request.json_body
client = boto3.client('cognito-idp')
username = event['username']
password = event['password']
for field in ["username", "password"]:
if event.get(field) is None:
return {"error": True,
"success": False,
"message": f"{field} is required",
"data": None}
resp, msg = initiate_auth(client, username, password)
if msg != None:
return {'message': msg,
"error": True, "success": False, "data": None}
if resp.get("AuthenticationResult"):
return {'message': "success",
"error": False,
"success": True,
"data": {
"id_token": resp["AuthenticationResult"]["IdToken"],
"refresh_token": resp["AuthenticationResult"]["RefreshToken"],
"access_token": resp["AuthenticationResult"]["AccessToken"],
"expires_in": resp["AuthenticationResult"]["ExpiresIn"],
"token_type": resp["AuthenticationResult"]["TokenType"]
}}
else: # this code block is relevant only when MFA is enabled
return {"error": True,
"success": False,
"data": None, "message": None}
Here's the important part parsed from the functions.
resp = client.admin_initiate_auth(
UserPoolId=USER_POOL_ID,
ClientId=CLIENT_ID,
AuthFlow='ADMIN_NO_SRP_AUTH',
AuthParameters={
'USERNAME': username,
'SECRET_HASH': secret_hash,
'PASSWORD': password,
},
ClientMetadata={
'username': username,
'password': password, })
The examples were taken from a four part tutorial that unfortunately didn't help me integrate this with the Chalice CognitoUserPoolAuthorizer but otherwise seems to work well. Here are the tutorials if you can't find better code examples.
part 1
part 2
part 3
part 4
Pass the access and secret key to boto3 like this.
provider_client = boto3.client('cognito-idp', region_name=region_name, aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY)
How can I put multiple email recipient addresses in my ses instead of
RECIPIENT = "recipient#example.com"
or how to make it read an email list txt file line by line using this:
mylist = open(list.txt,r)
read_me = mylist.readlines()
for i in read_me:
mailist = i.strip()
import boto3
from botocore.exceptions import ClientError
SENDER = "Sender Name <sender#example.com>"
RECIPIENT = "recipient#example.com"
AWS_REGION = "us-west-2"
SUBJECT = "Amazon SES Test (SDK for Python)"
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto)."
)
BODY_HTML = """<html>
<head></head>
<body>
<h1>Amazon SES Test (SDK for Python)</h1>
<p>This email was sent with
<a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
<a href='https://aws.amazon.com/sdk-for-python/'>
AWS SDK for Python (Boto)</a>.</p>
</body>
</html>
"""
CHARSET = "UTF-8"
# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)
# Try to send the email.
try:
#Provide the contents of the email.
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
# If you are not using a configuration set, comment or delete the
# following line
ConfigurationSetName=CONFIGURATION_SET,
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
Try passing in array like this:
Recipients = ['Recipient One <recipient_1#email.com>', 'recipient_2#email.com']
or more specifically:
Destination={'ToAddresses': ['recipient_1#email.com', 'recipient_2#email.com']},
Using the Python Quickstart example for the v4 Google Sheets API as a starting point, I've tried to make a library with read and write functions which can then be used by higher-level classes to easily interact with my sheet. This only works if I use the library itself as a script to call the read/write functions. Both read and write throw the following error if I use them after importing into an external script located in a different directory:
HttpError 404 when requesting https://sheets.googleapis.com/$discovery/v4/spreadsheets/
That URL looks malformed with "$discovery" in it.
Here's my library with a main section which works well if this library is run as a script:
# sheetlib.py
""" Google Docs Spreadsheets wrapper
"""
import httplib2
import os
import json
json.JSONEncoder.default=str
from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
SCOPES = 'https://www.googleapis.com/auth/spreadsheets'
CLIENT_SECRET_FILE = 'client_secret.json'
CREDENTIAL_PATH = 'sheets.googleapis.test.json'
APPLICATION_NAME = 'Test Sheet'
SPREADSHEET_ID = 'abcdefg'
def get_credentials():
"""Gets valid user credentials from storage.
If nothing has been stored, or if the stored credentials are invalid,
the OAuth2 flow is completed to obtain the new credentials.
Returns:
Credentials, the obtained credential.
"""
store = Storage(CREDENTIAL_PATH)
print 'Environment: {}'.format(json.dumps(os.environ.__dict__['data']))
print 'Loaded store from {}: {}'.format(CREDENTIAL_PATH, json.dumps(store.__dict__))
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
credentials = tools.run_flow(flow, store)
print 'Storing credentials to ' + CREDENTIAL_PATH
return credentials
def build_service():
""" Returns service object for reading/writing to spreadsheet """
credentials = get_credentials()
print "credentials: {}".format(json.dumps(credentials.__dict__))
http = credentials.authorize(httplib2.Http())
discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?version=v4')
service = discovery.build('sheets', 'v4', http=http, discoveryServiceUrl=discoveryUrl)
print 'service: {}'.format(json.dumps(service._http.__dict__))
return service
def write(range, values):
service = build_service()
body = {
'values': values
}
service.spreadsheets().values().append(
spreadsheetId=SPREADSHEET_ID, range=range,
valueInputOption='RAW', body=body, insertDataOption='INSERT_ROWS').execute()
def read(range):
""" Pass a range to read, like 'RawData!A:E' """
service = build_service()
resp = service.spreadsheets().values().get(spreadsheetId=SPREADSHEET_ID, range=range).execute()
return resp
class Magic():
"""Reads and writes to the Magic tab of sheet"""
def spell_list(self):
return [r for r in read('Magic!A1:G100')['values'][1:]]
if __name__ == '__main__':
m = Magic()
print m.spell_list()
If I move the Magic class to another file located in a different directory and try to use imported read, it throws the 404 error:
# magic_test.py
from sheetlib import read
class BadMagic():
"""Reads and writes to the Magic tab of sheet"""
def spell_list(self):
return [r for r in read('Magic!A1:G100')['values'][1:]]
m = BadMagic()
m.spell_list()
Traceback (most recent call last):
File "magic_test.py", line 0, in main
return [r[0] for r in read('Magic!A2:A100')['values']]
File "sheetlib.py", line 0, in read
resp = service.spreadsheets().values().get(spreadsheetId=SPREADSHEET_ID, range=range).execute()
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/google-api-python-client/apiclient/http.py", line 292, in execute
raise HttpError(resp, content, self.uri)
apiclient.errors.HttpError: <HttpError 404 when requesting https://sheets.googleapis.com/$discovery/v4/spreadsheets/abcdefg/values/Magic%21A2%3AA100?alt=json returned "Not Found">
Exploring further, I see that the credentials: and service: output from the build_service() function is different depending on which script is using it:
Calling from sheetlib.py (working)
credentials:
{
"scopes": "set([u'https://www.googleapis.com/auth/spreadsheets'])",
"revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
"access_token": "asdf",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"token_info_uri": "https://www.googleapis.com/oauth2/v3/tokeninfo",
"token_response": {
"access_token": "asdf",
"token_type": "Bearer",
"expires_in": 3600
},
"invalid": false,
"refresh_token": "qwer",
"client_id": "1234.apps.googleusercontent.com",
"id_token": null,
"client_secret": "zxcv",
"token_expiry": "2017-03-08 17:01:42",
"store": "<oauth2client.file.Storage object at 0x10bbd6690>",
"user_agent": "Magic Sheet"
}
service:
{
"force_exception_to_status_code": false,
"forward_authorization_headers": false,
"authorizations": [],
"proxy_info": "<function proxy_info_from_environment at 0x10af9aed8>",
"follow_redirects": true,
"cache": null,
"request": "<function new_request at 0x10b3dba28>",
"connections": {},
"certificates": "<httplib2.KeyCerts object at 0x10b3df3d0>",
"optimistic_concurrency_methods": [
"PUT",
"PATCH"
],
"follow_all_redirects": false,
"timeout": null,
"ignore_etag": false,
"ca_certs": null,
"credentials": "<httplib2.Credentials object at 0x10b3df410>",
"disable_ssl_certificate_validation": false
}
Calling from magic_test.py (broken)
credentials:
{
"access_token": "asdf",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"invalid": false,
"refresh_token": "qwer",
"client_id": "1234.apps.googleusercontent.com",
"id_token": null,
"client_secret": "zxcv",
"token_expiry": "2017-03-08 17:01:42",
"store": "<oauth2client.file.Storage object at 0x1101a2e50>",
"user_agent": "Accounting Sheet"
}
service:
{
"force_exception_to_status_code": false,
"forward_authorization_headers": false,
"authorizations": [],
"proxy_info": "<bound method type.from_environment of <class 'httplib2.ProxyInfo'>>",
"follow_redirects": true,
"cache": null,
"request": "<function new_request at 0x1101bae60>",
"connections": {
"https:sheets.googleapis.com": "<httplib2.HTTPSConnectionWithTimeout instance at 0x1101b1ea8>"
},
"certificates": "<httplib2.KeyCerts object at 0x1101c2890>",
"optimistic_concurrency_methods": [
"PUT",
"PATCH"
],
"follow_all_redirects": false,
"timeout": null,
"ignore_etag": false,
"ca_certs": null,
"credentials": "<httplib2.Credentials object at 0x1101c28d0>",
"disable_ssl_certificate_validation": false
}
Any clue why different parts of http2lib would be used depending on which script called it?
You may refer with this thread. Be noted that the body arg included in update(), although shown in the documentation as json, actually needs to be a regular python dictionary. Also, your 404 error means that you requested something that doesn't exist (or that it doesn't want you to know exists). Here's an article which might help you in fixing 404 error.