mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-07-07 04:47:55 +02:00
add override sender on template
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
@@ -62,6 +62,12 @@ type CampaignTemplate struct {
|
||||
EmailID *uuid.UUID `gorm:"type:uuid;index;"`
|
||||
Email *Email `gorm:"foreignKey:EmailID;references:ID;"`
|
||||
|
||||
// optional sender overrides applied at send time
|
||||
// an empty value means inherit the value from the email
|
||||
OverrideMailEnvelopeFrom string `gorm:"not null;default:''"`
|
||||
OverrideMailHeaderFrom string `gorm:"not null;default:''"`
|
||||
OverrideSubject string `gorm:"not null;default:''"`
|
||||
|
||||
SMTPConfigurationID *uuid.UUID `gorm:"type:uuid;index;"`
|
||||
SMTPConfiguration *SMTPConfiguration `gorm:"foreignKey:SMTPConfigurationID"`
|
||||
|
||||
|
||||
@@ -57,6 +57,11 @@ type CampaignTemplate struct {
|
||||
EmailID nullable.Nullable[uuid.UUID] `json:"emailID"`
|
||||
Email *Email `json:"email"`
|
||||
|
||||
// optional sender overrides; an empty value means inherit from the email
|
||||
OverrideMailEnvelopeFrom nullable.Nullable[vo.MailEnvelopeFrom] `json:"overrideMailEnvelopeFrom"`
|
||||
OverrideMailHeaderFrom nullable.Nullable[vo.Email] `json:"overrideMailHeaderFrom"`
|
||||
OverrideSubject nullable.Nullable[vo.OptionalString255] `json:"overrideSubject"`
|
||||
|
||||
SMTPConfigurationID nullable.Nullable[uuid.UUID] `json:"smtpConfigurationID"`
|
||||
SMTPConfiguration *SMTPConfiguration `json:"smtpConfiguration"`
|
||||
|
||||
@@ -222,6 +227,24 @@ func (c *CampaignTemplate) ToDBMap() map[string]any {
|
||||
m["email_id"] = c.EmailID.MustGet()
|
||||
}
|
||||
}
|
||||
if c.OverrideMailEnvelopeFrom.IsSpecified() {
|
||||
m["override_mail_envelope_from"] = ""
|
||||
if v, err := c.OverrideMailEnvelopeFrom.Get(); err == nil {
|
||||
m["override_mail_envelope_from"] = v.String()
|
||||
}
|
||||
}
|
||||
if c.OverrideMailHeaderFrom.IsSpecified() {
|
||||
m["override_mail_header_from"] = ""
|
||||
if v, err := c.OverrideMailHeaderFrom.Get(); err == nil {
|
||||
m["override_mail_header_from"] = v.String()
|
||||
}
|
||||
}
|
||||
if c.OverrideSubject.IsSpecified() {
|
||||
m["override_subject"] = ""
|
||||
if v, err := c.OverrideSubject.Get(); err == nil {
|
||||
m["override_subject"] = v.String()
|
||||
}
|
||||
}
|
||||
if c.SMTPConfigurationID.IsSpecified() {
|
||||
if c.SMTPConfigurationID.IsNull() {
|
||||
m["smtp_configuration_id"] = nil
|
||||
|
||||
@@ -889,6 +889,18 @@ func ToCampaignTemplate(row *database.CampaignTemplate) (*model.CampaignTemplate
|
||||
if row.Email != nil {
|
||||
email = ToEmail(row.Email)
|
||||
}
|
||||
overrideMailEnvelopeFrom := nullable.NewNullNullable[vo.MailEnvelopeFrom]()
|
||||
if row.OverrideMailEnvelopeFrom != "" {
|
||||
overrideMailEnvelopeFrom.Set(*vo.NewMailEnvelopeFromMust(row.OverrideMailEnvelopeFrom))
|
||||
}
|
||||
overrideMailHeaderFrom := nullable.NewNullNullable[vo.Email]()
|
||||
if row.OverrideMailHeaderFrom != "" {
|
||||
overrideMailHeaderFrom.Set(*vo.NewEmailMust(row.OverrideMailHeaderFrom))
|
||||
}
|
||||
overrideSubject := nullable.NewNullNullable[vo.OptionalString255]()
|
||||
if row.OverrideSubject != "" {
|
||||
overrideSubject.Set(*vo.NewOptionalString255Must(row.OverrideSubject))
|
||||
}
|
||||
smtpConfigurationID := nullable.NewNullNullable[uuid.UUID]()
|
||||
if row.SMTPConfigurationID != nil {
|
||||
smtpConfigurationID.Set(*row.SMTPConfigurationID)
|
||||
@@ -944,6 +956,9 @@ func ToCampaignTemplate(row *database.CampaignTemplate) (*model.CampaignTemplate
|
||||
AfterLandingPageRedirectURL: redirectURL,
|
||||
EmailID: emailID,
|
||||
Email: email,
|
||||
OverrideMailEnvelopeFrom: overrideMailEnvelopeFrom,
|
||||
OverrideMailHeaderFrom: overrideMailHeaderFrom,
|
||||
OverrideSubject: overrideSubject,
|
||||
SMTPConfigurationID: smtpConfigurationID,
|
||||
SMTPConfiguration: smtpConfiguration,
|
||||
APISenderID: apiSenderID,
|
||||
|
||||
@@ -2088,6 +2088,24 @@ func isCalendarAttachment(path string) bool {
|
||||
return ext == ".ics" || ext == ".ical"
|
||||
}
|
||||
|
||||
// applySenderOverride applies the campaign template's optional sender overrides
|
||||
// onto the in-memory email so all downstream send logic uses the effective values.
|
||||
// an unset override leaves the email's own value unchanged.
|
||||
func applySenderOverride(cTemplate *model.CampaignTemplate, email *model.Email) {
|
||||
if cTemplate == nil || email == nil {
|
||||
return
|
||||
}
|
||||
if v, err := cTemplate.OverrideMailEnvelopeFrom.Get(); err == nil {
|
||||
email.MailEnvelopeFrom = nullable.NewNullableWithValue(v)
|
||||
}
|
||||
if v, err := cTemplate.OverrideMailHeaderFrom.Get(); err == nil {
|
||||
email.MailHeaderFrom = nullable.NewNullableWithValue(v)
|
||||
}
|
||||
if v, err := cTemplate.OverrideSubject.Get(); err == nil {
|
||||
email.MailHeaderSubject = nullable.NewNullableWithValue(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Campaign) sendCampaignMessages(
|
||||
ctx context.Context,
|
||||
session *model.Session,
|
||||
@@ -2208,6 +2226,7 @@ func (c *Campaign) sendCampaignMessages(
|
||||
)
|
||||
return errs.Wrap(errors.Join(err, closeErr))
|
||||
}
|
||||
applySenderOverride(cTemplate, email)
|
||||
content, err := email.Content.Get()
|
||||
if err != nil {
|
||||
// if mail templates fails to parse, close the campaign
|
||||
@@ -3413,6 +3432,7 @@ func (c *Campaign) GetCampaignEmailBody(
|
||||
c.Logger.Errorw("failed to get message by id", "error", err)
|
||||
return "", errs.Wrap(err)
|
||||
}
|
||||
applySenderOverride(cTemplate, email)
|
||||
urlIdentifier := cTemplate.URLIdentifier
|
||||
if urlIdentifier == nil {
|
||||
return "", errors.New("url identifier is nil")
|
||||
@@ -4139,6 +4159,7 @@ func (c *Campaign) sendSingleCampaignMessage(
|
||||
c.Logger.Errorw("failed to get email by id", "error", err)
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
applySenderOverride(cTemplate, email)
|
||||
|
||||
// update last attempt timestamp
|
||||
campaignRecipientID := campaignRecipient.ID.MustGet()
|
||||
|
||||
@@ -655,6 +655,27 @@ func (c *CampaignTemplate) UpdateByID(
|
||||
incoming.EmailID.SetNull()
|
||||
}
|
||||
}
|
||||
if campaignTemplate.OverrideMailEnvelopeFrom.IsSpecified() {
|
||||
if v, err := campaignTemplate.OverrideMailEnvelopeFrom.Get(); err == nil {
|
||||
incoming.OverrideMailEnvelopeFrom.Set(v)
|
||||
} else {
|
||||
incoming.OverrideMailEnvelopeFrom.SetNull()
|
||||
}
|
||||
}
|
||||
if campaignTemplate.OverrideMailHeaderFrom.IsSpecified() {
|
||||
if v, err := campaignTemplate.OverrideMailHeaderFrom.Get(); err == nil {
|
||||
incoming.OverrideMailHeaderFrom.Set(v)
|
||||
} else {
|
||||
incoming.OverrideMailHeaderFrom.SetNull()
|
||||
}
|
||||
}
|
||||
if campaignTemplate.OverrideSubject.IsSpecified() {
|
||||
if v, err := campaignTemplate.OverrideSubject.Get(); err == nil {
|
||||
incoming.OverrideSubject.Set(v)
|
||||
} else {
|
||||
incoming.OverrideSubject.SetNull()
|
||||
}
|
||||
}
|
||||
if campaignTemplate.BeforeLandingPageID.IsSpecified() {
|
||||
if v, err := campaignTemplate.BeforeLandingPageID.Get(); err == nil {
|
||||
incoming.BeforeLandingPageID.Set(v)
|
||||
|
||||
@@ -1074,6 +1074,9 @@ export class API {
|
||||
* @param {string} template.stateIdentifierID
|
||||
* @param {string} template.urlPath
|
||||
* @param {string} template.emailID
|
||||
* @param {string|null} template.overrideMailHeaderFrom
|
||||
* @param {string|null} template.overrideMailEnvelopeFrom
|
||||
* @param {string|null} template.overrideSubject
|
||||
* @returns {Promise<ApiResponse>}
|
||||
*/
|
||||
create: async ({
|
||||
@@ -1092,6 +1095,9 @@ export class API {
|
||||
stateIdentifierID,
|
||||
afterLandingPageRedirectURL,
|
||||
emailID: emailID,
|
||||
overrideMailHeaderFrom = null,
|
||||
overrideMailEnvelopeFrom = null,
|
||||
overrideSubject = null,
|
||||
urlPath: urlPath
|
||||
}) => {
|
||||
return await postJSON(this.getPath('/campaign/template'), {
|
||||
@@ -1110,6 +1116,9 @@ export class API {
|
||||
urlIdentifierID: urlIdentifierID,
|
||||
stateIdentifierID: stateIdentifierID,
|
||||
emailID: emailID,
|
||||
overrideMailHeaderFrom: overrideMailHeaderFrom,
|
||||
overrideMailEnvelopeFrom: overrideMailEnvelopeFrom,
|
||||
overrideSubject: overrideSubject,
|
||||
urlPath: urlPath
|
||||
});
|
||||
},
|
||||
@@ -1132,6 +1141,9 @@ export class API {
|
||||
* @param {string} template.apiSenderID
|
||||
* @param {string} template.afterLandingPageRedirectURL
|
||||
* @param {string} template.emailID
|
||||
* @param {string|null} template.overrideMailHeaderFrom
|
||||
* @param {string|null} template.overrideMailEnvelopeFrom
|
||||
* @param {string|null} template.overrideSubject
|
||||
* @param {string} template.urlIdentifierID
|
||||
* @param {string} template.stateIdentifierID
|
||||
* @param {string} template.urlPath
|
||||
@@ -1152,6 +1164,9 @@ export class API {
|
||||
apiSenderID,
|
||||
afterLandingPageRedirectURL,
|
||||
emailID: emailID,
|
||||
overrideMailHeaderFrom = null,
|
||||
overrideMailEnvelopeFrom = null,
|
||||
overrideSubject = null,
|
||||
urlIdentifierID: urlIdentifierID,
|
||||
stateIdentifierID: stateIdentifierID,
|
||||
urlPath: urlPath
|
||||
@@ -1170,6 +1185,9 @@ export class API {
|
||||
apiSenderID: apiSenderID,
|
||||
afterLandingPageRedirectURL: afterLandingPageRedirectURL,
|
||||
emailID: emailID,
|
||||
overrideMailHeaderFrom: overrideMailHeaderFrom,
|
||||
overrideMailEnvelopeFrom: overrideMailEnvelopeFrom,
|
||||
overrideSubject: overrideSubject,
|
||||
urlIdentifierID: urlIdentifierID,
|
||||
stateIdentifierID: stateIdentifierID,
|
||||
urlPath: urlPath
|
||||
|
||||
@@ -55,6 +55,9 @@
|
||||
afterLandingPage: null,
|
||||
afterLandingPageRedirectURL: '',
|
||||
email: null,
|
||||
overrideMailHeaderFrom: '',
|
||||
overrideMailEnvelopeFrom: '',
|
||||
overrideSubject: '',
|
||||
smtpConfiguration: null,
|
||||
apiSender: null,
|
||||
urlIdentifier: 'id',
|
||||
@@ -306,6 +309,9 @@
|
||||
name: formValues.name,
|
||||
domainID: domainMap.byValue(formValues.domain),
|
||||
emailID: emailMap.byValueOrNull(formValues.email),
|
||||
overrideMailHeaderFrom: formValues.overrideMailHeaderFrom || null,
|
||||
overrideMailEnvelopeFrom: formValues.overrideMailEnvelopeFrom || null,
|
||||
overrideSubject: formValues.overrideSubject || null,
|
||||
smtpConfigurationID: smtpConfigurationMap.byValueOrNull(formValues.smtpConfiguration),
|
||||
apiSenderID: apiSenderMap.byValueOrNull(formValues.apiSender),
|
||||
landingPageID:
|
||||
@@ -346,6 +352,9 @@
|
||||
name: formValues.name,
|
||||
domainID: domainMap.byValueOrNull(formValues.domain),
|
||||
emailID: emailMap.byValueOrNull(formValues.email),
|
||||
overrideMailHeaderFrom: formValues.overrideMailHeaderFrom || null,
|
||||
overrideMailEnvelopeFrom: formValues.overrideMailEnvelopeFrom || null,
|
||||
overrideSubject: formValues.overrideSubject || null,
|
||||
smtpConfigurationID: smtpConfigurationMap.byValueOrNull(formValues.smtpConfiguration),
|
||||
apiSenderID: apiSenderMap.byValueOrNull(formValues.apiSender),
|
||||
landingPageID:
|
||||
@@ -418,6 +427,9 @@
|
||||
afterLandingPage: null,
|
||||
afterLandingPageRedirectURL: '',
|
||||
email: null,
|
||||
overrideMailHeaderFrom: '',
|
||||
overrideMailEnvelopeFrom: '',
|
||||
overrideSubject: '',
|
||||
smtpConfiguration: null,
|
||||
apiSender: null,
|
||||
urlIdentifier: 'id',
|
||||
@@ -511,6 +523,9 @@
|
||||
}
|
||||
formValues.domain = domainMap.byKey(template.domainID);
|
||||
formValues.email = emailMap.byKey(template.emailID);
|
||||
formValues.overrideMailHeaderFrom = template.overrideMailHeaderFrom || '';
|
||||
formValues.overrideMailEnvelopeFrom = template.overrideMailEnvelopeFrom || '';
|
||||
formValues.overrideSubject = template.overrideSubject || '';
|
||||
|
||||
// handle landing page (page or proxy)
|
||||
if (template.landingPageID) {
|
||||
@@ -544,6 +559,9 @@
|
||||
(template.urlIdentifierID && identifierMap.byKey(template.urlIdentifierID) !== 'id') ||
|
||||
(template.stateIdentifierID &&
|
||||
identifierMap.byKey(template.stateIdentifierID) !== 'session') ||
|
||||
template.overrideMailHeaderFrom ||
|
||||
template.overrideMailEnvelopeFrom ||
|
||||
template.overrideSubject ||
|
||||
template.apiSenderID
|
||||
) // Show advanced if using External API
|
||||
);
|
||||
@@ -643,6 +661,12 @@
|
||||
>
|
||||
{emailMap.byKey(template.emailID)}
|
||||
</a>
|
||||
{#if template.overrideMailHeaderFrom || template.overrideMailEnvelopeFrom || template.overrideSubject}
|
||||
<span
|
||||
class="text-xs text-gray-500 dark:text-gray-400"
|
||||
title="This template overrides the email's sender">sender overridden</span
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
@@ -1145,6 +1169,42 @@ Simulation URLs to allow:\n${allowListingData.simulationUrl}\n
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="text-sm font-medium text-pc-darkblue dark:text-white mt-6 mb-3">
|
||||
Sender override
|
||||
</h4>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-3">
|
||||
Reuse the same email with a different sender. Leave a field blank to use the email's
|
||||
own value.
|
||||
</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<TextField
|
||||
toolTipText="Overrides the email's header From. Display name is allowed, e.g. Support <support@acme.com>."
|
||||
optional
|
||||
maxLength={255}
|
||||
bind:value={formValues.overrideMailHeaderFrom}
|
||||
placeholder="Support <support@acme.com>">From override</TextField
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
toolTipText="Overrides the email's envelope from / Return-Path."
|
||||
optional
|
||||
maxLength={255}
|
||||
bind:value={formValues.overrideMailEnvelopeFrom}
|
||||
placeholder="bounce@acme.com">Envelope from override</TextField
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
toolTipText="Overrides the email's subject line."
|
||||
optional
|
||||
maxLength={255}
|
||||
bind:value={formValues.overrideSubject}
|
||||
placeholder="Action required: verify your account">Subject override</TextField
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user