Skip to content
Open
1 change: 1 addition & 0 deletions common/const_var.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ const (
APPLICATION_TYPE = "applicationType"
ACCOUNT_ID = "accountId"
ACCOUNT_HASH = "accountHash"
ACCOUNT_PRODUCTS = "accountProducts"
CONFIG_SET_HASH = "configSetHash"
SYNDICATION_PARTNER = "SyndicationPartner"
MAC = "mac"
Expand Down
1 change: 1 addition & 0 deletions config/sample_xconfwebconfig.conf
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ xconfwebconfig {
enable_rfc_penetration_metrics = false // Enable RFC penetration metrics
enable_device_service = false // Enable device service integration
enable_account_service = false // Enable account service integration
enable_account_data_service = true
enable_tagging_service = false // Enable tagging service integration
enable_device_db_lookup = false // Enable device DB lookup
enable_group_service = false // Enable group service integration
Expand Down
21 changes: 10 additions & 11 deletions dataapi/dataapi_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,17 @@ func GetPartnerFromAccountServiceByHostMac(ws *xhttp.XconfServer, macAddress str
}

var partnerId string
if Xc.EnableAccountService {
var accountObject xhttp.AccountServiceDevices
var err error
if util.IsValidMacAddress(macAddress) {
accountObject, err = ws.AccountServiceConnector.GetDevices(common.HOST_MAC_PARAM, macAddress, satToken, fields)
}
if err != nil {
log.WithFields(log.Fields{"error": err}).Error("Error getting account information")
} else {
partnerId = strings.ToUpper(accountObject.DeviceData.Partner)
}
var accountObject xhttp.AccountServiceDevices
var err error
if util.IsValidMacAddress(macAddress) {
accountObject, err = ws.AccountServiceConnector.GetDevices(common.HOST_MAC_PARAM, macAddress, satToken, fields)
}
if err != nil {
log.WithFields(log.Fields{"error": err}).Error("Error getting account information")
} else {
partnerId = strings.ToUpper(accountObject.DeviceData.Partner)
}

return partnerId
}

Expand Down
49 changes: 45 additions & 4 deletions dataapi/estb_firmware_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,53 @@ func AddEstbFirmwareContext(ws *xhttp.XconfServer, r *http.Request, contextMap m
return err
}
satToken := localToken.Token
if Xc.EnableAccountService {
if util.IsUnknownValue(contextMap[common.PARTNER_ID]) {
partnerId := GetPartnerFromAccountServiceByHostMac(ws, contextMap[common.ESTB_MAC], satToken, fields)
if partnerId != "" {
contextMap[common.PARTNER_ID] = partnerId
}
}
} else if Xc.EnableAccountDataService {
// Only call XAC/ADA if partner is unknown or empty
if util.IsUnknownValue(contextMap[common.PARTNER_ID]) || contextMap[common.PARTNER_ID] == "" {
macValue := contextMap[common.ESTB_MAC]
if macValue != "" {
// Remove colons from MAC for XAC call
macValueWithoutColons := strings.ReplaceAll(macValue, ":", "")

xAccountId, err := ws.GroupServiceConnector.GetAccountIdData(macValueWithoutColons, fields)
if err != nil {
log.WithFields(fields).Warn(fmt.Sprintf("XAC call failed for MAC='%s': %v", macValue, err))
}

if util.IsUnknownValue(contextMap[common.PARTNER_ID]) {
partnerId := GetPartnerFromAccountServiceByHostMac(ws, contextMap[common.ESTB_MAC], satToken, fields)
if partnerId != "" {
contextMap[common.PARTNER_ID] = partnerId
if xAccountId != nil && xAccountId.GetAccountId() != "" {
accountId := xAccountId.GetAccountId()
accountProducts, err := ws.GroupServiceConnector.GetAccountProducts(accountId, fields)
if err != nil {
log.WithFields(fields).Warn(fmt.Sprintf("ADA call failed for AccountId='%s': %v", accountId, err))
} else {
if partner, ok := accountProducts["Partner"]; ok && partner != "" {
contextMap[common.PARTNER_ID] = partner
log.WithFields(fields).Info(fmt.Sprintf("Partner='%s' retrieved from ADA", partner))
}
}
}
}

// Fallback to old Account Service logic if XAC/ADA didn't return partner
if util.IsUnknownValue(contextMap[common.PARTNER_ID]) {
log.WithFields(fields).Info("Trying fallback Account Service for partner retrieval")
partnerId := GetPartnerFromAccountServiceByHostMac(ws, contextMap[common.ESTB_MAC], satToken, fields)
if partnerId != "" {
contextMap[common.PARTNER_ID] = partnerId
log.WithFields(fields).Info(fmt.Sprintf("Partner='%s' retrieved from fallback Account Service", partnerId))
}
}
}
} else {
//err both the service
log.Error("Both the Account Service calls have been disabled")
}
AddContextFromTaggingService(ws, contextMap, satToken, "", false, fields)
AddGroupServiceFTContext(Ws, common.ESTB_MAC, contextMap, true, fields)
Expand Down
152 changes: 150 additions & 2 deletions dataapi/feature_control_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,69 @@ func CompareHashWithXDAS(contextMap map[string]string, xdasHash string, tags []s
return calculatedHash == xdasHash, nil
}

// getPartnerFromAccountDataService attempts to retrieve account data using XAC → ADA flow
func getPartnerFromAccountDataService(ws *xhttp.XconfServer, contextMap map[string]string, fields log.Fields) (*PodData, *AccountServiceData) {
var podData *PodData
var td *AccountServiceData

// Try ECM MAC first, then fall back to ESTB MAC
var macAddress string
if util.IsValidMacAddress(contextMap[common.ESTB_MAC_ADDRESS]) {
macAddress = util.GetEcmMacAddress(util.AlphaNumericMacAddress(strings.TrimSpace(contextMap[common.ESTB_MAC_ADDRESS])))
} else if util.IsValidMacAddress(contextMap[common.ECM_MAC_ADDRESS]) {
macAddress = contextMap[common.ECM_MAC_ADDRESS]
}

// Remove colons from MAC for XAC call
macValueWithoutColons := strings.ReplaceAll(macAddress, ":", "")

// Call XAC to get AccountId
xAccountId, err := ws.GroupServiceConnector.GetAccountIdData(macValueWithoutColons, fields)
if err != nil {
log.WithFields(fields).Warn(fmt.Sprintf("XAC call failed for MAC='%s': %v", macValueWithoutColons, err))
return nil, nil
}

if xAccountId == nil || xAccountId.GetAccountId() == "" {
return nil, nil
}

accountId := xAccountId.GetAccountId()

// Call ADA to get account products
accountProducts, err := ws.GroupServiceConnector.GetAccountProducts(accountId, fields)
if err != nil {
log.WithFields(fields).Warn(fmt.Sprintf("ADA call failed for AccountId='%s': %v", accountId, err))
return nil, nil
}

// Extract Partner and TimeZone from ADA response
var partnerId, timeZone string
if partner, ok := accountProducts["Partner"]; ok && partner != "" {
partnerId = strings.ToUpper(partner)
log.WithFields(fields).Info(fmt.Sprintf("Partner='%s' retrieved from ADA for pods", partnerId))
}
if tz, ok := accountProducts["TimeZone"]; ok && tz != "" {
timeZone = tz
log.WithFields(fields).Info(fmt.Sprintf("TimeZone='%s' retrieved from ADA for pods", timeZone))
}

// Create PodData and AccountServiceData with retrieved information
podData = &PodData{
AccountId: accountId,
PartnerId: partnerId,
TimeZone: timeZone,
}

td = &AccountServiceData{
AccountId: accountId,
PartnerId: partnerId,
TimeZone: timeZone,
}

return podData, td
}

func AddContextForPods(ws *xhttp.XconfServer, contextMap map[string]string, satToken string, vargs ...log.Fields) (*PodData, *AccountServiceData) {
var fields log.Fields
var podData *PodData
Expand All @@ -130,6 +193,17 @@ func AddContextForPods(ws *xhttp.XconfServer, contextMap map[string]string, satT
tfields := common.FilterLogFields(fields)

if Xc.EnableMacAccountServiceCall && strings.HasPrefix(strings.ToUpper(contextMap[common.SERIAL_NUM]), Xc.AccountServiceMacPrefix) {
if Xc.EnableAccountDataService {
podData, td = getPartnerFromAccountDataService(ws, contextMap, fields)
if podData != nil && podData.AccountId != "" {
if util.IsUnknownValue(contextMap[common.ACCOUNT_ID]) && podData.AccountId != "" {
contextMap[common.ACCOUNT_ID] = podData.AccountId
}
return podData, td
}
}

// Fallback: Old AccountService logic
AccountServiceDeviceObject, err := ws.AccountServiceConnector.GetDevices(common.SERIAL_NUMBER_PARAM, contextMap[common.SERIAL_NUM], satToken, fields)
if err != nil {
log.WithFields(log.Fields{"error": err}).Errorf("Error getting AccountService device information: serialNum=%s", contextMap[common.SERIAL_NUM])
Expand Down Expand Up @@ -167,6 +241,18 @@ func AddContextForPods(ws *xhttp.XconfServer, contextMap map[string]string, satT
podData.TimeZone = AccountServiceAccountObject.AccountData.AccountAttributes.TimeZone
log.WithFields(tfields).Infof("Successfully got AccountService information for XLE device: accountId=%s, serialNum=%s", AccountServiceDeviceObject.DeviceData.ServiceAccountUri, contextMap[common.SERIAL_NUM])
} else if Xc.EnableDeviceDBLookup && contextMap[common.SERIAL_NUM] != "" && !strings.HasPrefix(contextMap[common.MODEL], GR_PREFIX) {
// Try XAC → ADA flow first if enabled
if Xc.EnableAccountDataService {
podData, td = getPartnerFromAccountDataService(ws, contextMap, fields)
if podData != nil && podData.AccountId != "" {
if util.IsUnknownValue(contextMap[common.ACCOUNT_ID]) && podData.AccountId != "" {
contextMap[common.ACCOUNT_ID] = podData.AccountId
}
return podData, td
}
}

// Fallback: Old AccountService logic
ecmMacAddress, err := ws.GetEcmMacFromPodTable(contextMap[common.SERIAL_NUM])
if err != nil {
log.WithFields(log.Fields{"error": err}).Errorf("Error looking up pod information from odp db: serialNum=%s", contextMap[common.SERIAL_NUM])
Expand Down Expand Up @@ -213,6 +299,18 @@ func AddContextForPods(ws *xhttp.XconfServer, contextMap map[string]string, satT
}
podData.TimeZone = accountServiceAccountObject.AccountData.AccountAttributes.TimeZone
} else if Xc.EnableDeviceService && contextMap[common.SERIAL_NUM] != "" && !strings.HasPrefix(contextMap[common.MODEL], GR_PREFIX) {
// Try XAC → ADA flow first if enabled
if Xc.EnableAccountDataService {
podData, td = getPartnerFromAccountDataService(ws, contextMap, fields)
if podData != nil && podData.AccountId != "" {
if util.IsUnknownValue(contextMap[common.ACCOUNT_ID]) && podData.AccountId != "" {
contextMap[common.ACCOUNT_ID] = podData.AccountId
}
return podData, td
}
}

// Fallback: Old Device Service (Titan) logic
deviceServiceObject, err := ws.DeviceServiceConnector.GetMeshPodAccountBySerialNum(contextMap[common.SERIAL_NUM], fields)
if err != nil {
log.WithFields(log.Fields{"error": err}).Errorf("Error getting Device Service information: serialNum=%s", contextMap[common.SERIAL_NUM])
Expand All @@ -234,16 +332,16 @@ func AddContextForPods(ws *xhttp.XconfServer, contextMap map[string]string, satT

func AddFeatureControlContextFromAccountService(ws *xhttp.XconfServer, contextMap map[string]string, satToken string, vargs ...log.Fields) *AccountServiceData {
var td *AccountServiceData

var accountId string
var fields log.Fields
if len(vargs) > 0 {
fields = vargs[0]
} else {
fields = log.Fields{}
}
var err error
if Xc.EnableAccountService {
var accountServiceObject xhttp.AccountServiceDevices
var err error
if util.IsValidMacAddress(contextMap[common.ESTB_MAC_ADDRESS]) {
accountServiceObject, err = ws.AccountServiceConnector.GetDevices(common.HOST_MAC_PARAM, contextMap[common.ESTB_MAC_ADDRESS], satToken, fields)
if err == nil {
Expand Down Expand Up @@ -297,6 +395,56 @@ func AddFeatureControlContextFromAccountService(ws *xhttp.XconfServer, contextMa
}
}
}
} else if Xc.EnableAccountDataService {
if util.IsValidMacAddress(contextMap[common.ESTB_MAC_ADDRESS]) || util.IsValidMacAddress(contextMap[common.ECM_MAC_ADDRESS]) {
var macAddress string
if contextMap[common.ESTB_MAC_ADDRESS] != "" {
macAddress = util.GetEcmMacAddress(util.AlphaNumericMacAddress(strings.TrimSpace(contextMap[common.ESTB_MAC_ADDRESS])))
} else {
macAddress = contextMap[common.ECM_MAC_ADDRESS]
}
// Normalize MAC (remove colons) before calling XAC
macValue := strings.ReplaceAll(macAddress, ":", "")
xboAccount, err := ws.GroupServiceConnector.GetAccountIdData(macValue, fields)
if err != nil {
log.WithFields(log.Fields{"error": err}).Error("Error getting accountId information")
xhttp.IncreaseAccountServiceEmptyResponseCounter(contextMap[common.MODEL])
return td
}
if xboAccount != nil && xboAccount.GetAccountId() != "" {
accountId = xboAccount.GetAccountId()
contextMap[common.ACCOUNT_ID] = accountId
} else {
xhttp.IncreaseAccountServiceEmptyResponseCounter(contextMap[common.MODEL])
return td
}

td = &AccountServiceData{
AccountId: accountId,
}
accountProducts, err := ws.GroupServiceConnector.GetAccountProducts(accountId, fields)
if err != nil {
log.WithFields(log.Fields{"error": err}).Error("Error getting accountProducts information")
} else {
if partner, ok := accountProducts["Partner"]; ok && partner != "" {
contextMap[common.PARTNER_ID] = strings.ToUpper(partner)
}

contextMap[common.ACCOUNT_HASH] = util.CalculateHash(accountId)

if accountProductsVal, ok := accountProducts["AccountProducts"]; ok {
contextMap[common.ACCOUNT_PRODUCTS] = accountProductsVal
}

if countryCode, ok := accountProducts["CountryCode"]; ok {
contextMap[common.COUNTRY_CODE] = countryCode
}

}
}
} else {
//error both service not enabled
log.WithFields(log.Fields{"error": err}).Error("Both the Account Service calls have been disabled")
}
return td
}
Expand Down
2 changes: 2 additions & 0 deletions dataapi/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type XconfConfigs struct {
EnableMacAccountServiceCall bool
AccountServiceMacPrefix string
EnableAccountService bool
EnableAccountDataService bool
EnableTaggingService bool
EnableTaggingServiceRFC bool
IPv4NetworkMaskPrefixLength int32
Expand Down Expand Up @@ -298,6 +299,7 @@ func GetXconfConfigs(conf *conf.Config) *XconfConfigs {
EnableDeviceDBLookup: conf.GetBoolean("xconfwebconfig.xconf.enable_device_db_lookup"),
EnableMacAccountServiceCall: conf.GetBoolean("xconfwebconfig.xconf.enable_mac_accountservice_call"),
EnableAccountService: conf.GetBoolean("xconfwebconfig.xconf.enable_account_service"),
EnableAccountDataService: conf.GetBoolean("xconfwebconfig.xconf.enable_account_data_service"),
EnableTaggingService: conf.GetBoolean("xconfwebconfig.xconf.enable_tagging_service"),
EnableTaggingServiceRFC: conf.GetBoolean("xconfwebconfig.xconf.enable_tagging_service_rfc"),
ReturnAccountId: conf.GetBoolean("xconfwebconfig.xconf.return_account_id"),
Expand Down
36 changes: 36 additions & 0 deletions http/group_service_connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package http
import (
"crypto/tls"
"fmt"
"net/http"
"reflect"

conversion "github.com/rdkcentral/xconfwebconfig/protobuf"
Expand All @@ -35,6 +36,8 @@ const (
getRfcPrecookUrlTemplate = "%s/v2/xd/%s"
getHashesUrlTemplate = "%s/v2/ft/%s"
getSecurityTokenUrlTemplate = "%s/v2/st/%s"
getAccountIdTemplate = "%s/v2/xac/%s"
getAccountProductsTemplate = "%s/v2/ada/%s"
)

type GroupServiceConnector interface {
Expand All @@ -47,6 +50,8 @@ type GroupServiceConnector interface {
CreateListFromGroupServiceProto(cpeGroup *conversion.CpeGroup) []string
GetFeatureTagsHashedItems(name string, fields log.Fields) (map[string]string, error)
GetSecurityTokenInfo(securityIdentifier string, fields log.Fields) (map[string]string, error)
GetAccountIdData(ecmMac string, fields log.Fields) (*conversion.XBOAccount, error)
GetAccountProducts(name string, fields log.Fields) (map[string]string, error)
}

type DefaultGroupService struct {
Expand Down Expand Up @@ -171,3 +176,34 @@ func (c *DefaultGroupService) GetSecurityTokenInfo(securityIdentifier string, fi
}
return message.Fields, nil
}

func (c *DefaultGroupService) GetAccountIdData(ecmMac string, fields log.Fields) (*conversion.XBOAccount, error) {
url := fmt.Sprintf(getAccountIdTemplate, c.host, ecmMac)
rbytes, err := c.DoWithRetries(http.MethodGet, url, nil, nil, fields, groupServiceName)
if err != nil {
return nil, err
}

var xboAccount conversion.XBOAccount
err = proto.Unmarshal(rbytes, &xboAccount)
if err != nil {
return nil, err
}

return &xboAccount, nil
}

func (c *DefaultGroupService) GetAccountProducts(name string, fields log.Fields) (map[string]string, error) {
url := fmt.Sprintf(getAccountProductsTemplate, c.GroupServiceHost(), name)
rbytes, err := c.DoWithRetries(http.MethodGet, url, nil, nil, fields, groupServiceName)
if err != nil {
return nil, err
}
message := conversion.XdasHashes{}
message.ProtoMessage()
err = proto.Unmarshal(rbytes, &message)
if err != nil {
return nil, err
}
return message.Fields, nil
}
Loading