Files
phishingclub/backend/model/domain.go
Ronni Skansing 3855e6d39b self signed certificates
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2025-11-04 20:00:59 +01:00

245 lines
7.9 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"
)
type Domain struct {
ID nullable.Nullable[uuid.UUID] `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
Name nullable.Nullable[vo.String255] `json:"name"`
Type nullable.Nullable[vo.String32] `json:"type"` // "regular" or "proxy"
ProxyTargetDomain nullable.Nullable[vo.OptionalString255] `json:"proxyTargetDomain"` // target URL for proxy (can be full URL or domain)
HostWebsite nullable.Nullable[bool] `json:"hostWebsite"`
ManagedTLS nullable.Nullable[bool] `json:"managedTLS"`
OwnManagedTLS nullable.Nullable[bool] `json:"ownManagedTLS"`
SelfSignedTLS nullable.Nullable[bool] `json:"selfSignedTLS"`
// private key
OwnManagedTLSKey nullable.Nullable[string] `json:"ownManagedTLSKey"`
// cert
OwnManagedTLSPem nullable.Nullable[string] `json:"ownManagedTLSPem"`
PageContent nullable.Nullable[vo.OptionalString1MB] `json:"pageContent"`
PageNotFoundContent nullable.Nullable[vo.OptionalString1MB] `json:"pageNotFoundContent"`
RedirectURL nullable.Nullable[vo.OptionalString1024] `json:"redirectURL"`
CompanyID nullable.Nullable[uuid.UUID] `json:"companyID"`
ProxyID nullable.Nullable[uuid.UUID] `json:"proxyID"`
Company *Company `json:"company"`
}
// Validate checks if the Domain configuration with a valid state
func (d *Domain) Validate() error {
if err := validate.NullableFieldRequired("name", d.Name); err != nil {
return err
}
// set default type if not specified
if !d.Type.IsSpecified() {
d.Type.Set(*vo.NewString32Must("regular"))
}
domainType, err := d.Type.Get()
if err != nil {
return validate.WrapErrorWithField(errors.New("type is required"), "type")
}
// validate type is either "regular" or "proxy"
if domainType.String() != "regular" && domainType.String() != "proxy" {
return validate.WrapErrorWithField(errors.New("type must be 'regular' or 'proxy'"), "type")
}
if domainType.String() == "proxy" {
// proxy domains require proxyTargetDomain
if err := validate.NullableFieldRequired("proxyTargetDomain", d.ProxyTargetDomain); err != nil {
return err
}
// proxy domains don't need page content validation
} else {
// regular domains need standard validation
if err := validate.NullableFieldRequired("hostWebsite", d.HostWebsite); err != nil {
return err
}
if err := validate.NullableFieldRequired("managedTLS", d.ManagedTLS); err != nil {
return err
}
if err := validate.NullableFieldRequired("pageContent", d.PageContent); err != nil {
return err
}
if err := validate.NullableFieldRequired("pageNotFoundContent", d.PageNotFoundContent); err != nil {
return err
}
if err := validate.NullableFieldRequired("redirectURL", d.RedirectURL); err != nil {
return err
}
}
//
//
ownManagedTLS, err := d.OwnManagedTLS.Get()
ownManagedTLSSet := err == nil && ownManagedTLS
selfSignedTLS, err := d.SelfSignedTLS.Get()
selfSignedTLSSet := err == nil && selfSignedTLS
managedTLS, err := d.ManagedTLS.Get()
managedTLSSet := err == nil && managedTLS
// count how many TLS options are enabled
tlsCount := 0
if managedTLSSet {
tlsCount++
}
if ownManagedTLSSet {
tlsCount++
}
if selfSignedTLSSet {
tlsCount++
}
// only one TLS option can be enabled at a time
if tlsCount > 1 {
return errs.NewValidationError(errors.New(
"Domain TLS can only have one option enabled: managed, own managed, or self-signed",
))
}
if ownManagedTLS {
// handle own managed ManagedTLS
ownManagedTLSKey, err := d.OwnManagedTLSKey.Get()
ownManagedTLSPem, err := d.OwnManagedTLSPem.Get()
ownManagedTLSKeyIsSet := err == nil && len(ownManagedTLSKey) > 0
ownManagedTLSPemIsSet := err == nil && len(ownManagedTLSPem) > 0
// both must be set, not one of
if (ownManagedTLSKeyIsSet && !ownManagedTLSPemIsSet) ||
(!ownManagedTLSKeyIsSet && ownManagedTLSPemIsSet) {
return errs.NewValidationError(errors.New(
"Own managed TLS requires a private key (.key) and a certificate (.pem)",
))
}
}
/*
// TODO hostWebsite vs redirectURL are mutually exclusive
hostWebsite := d.HostWebsite.MustGet()
redirectURL := d.RedirectURL.MustGet()
redirectURLLen := len(redirectURL.String())
if hostWebsite && redirectURLLen > 0 {
return validate.WrapErrorWithField(
errors.New("both can not be set"),
"Host website and redirect url",
)
} */
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 (d *Domain) ToDBMap() map[string]any {
m := map[string]any{}
if d.Name.IsSpecified() {
m["name"] = nil
if name, err := d.Name.Get(); err == nil {
m["name"] = name.String()
}
}
if d.Type.IsSpecified() {
m["type"] = "regular"
if domainType, err := d.Type.Get(); err == nil {
m["type"] = domainType.String()
}
}
if d.ProxyTargetDomain.IsSpecified() {
m["proxy_target_domain"] = nil
if proxyTargetDomain, err := d.ProxyTargetDomain.Get(); err == nil {
m["proxy_target_domain"] = proxyTargetDomain.String()
}
}
if d.HostWebsite.IsSpecified() {
m["host_website"] = nil
if hostWebsite, err := d.HostWebsite.Get(); err == nil {
m["host_website"] = hostWebsite
}
m["redirect_url"] = ""
}
if d.RedirectURL.IsSpecified() {
m["redirect_url"] = nil
if redirectURL, err := d.RedirectURL.Get(); err == nil {
m["redirect_url"] = redirectURL.String()
}
}
if d.PageContent.IsSpecified() {
m["page_content"] = nil
if staticPage, err := d.PageContent.Get(); err == nil {
m["page_content"] = staticPage.String()
}
}
if d.PageNotFoundContent.IsSpecified() {
m["page_not_found_content"] = nil
if staticNotFound, err := d.PageNotFoundContent.Get(); err == nil {
m["page_not_found_content"] = staticNotFound.String()
}
}
if d.CompanyID.IsSpecified() {
if d.CompanyID.IsNull() {
m["company_id"] = nil
} else {
m["company_id"] = d.CompanyID.MustGet()
}
}
if d.ManagedTLS.IsSpecified() {
m["managed_tls_certs"] = false
if d.ManagedTLS.IsNull() {
m["managed_tls_certs"] = nil
} else {
m["managed_tls_certs"] = d.ManagedTLS.MustGet()
}
}
if d.OwnManagedTLS.IsSpecified() {
m["own_managed_tls"] = false
if d.OwnManagedTLS.IsNull() {
m["own_managed_tls"] = nil
} else {
m["own_managed_tls"] = d.OwnManagedTLS.MustGet()
}
}
if d.SelfSignedTLS.IsSpecified() {
m["self_signed_tls"] = false
if d.SelfSignedTLS.IsNull() {
m["self_signed_tls"] = nil
} else {
m["self_signed_tls"] = d.SelfSignedTLS.MustGet()
}
}
if d.ProxyID.IsSpecified() {
if d.ProxyID.IsNull() {
m["proxy_id"] = nil
} else {
m["proxy_id"] = d.ProxyID.MustGet()
}
}
return m
}
// DomainOverview is a subset of the domain as used as read-only
type DomainOverview struct {
ID uuid.UUID `json:"id,omitempty"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
Name string `json:"name"`
Type string `json:"type"`
ProxyTargetDomain string `json:"proxyTargetDomain"`
HostWebsite bool `json:"hostWebsite"`
ManagedTLS bool `json:"managedTLS"`
OwnManagedTLS bool `json:"ownManagedTLS"`
SelfSignedTLS bool `json:"selfSignedTLS"`
RedirectURL string `json:"redirectURL"`
CompanyID *uuid.UUID `json:"companyID"`
ProxyID *uuid.UUID `json:"proxyID"`
}