Files
phishingclub/backend/model/campaignTemplate.go
2025-11-21 10:45:53 +01:00

282 lines
8.3 KiB
Go

package model
import (
"time"
"github.com/go-errors/errors"
"github.com/google/uuid"
"github.com/oapi-codegen/nullable"
"github.com/phishingclub/phishingclub/errs"
"github.com/phishingclub/phishingclub/validate"
"github.com/phishingclub/phishingclub/vo"
)
// CampaignTemplate is a campaign template
type CampaignTemplate struct {
ID nullable.Nullable[uuid.UUID] `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
Name nullable.Nullable[vo.String64] `json:"name"`
DomainID nullable.Nullable[uuid.UUID] `json:"domainID"`
Domain *Domain `json:"domain"`
BeforeLandingPageID nullable.Nullable[uuid.UUID] `json:"beforeLandingPageID"`
BeforeLandingePage *Page `json:"beforeLandingPage"`
// before landing page can also be a proxy
BeforeLandingProxyID nullable.Nullable[uuid.UUID] `json:"beforeLandingProxyID"`
BeforeLandingProxy *Proxy `json:"beforeLandingProxy"`
LandingPageID nullable.Nullable[uuid.UUID] `json:"landingPageID"`
LandingPage *Page `json:"landingPage"`
// landing page can also be a proxy
LandingProxyID nullable.Nullable[uuid.UUID] `json:"landingProxyID"`
LandingProxy *Proxy `json:"landingProxy"`
AfterLandingPageID nullable.Nullable[uuid.UUID] `json:"afterLandingPageID"`
AfterLandingPage *Page `json:"afterLandingPage"`
// after landing page can also be a proxy
AfterLandingProxyID nullable.Nullable[uuid.UUID] `json:"afterLandingProxyID"`
AfterLandingProxy *Proxy `json:"afterLandingProxy"`
AfterLandingPageRedirectURL nullable.Nullable[vo.OptionalString255] `json:"afterLandingPageRedirectURL"`
URLIdentifierID nullable.Nullable[*uuid.UUID] `json:"urlIdentifierID"`
URLIdentifier *Identifier `json:"urlIdentifier"`
StateIdentifierID nullable.Nullable[*uuid.UUID] `json:"stateIdentifierID"`
StateIdentifier *Identifier `json:"stateIdentifier"`
URLPath nullable.Nullable[vo.URLPath] `json:"urlPath"`
EmailID nullable.Nullable[uuid.UUID] `json:"emailID"`
Email *Email `json:"email"`
SMTPConfigurationID nullable.Nullable[uuid.UUID] `json:"smtpConfigurationID"`
SMTPConfiguration *SMTPConfiguration `json:"smtpConfiguration"`
APISenderID nullable.Nullable[uuid.UUID] `json:"apiSenderID"`
APISender *APISender `json:"apiSender"`
CompanyID nullable.Nullable[uuid.UUID] `json:"companyID"`
Company *Company `json:"company"`
IsUsable nullable.Nullable[bool] `json:"isUsable"`
}
// Validate checks if the campaign template has a valid state
func (c *CampaignTemplate) Validate() error {
if err := validate.NullableFieldRequired("name", c.Name); err != nil {
return err
}
if err := validate.NullableFieldRequired("urlIdentifierID", c.URLIdentifierID); err != nil {
return err
}
if err := validate.NullableFieldRequired("stateIdentifierID", c.StateIdentifierID); err != nil {
return err
}
if a, err := c.URLIdentifierID.Get(); err == nil {
if b, err := c.StateIdentifierID.Get(); err == nil {
if a.String() == b.String() {
return errs.NewValidationError(
errors.New("URL and state identifier can not be the same"),
)
}
}
}
// URLPath is optional, no validation needed
// validate that only one type is set per stage
// before landing page: can have neither (optional), or one type, but not both
_, errBeforePage := c.BeforeLandingPageID.Get()
_, errBeforeProxy := c.BeforeLandingProxyID.Get()
if errBeforePage == nil && errBeforeProxy == nil {
return errs.NewValidationError(
errors.New("before landing page cannot be both a page and a proxy"),
)
}
// landing page: must have exactly one type (required)
_, errLandingPage := c.LandingPageID.Get()
_, errLandingProxy := c.LandingProxyID.Get()
if errLandingPage == nil && errLandingProxy == nil {
return errs.NewValidationError(
errors.New("landing page cannot be both a page and a proxy"),
)
}
if errLandingPage != nil && errLandingProxy != nil {
return errs.NewValidationError(
errors.New("landing page is required (must be either a page or a proxy)"),
)
}
// after landing page: can have neither (optional), or one type, but not both
_, errAfterPage := c.AfterLandingPageID.Get()
_, errAfterProxy := c.AfterLandingProxyID.Get()
if errAfterPage == nil && errAfterProxy == nil {
return errs.NewValidationError(
errors.New("after landing page cannot be both a page and a proxy"),
)
}
// validate that smtp configuration and api sender are mutually exclusive
_, errSMTP := c.SMTPConfigurationID.Get()
_, errAPISender := c.APISenderID.Get()
if errSMTP == nil && errAPISender == nil {
return errs.NewValidationError(
errors.New("smtp configuration and api sender cannot both be set"),
)
}
return nil
}
// ToDBMap converts the fields that can be stored or updated to a map
// if the value is nullable and not set, it is not included
// if the value is nullable and set, it is included, if it is null, it is set to nil
func (c *CampaignTemplate) ToDBMap() map[string]any {
m := map[string]any{}
if c.Name.IsSpecified() {
m["name"] = nil
if name, err := c.Name.Get(); err == nil {
m["name"] = name.String()
}
}
if c.DomainID.IsSpecified() {
if c.DomainID.IsNull() {
m["domain_id"] = nil
} else {
m["domain_id"] = c.DomainID.MustGet()
}
}
if c.BeforeLandingPageID.IsSpecified() {
if c.BeforeLandingPageID.IsNull() {
m["before_landing_page_id"] = nil
} else {
m["before_landing_page_id"] = c.BeforeLandingPageID.MustGet()
}
}
if c.BeforeLandingProxyID.IsSpecified() {
if c.BeforeLandingProxyID.IsNull() {
m["before_landing_proxy_id"] = nil
} else {
m["before_landing_proxy_id"] = c.BeforeLandingProxyID.MustGet()
}
}
if c.LandingPageID.IsSpecified() {
if c.LandingPageID.IsNull() {
m["landing_page_id"] = nil
} else {
m["landing_page_id"] = c.LandingPageID.MustGet()
}
}
if c.LandingProxyID.IsSpecified() {
if c.LandingProxyID.IsNull() {
m["landing_proxy_id"] = nil
} else {
m["landing_proxy_id"] = c.LandingProxyID.MustGet()
}
}
if c.AfterLandingPageID.IsSpecified() {
if c.AfterLandingPageID.IsNull() {
m["after_landing_page_id"] = nil
} else {
m["after_landing_page_id"] = c.AfterLandingPageID.MustGet()
}
}
if c.AfterLandingProxyID.IsSpecified() {
if c.AfterLandingProxyID.IsNull() {
m["after_landing_proxy_id"] = nil
} else {
m["after_landing_proxy_id"] = c.AfterLandingProxyID.MustGet()
}
}
if c.AfterLandingPageRedirectURL.IsSpecified() {
if c.AfterLandingPageRedirectURL.IsNull() {
m["after_landing_page_redirect_url"] = ""
} else {
if v, err := c.AfterLandingPageRedirectURL.Get(); err == nil {
m["after_landing_page_redirect_url"] = v.String()
} else {
m["after_landing_page_redirect_url"] = ""
}
}
}
if c.EmailID.IsSpecified() {
if c.EmailID.IsNull() {
m["email_id"] = nil
} else {
m["email_id"] = c.EmailID.MustGet()
}
}
if c.SMTPConfigurationID.IsSpecified() {
if c.SMTPConfigurationID.IsNull() {
m["smtp_configuration_id"] = nil
} else {
m["smtp_configuration_id"] = c.SMTPConfigurationID.MustGet()
}
}
if c.APISenderID.IsSpecified() {
if c.APISenderID.IsNull() {
m["api_sender_id"] = nil
} else {
m["api_sender_id"] = c.APISenderID.MustGet()
}
}
if c.CompanyID.IsSpecified() {
if c.CompanyID.IsNull() {
m["company_id"] = nil
} else {
m["company_id"] = c.CompanyID.MustGet()
}
}
if v, err := c.URLIdentifierID.Get(); err == nil {
m["url_identifier_id"] = v
}
if v, err := c.StateIdentifierID.Get(); err == nil {
m["state_identifier_id"] = v
}
if c.URLPath.IsSpecified() {
if c.URLPath.IsNull() {
m["url_path"] = ""
} else {
if v, err := c.URLPath.Get(); err == nil {
m["url_path"] = v.String()
} else {
m["url_path"] = ""
}
}
}
_, errDomain := c.DomainID.Get()
_, errSMTP := c.SMTPConfigurationID.Get()
_, errAPISender := c.APISenderID.Get()
_, errEmail := c.EmailID.Get()
_, errLandingPage := c.LandingPageID.Get()
_, errLandingProxy := c.LandingProxyID.Get()
// landing page is required (either page or proxy)
hasLanding := errLandingPage == nil || errLandingProxy == nil
m["is_usable"] = errDomain == nil &&
errEmail == nil &&
hasLanding &&
(errSMTP == nil || errAPISender == nil)
return m
}