mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 16:26:38 +00:00
230 lines
7.2 KiB
Go
230 lines
7.2 KiB
Go
package manager
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/netbirdio/netbird/management/internals/modules/zones"
|
|
"github.com/netbirdio/netbird/management/server/account"
|
|
"github.com/netbirdio/netbird/management/server/activity"
|
|
"github.com/netbirdio/netbird/management/server/permissions"
|
|
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
|
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
|
"github.com/netbirdio/netbird/management/server/store"
|
|
"github.com/netbirdio/netbird/shared/management/status"
|
|
)
|
|
|
|
type managerImpl struct {
|
|
store store.Store
|
|
accountManager account.Manager
|
|
permissionsManager permissions.Manager
|
|
dnsDomain string
|
|
}
|
|
|
|
func NewManager(store store.Store, accountManager account.Manager, permissionsManager permissions.Manager, dnsDomain string) zones.Manager {
|
|
return &managerImpl{
|
|
store: store,
|
|
accountManager: accountManager,
|
|
permissionsManager: permissionsManager,
|
|
dnsDomain: dnsDomain,
|
|
}
|
|
}
|
|
|
|
func (m *managerImpl) GetAllZones(ctx context.Context, accountID, userID string) ([]*zones.Zone, error) {
|
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Dns, operations.Read)
|
|
if err != nil {
|
|
return nil, status.NewPermissionValidationError(err)
|
|
}
|
|
if !ok {
|
|
return nil, status.NewPermissionDeniedError()
|
|
}
|
|
|
|
return m.store.GetAccountZones(ctx, store.LockingStrengthNone, accountID)
|
|
}
|
|
|
|
func (m *managerImpl) GetZone(ctx context.Context, accountID, userID, zoneID string) (*zones.Zone, error) {
|
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Dns, operations.Read)
|
|
if err != nil {
|
|
return nil, status.NewPermissionValidationError(err)
|
|
}
|
|
if !ok {
|
|
return nil, status.NewPermissionDeniedError()
|
|
}
|
|
|
|
return m.store.GetZoneByID(ctx, store.LockingStrengthNone, accountID, zoneID)
|
|
}
|
|
|
|
func (m *managerImpl) CreateZone(ctx context.Context, accountID, userID string, zone *zones.Zone) (*zones.Zone, error) {
|
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Dns, operations.Create)
|
|
if err != nil {
|
|
return nil, status.NewPermissionValidationError(err)
|
|
}
|
|
if !ok {
|
|
return nil, status.NewPermissionDeniedError()
|
|
}
|
|
|
|
if err = m.validateZoneDomainConflict(ctx, accountID, zone.Domain); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
zone = zones.NewZone(accountID, zone.Name, zone.Domain, zone.Enabled, zone.EnableSearchDomain, zone.DistributionGroups)
|
|
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
|
existingZone, err := transaction.GetZoneByDomain(ctx, accountID, zone.Domain)
|
|
if err != nil {
|
|
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
|
|
return fmt.Errorf("failed to check existing zone: %w", err)
|
|
}
|
|
}
|
|
if existingZone != nil {
|
|
return status.Errorf(status.AlreadyExists, "zone with domain %s already exists", zone.Domain)
|
|
}
|
|
|
|
for _, groupID := range zone.DistributionGroups {
|
|
_, err = transaction.GetGroupByID(ctx, store.LockingStrengthNone, accountID, groupID)
|
|
if err != nil {
|
|
return status.Errorf(status.InvalidArgument, "%s", err.Error())
|
|
}
|
|
}
|
|
|
|
if err = transaction.CreateZone(ctx, zone); err != nil {
|
|
return fmt.Errorf("failed to create zone: %w", err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m.accountManager.StoreEvent(ctx, userID, zone.ID, accountID, activity.DNSZoneCreated, zone.EventMeta())
|
|
|
|
return zone, nil
|
|
}
|
|
|
|
func (m *managerImpl) UpdateZone(ctx context.Context, accountID, userID string, updatedZone *zones.Zone) (*zones.Zone, error) {
|
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Dns, operations.Update)
|
|
if err != nil {
|
|
return nil, status.NewPermissionValidationError(err)
|
|
}
|
|
if !ok {
|
|
return nil, status.NewPermissionDeniedError()
|
|
}
|
|
|
|
zone, err := m.store.GetZoneByID(ctx, store.LockingStrengthUpdate, accountID, updatedZone.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get zone: %w", err)
|
|
}
|
|
|
|
if zone.Domain != updatedZone.Domain {
|
|
return nil, status.Errorf(status.InvalidArgument, "zone domain cannot be updated")
|
|
}
|
|
|
|
zone.Name = updatedZone.Name
|
|
zone.Enabled = updatedZone.Enabled
|
|
zone.EnableSearchDomain = updatedZone.EnableSearchDomain
|
|
zone.DistributionGroups = updatedZone.DistributionGroups
|
|
|
|
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
|
for _, groupID := range zone.DistributionGroups {
|
|
_, err = transaction.GetGroupByID(ctx, store.LockingStrengthNone, accountID, groupID)
|
|
if err != nil {
|
|
return status.Errorf(status.InvalidArgument, "%s", err.Error())
|
|
}
|
|
}
|
|
|
|
if err = transaction.UpdateZone(ctx, zone); err != nil {
|
|
return fmt.Errorf("failed to update zone: %w", err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m.accountManager.StoreEvent(ctx, userID, zone.ID, accountID, activity.DNSZoneUpdated, zone.EventMeta())
|
|
|
|
go m.accountManager.UpdateAccountPeers(ctx, accountID)
|
|
|
|
return zone, nil
|
|
}
|
|
|
|
func (m *managerImpl) DeleteZone(ctx context.Context, accountID, userID, zoneID string) error {
|
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Dns, operations.Delete)
|
|
if err != nil {
|
|
return status.NewPermissionValidationError(err)
|
|
}
|
|
if !ok {
|
|
return status.NewPermissionDeniedError()
|
|
}
|
|
|
|
zone, err := m.store.GetZoneByID(ctx, store.LockingStrengthUpdate, accountID, zoneID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get zone: %w", err)
|
|
}
|
|
|
|
var eventsToStore []func()
|
|
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
|
records, err := transaction.GetZoneDNSRecords(ctx, store.LockingStrengthNone, accountID, zoneID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get records: %w", err)
|
|
}
|
|
|
|
err = transaction.DeleteZoneDNSRecords(ctx, accountID, zoneID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete zone dns records: %w", err)
|
|
}
|
|
|
|
err = transaction.DeleteZone(ctx, accountID, zoneID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete zone: %w", err)
|
|
}
|
|
|
|
err = transaction.IncrementNetworkSerial(ctx, accountID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to increment network serial: %w", err)
|
|
}
|
|
|
|
for _, record := range records {
|
|
eventsToStore = append(eventsToStore, func() {
|
|
meta := record.EventMeta(zone.ID, zone.Name)
|
|
m.accountManager.StoreEvent(ctx, userID, record.ID, accountID, activity.DNSRecordDeleted, meta)
|
|
})
|
|
}
|
|
|
|
eventsToStore = append(eventsToStore, func() {
|
|
m.accountManager.StoreEvent(ctx, userID, zoneID, accountID, activity.DNSZoneDeleted, zone.EventMeta())
|
|
})
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, event := range eventsToStore {
|
|
event()
|
|
}
|
|
|
|
go m.accountManager.UpdateAccountPeers(ctx, accountID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *managerImpl) validateZoneDomainConflict(ctx context.Context, accountID, domain string) error {
|
|
if m.dnsDomain != "" && m.dnsDomain == domain {
|
|
return status.Errorf(status.InvalidArgument, "zone domain %s conflicts with peer DNS domain", domain)
|
|
}
|
|
|
|
settings, err := m.store.GetAccountSettings(ctx, store.LockingStrengthNone, accountID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if settings.DNSDomain != "" && settings.DNSDomain == domain {
|
|
return status.Errorf(status.InvalidArgument, "zone domain %s conflicts with peer DNS domain", domain)
|
|
}
|
|
|
|
return nil
|
|
}
|