168 строки
5.6 KiB
Go
168 строки
5.6 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/base64"
|
|
|
|
"gorm.io/datatypes"
|
|
|
|
"rsgit.ru/rsmon/rsmon/app/models/concerns"
|
|
)
|
|
|
|
// NotificationCredential kinds stored in the database.
|
|
const (
|
|
CredentialKindSMTP = "smtp"
|
|
CredentialKindTelegram = "telegram"
|
|
)
|
|
|
|
// NotificationCredential stores per-method delivery credentials (SMTP login,
|
|
// Telegram bot token, etc.) encrypted at rest. Credentials are pushed to
|
|
// workers through the init/config websocket refresh (see docs/worker-protocol.md
|
|
// "Credentials Push").
|
|
type NotificationCredential struct {
|
|
concerns.Model
|
|
// AccountID is nil for platform-managed credentials and set for credentials
|
|
// owned by one customer account.
|
|
AccountID *int64 `gorm:"type:bigint REFERENCES accounts(id) ON DELETE CASCADE;index" json:"account_id,omitempty"`
|
|
Kind string `gorm:"not null" json:"kind"`
|
|
Name string `gorm:"not null" json:"name"`
|
|
|
|
// SMTP-specific
|
|
Server *string `json:"server,omitempty"`
|
|
Port *int `json:"port,omitempty"`
|
|
Login *string `json:"login,omitempty"`
|
|
FromName *string `json:"from_name,omitempty"`
|
|
FromAddr *string `json:"from_address,omitempty"`
|
|
InsecureSkipVerify bool `gorm:"default:false" json:"insecure_skip_verify"`
|
|
|
|
// Telegram-specific
|
|
BotName *string `json:"bot_name,omitempty"`
|
|
APIURL *string `json:"api_url,omitempty"`
|
|
WebhookToken string `gorm:"size:128" json:"webhook_token,omitempty"`
|
|
|
|
// SecretEnc holds the encrypted (or "plain:"-prefixed fallback) secret
|
|
// value — SMTP password or Telegram bot token. Decrypt via GetSecret.
|
|
SecretEnc string `gorm:"column:secret;type:text" json:"-"`
|
|
SecretMasked string `gorm:"-" json:"secret_masked,omitempty"`
|
|
|
|
Enabled *bool `gorm:"not null;default:true" json:"enabled"`
|
|
Meta datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"meta,omitempty"`
|
|
|
|
concerns.Timestamped
|
|
Audited
|
|
}
|
|
|
|
// SetSecret encrypts and stores the plaintext secret value.
|
|
func (c *NotificationCredential) SetSecret(plaintext string) error {
|
|
enc, err := encryptSecret(plaintext)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.SecretEnc = enc
|
|
return nil
|
|
}
|
|
|
|
// GetSecret decrypts and returns the secret value.
|
|
func (c *NotificationCredential) GetSecret() (string, error) {
|
|
return decryptSecret(c.SecretEnc)
|
|
}
|
|
|
|
// FillSecretMasked populates SecretMasked with a display-safe version of the credential secret.
|
|
func (c *NotificationCredential) FillSecretMasked() {
|
|
secret, err := c.GetSecret()
|
|
if err != nil {
|
|
return
|
|
}
|
|
c.SecretMasked = maskSecret(secret)
|
|
}
|
|
|
|
// EnsureWebhookToken creates the per-bot webhook URL token if it is missing.
|
|
func (c *NotificationCredential) EnsureWebhookToken() {
|
|
if c.WebhookToken != "" {
|
|
return
|
|
}
|
|
c.WebhookToken = base64.RawURLEncoding.EncodeToString(concerns.RandomToken(32))
|
|
}
|
|
|
|
func maskSecret(secret string) string {
|
|
if secret == "" {
|
|
return ""
|
|
}
|
|
if len(secret) == 1 {
|
|
return secret
|
|
}
|
|
return secret[:1] + "***" + secret[len(secret)-1:]
|
|
}
|
|
|
|
// TableName overrides the default table name.
|
|
func (NotificationCredential) TableName() string {
|
|
return "notification_credentials"
|
|
}
|
|
|
|
// AllNotificationCredentials returns all credentials ordered by kind and name.
|
|
func AllNotificationCredentials() ([]NotificationCredential, error) {
|
|
var creds []NotificationCredential
|
|
err := DB().Where("account_id IS NULL").Order("kind ASC, name ASC").Find(&creds).Error
|
|
for i := range creds {
|
|
creds[i].FillSecretMasked()
|
|
}
|
|
return creds, err
|
|
}
|
|
|
|
// AccountNotificationCredentials returns credentials owned by accountID.
|
|
func AccountNotificationCredentials(accountID int64) ([]NotificationCredential, error) {
|
|
var creds []NotificationCredential
|
|
err := DB().Where("account_id = ?", accountID).Order("kind ASC, name ASC").Find(&creds).Error
|
|
for i := range creds {
|
|
creds[i].FillSecretMasked()
|
|
}
|
|
return creds, err
|
|
}
|
|
|
|
// EnabledCredentialsByKind returns enabled credentials of the given kind.
|
|
func EnabledCredentialsByKind(kind string) ([]NotificationCredential, error) {
|
|
var creds []NotificationCredential
|
|
err := DB().Where("account_id IS NULL AND kind = ? AND enabled = ?", kind, true).Order("name ASC").Find(&creds).Error
|
|
return creds, err
|
|
}
|
|
|
|
// EnabledCredentialsByAccountAndKind returns only enabled credentials owned by
|
|
// accountID. It never falls back to platform credentials.
|
|
func EnabledCredentialsByAccountAndKind(accountID int64, kind string) ([]NotificationCredential, error) {
|
|
var creds []NotificationCredential
|
|
err := DB().Where("account_id = ? AND kind = ? AND enabled = ?", accountID, kind, true).Order("name ASC").Find(&creds).Error
|
|
return creds, err
|
|
}
|
|
|
|
// FindCredential returns a credential by id.
|
|
func FindCredential(id int64) (*NotificationCredential, error) {
|
|
var c NotificationCredential
|
|
if err := DB().First(&c, id).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
c.FillSecretMasked()
|
|
return &c, nil
|
|
}
|
|
|
|
// FindCredentialByName returns a credential by kind and name.
|
|
func FindCredentialByName(kind, name string) (*NotificationCredential, error) {
|
|
var c NotificationCredential
|
|
if err := DB().Where("kind = ? AND name = ?", kind, name).First(&c).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
// FindTelegramCredentialByWebhookToken returns an enabled Telegram credential by webhook token.
|
|
func FindTelegramCredentialByWebhookToken(token string) (*NotificationCredential, error) {
|
|
var c NotificationCredential
|
|
if err := DB().Where("kind = ? AND webhook_token = ? AND enabled = ?", CredentialKindTelegram, token, true).First(&c).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
// DeleteCredential removes a credential by id.
|
|
func DeleteCredential(id int64) error {
|
|
return DB().Delete(&NotificationCredential{}, id).Error
|
|
}
|