package db import ( "strings" mongoimpl "git.sendico.io/tech/sxchnge/bff/pkg/db/internal/mongo" "git.sendico.io/tech/sxchnge/bff/pkg/merrors" "git.sendico.io/tech/sxchnge/bff/pkg/model" "github.com/go-viper/mapstructure/v2" "go.uber.org/zap" ) type DBDriver string const ( Mongo DBDriver = "mongodb" ) type Config = model.DriverConfig[DBDriver] // PermissionsDatabaseConfig is the explicit public contract for services that // read authorization metadata from the central permissions database. type PermissionsDatabaseConfig Config // AsDatabaseConfig converts the permissions DB contract to the existing DB // config type without duplicating driver parsing or connection behavior. func (c *PermissionsDatabaseConfig) AsDatabaseConfig() *Config { if c == nil { return nil } converted := Config(*c) return &converted } // ResolvedDatabaseName identifies the database selected by a DB config. type ResolvedDatabaseName struct { Name string Source string } // ResolveDatabaseName resolves only the database name portion of a DB config. // It intentionally mirrors the existing env-over-literal behavior and leaves // low-level connection validation to the normal DB connection path. func ResolveDatabaseName(config *Config) ResolvedDatabaseName { if config == nil { return ResolvedDatabaseName{} } return resolveDatabaseName(config) } // ResolvePermissionsDatabaseName resolves only the database name portion of a // permissions DB config. func ResolvePermissionsDatabaseName(config *PermissionsDatabaseConfig) ResolvedDatabaseName { return ResolveDatabaseName(config.AsDatabaseConfig()) } // ValidateSplitDomainPermissionsDatabase verifies the generic split-domain // invariant for services that must keep authorization metadata separate from // domain storage. func ValidateSplitDomainPermissionsDatabase(service string, domain *Config, permissions *PermissionsDatabaseConfig) (ResolvedDatabaseName, ResolvedDatabaseName, error) { domainName := ResolveDatabaseName(domain) permissionsName := ResolvePermissionsDatabaseName(permissions) if permissions == nil { return domainName, permissionsName, merrors.InvalidArgument(service+" permissions_database configuration is required", "permissions_database") } if strings.TrimSpace(domainName.Name) == "" { return domainName, permissionsName, merrors.InvalidArgument(service+" domain database name is required for split-domain validation", "database") } if strings.TrimSpace(permissionsName.Name) == "" { return domainName, permissionsName, merrors.InvalidArgument(service+" permissions database name is required for split-domain validation", "permissions_database") } if domainName.Name == permissionsName.Name { return domainName, permissionsName, merrors.InvalidArgument(service+" permissions_database must resolve to a different database than the domain database", "permissions_database") } return domainName, permissionsName, nil } func resolveDatabaseName(config *Config) ResolvedDatabaseName { if config == nil || config.Driver != Mongo { return ResolvedDatabaseName{} } var mongoConfig mongoimpl.Config if err := mapstructure.Decode(config.Settings, &mongoConfig); err != nil { return ResolvedDatabaseName{} } dbSettings := mongoimpl.Config2DBSettings(zap.NewNop(), &mongoConfig) source := "" if mongoConfig.DatabaseEnv != nil { source = "env:" + *mongoConfig.DatabaseEnv } else if mongoConfig.Database != nil { source = "settings.database" } if source != "" { return ResolvedDatabaseName{ Name: dbSettings.Database, Source: source, } } return ResolvedDatabaseName{} }