Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,25 @@ cert_bundle = ; Path to CA/chain PEM when validate_certs
; When validate_certs=no, both chain and hostname checks are disabled and
; cert_bundle is ignored.

# Optional HTTP CONNECT proxy for SMTP transport
proxy_host = ; e.g. proxy.company.local
proxy_port = ; e.g. 3128
proxy_user = ; Optional basic-auth username
proxy_password = ; Optional basic-auth password
proxy_connect_timeout = 10 ; Proxy TCP/CONNECT timeout only (seconds)
; Does not change SMTP EHLO/TLS/AUTH/DATA timeouts.

```

**Important**

* Use real booleans: `yes|no` (not quoted strings).
* `cert_bundle` must be a **CA/chain PEM**, **not** the server cert.
* If you supply a custom TLS context in code, some clients ignore `validate_certs`/`cert_bundle`.
* Proxy support uses **HTTP CONNECT** (not SOCKS) and is enabled only when both `proxy_host` and `proxy_port` are set.
* If `proxy_user` is set, ASAB Iris sends `Proxy-Authorization: Basic ...` during CONNECT.
* Proxy mode supports plain SMTP, STARTTLS, and implicit TLS (SMTPS) with normal certificate validation.
* `proxy_connect_timeout` applies only to connecting to the proxy and completing the HTTP CONNECT handshake.

**Common Setups**

Expand Down Expand Up @@ -290,6 +302,26 @@ validate_certs = no
cert_bundle =
```

**D) SMTPS (465) through an HTTP CONNECT proxy**

```ini
[smtp]
host = mail.internal.lan
user = admin
port = 465
ssl = yes
starttls = no
password = ****
from = noreply@internal.lan
validate_certs = yes
cert_bundle = /etc/ssl/internal_ca.pem
proxy_host = proxy.company.local
proxy_port = 3128
proxy_user = proxy-user ; optional
proxy_password = **** ; optional
proxy_connect_timeout = 10
```

### 🚨 2. Sending Slack messages

**Overview**
Expand Down
108 changes: 52 additions & 56 deletions asabiris/handlers/webhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,78 +55,74 @@ async def get_features(self, request):
}
return asab.web.rest.json_response(request, response)

@asab.web.tenant.allow_no_tenant
@asab.web.rest.json_schema_handler(email_schema)
async def send_email(self, request, *, json_data):
"""
This endpoint is for sending emails.
```
1) It collects the basic email info (to, cc, bcc, subject, from)
2) It renders the email body based on the template
3) Optionally it adds attachments:
@asab.web.tenant.allow_no_tenant
@asab.web.rest.json_schema_handler(email_schema)
async def send_email(self, request, *, json_data):
"""
Send one email defined by the request JSON payload.

3.1) The attachment is renders by this service.
The request contract covers message content only:
1. collect recipients and optional headers (`to`, `cc`, `bcc`, `subject`, `from`)
2. render the email body from a template under `/Templates/Email/`
3. optionally render or attach files from `/Templates/Attachment/` or caller-supplied Base64 content

3.2) The attachment is provided by the caller.
Transport behavior such as direct SMTP, SMTP via HTTP CONNECT proxy, or MS365
is selected by server-side configuration and is not part of the request body.

```
Example body:
Example body:

```
{
"to": ['Tony.Montana@Goodfellas.com'],
"cc": ['Jimmy2.times@Goodfellas.com'],
"bcc": ['Henry.Hill@Goodfellas.com'],
"subject": "Lufthansa Hiest",
"from": "Jimmy.Conway@Goodfellas.com",
"body": {
"template": "/Templates/Emails/test.md",
"params": {
"Name": "Toddy Siciro"
}
},
"attachments": [
```json
{
"template": "/Templates/Emails/hello.html",
"params": {
"Name": "Michael Corleone"
"to": ["tony.montana@goodfellas.com"],
"cc": ["jimmy.conway@goodfellas.com"],
"bcc": ["henry.hill@goodfellas.com"],
"subject": "Lufthansa Heist",
"from": "jimmy.conway@goodfellas.com",
"body": {
"template": "/Templates/Email/test.md",
"params": {
"name": "Toddy Siciro"
}
},
"format": "pdf",
"filename": "Alert.pdf"
}]
}

```
Attached will be retrieved from request. Content when rendering the email is not required.
"attachments": [
{
"template": "/Templates/Attachment/hello.html",
"params": {
"name": "Michael Corleone"
},
"format": "pdf",
"filename": "Alert.pdf"
}
]
}
```

Example of the email body template:
```
SUBJECT: Automated email for {{name}}
Example of an email body template:

Hi {{name}},
```text
SUBJECT: Automated email for {{name}}

this is a nice template for an email.
It is {{time}} to leave.
Hi {{name}},

Br,
Your automated ASAB report
```
This is a nice template for an email.
It is {{time}} to leave.

It is a markdown template.
---
tags: ['Send mail']
"""
return await self._send_email(request, json_data)
Br,
Your automated ASAB report
```
---
tags: ['Send mail']
"""
return await self._send_email(request, json_data)

@asab.web.tenant.allow_no_tenant
async def send_email_jsonata(self, request):
"""
This endpoint is for sending emails - JSONata template is applied first to the request body.

It applies JSONata template (stored in `/Templates/JSONata`) to the request body and then the output is used as a body to /send_email endpoint.
It allows to transform arbitrary JSON data into a valid email body.
Apply a JSONata template to the request body, then forward the result to
`/send_email`.

Build the JSONata template at https://try.jsonata.org
JSONata templates are stored under `/Templates/JSONata/` and must produce an
object compatible with the `/send_email` request contract.
"""
jsonata_template = request.match_info["jsonata"]
if '..' in jsonata_template or '/' in jsonata_template:
Expand Down
Loading