diff --git a/cmd/server/internal/handler.go b/cmd/server/internal/handler.go index 23f89b34..09fee44f 100644 --- a/cmd/server/internal/handler.go +++ b/cmd/server/internal/handler.go @@ -162,12 +162,17 @@ type tenantInfo struct { type serviceCredentials struct { XSAppName string `json:"xsappname"` SaasRegistryEnabled bool `json:"saasregistryenabled"` - Plan string `json:"plan"` UAA *struct { XSAppName string `json:"xsappname"` } `json:"uaa"` } +// Credentials with plan +type serviceMetaInfo struct { + Plan string `json:"plan"` + Credentials serviceCredentials `json:"credentials"` +} + type GetDependenciesAuthError struct{} func (err *GetDependenciesAuthError) Error() string { @@ -833,7 +838,7 @@ func (s *SubscriptionHandler) getSaasDetails(capApp *v1alpha1.CAPApplication, st info *v1alpha1.ServiceInfo ) if info, err = s.getServiceInfo(capApp, "saas-registry"); err == nil { - result, err = util.ReadServiceCredentialsFromSecret[util.SaasRegistryCredentials](info, capApp.Namespace, s.KubeClientset) + result, err = util.ReadServiceCredentialsFromSecret[util.SaasRegistryCredentials](info, capApp.Namespace, s.KubeClientset, false) } if err != nil { util.LogError(err, "SaaS Registry credentials could not be read. Exiting..", step, capApp, nil) @@ -852,7 +857,7 @@ func (s *SubscriptionHandler) getXSUAADetails(capApp *v1alpha1.CAPApplication, s if info == nil { err = fmt.Errorf("could not find service with class %s in CAPApplication %s.%s", "xsuaa", capApp.Namespace, capApp.Name) } else { - result, err = util.ReadServiceCredentialsFromSecret[util.XSUAACredentials](info, capApp.Namespace, s.KubeClientset) + result, err = util.ReadServiceCredentialsFromSecret[util.XSUAACredentials](info, capApp.Namespace, s.KubeClientset, false) } if err != nil { @@ -868,7 +873,7 @@ func (s *SubscriptionHandler) getSmsDetails(capApp *v1alpha1.CAPApplication, ste info *v1alpha1.ServiceInfo ) if info, err = s.getServiceInfo(capApp, "subscription-manager"); err == nil { - result, err = util.ReadServiceCredentialsFromSecret[util.SmsCredentials](info, capApp.Namespace, s.KubeClientset) + result, err = util.ReadServiceCredentialsFromSecret[util.SmsCredentials](info, capApp.Namespace, s.KubeClientset, false) } if err != nil { util.LogError(err, "SMS credentials could not be read. Exiting..", step, capApp, nil) @@ -1154,14 +1159,22 @@ func (c *serviceCredentials) xsAppName() string { } func (s *SubscriptionHandler) getServiceDependencies(capApp *v1alpha1.CAPApplication, service v1alpha1.ServiceInfo) map[string]string { - creds, err := util.ReadServiceCredentialsFromSecret[serviceCredentials](&service, capApp.Namespace, s.KubeClientset) + // Read credentials with metadata (as we need a check based on the plan + serviceCredInfo, err := util.ReadServiceCredentialsFromSecret[serviceMetaInfo](&service, capApp.Namespace, s.KubeClientset, true) if err != nil { util.LogError(err, "Failed to read secret for service", GetDependencies, capApp, nil, "service", service.Name, "secret", service.Secret) return nil } - if isServiceRelevantForDependencies(service, creds) { - if name := creds.xsAppName(); name != "" { + if isServiceRelevantForDependencies(service, serviceCredInfo) { + + if name := serviceCredInfo.Credentials.xsAppName(); name != "" { + if isSpecialDependency(service, serviceCredInfo) { + return map[string]string{ + "appName": service.Class, + "appId": name, + } + } return map[string]string{"xsappname": name} } } @@ -1169,21 +1182,26 @@ func (s *SubscriptionHandler) getServiceDependencies(capApp *v1alpha1.CAPApplica return nil } -func isServiceRelevantForDependencies(serviceInfo v1alpha1.ServiceInfo, creds *serviceCredentials) bool { +func isServiceRelevantForDependencies(serviceInfo v1alpha1.ServiceInfo, creds *serviceMetaInfo) bool { if serviceInfo.GetSubscriptionDependency() == v1alpha1.SubscriptionDependencyAlways { return true } if serviceInfo.GetSubscriptionDependency() == v1alpha1.SubscriptionDependencyAuto { - return serviceInfo.Class == "destination" || - serviceInfo.Class == "connectivity" || - (serviceInfo.Class == "auditlog" && creds.Plan == "oauth2") || - creds.SaasRegistryEnabled + return isSpecialDependency(serviceInfo, creds) || + creds.Credentials.SaasRegistryEnabled } return false } +// These services might need some special handling for now, until there is some clarity from BTP as to how saas-registry differentiates b/w xsappname and appId/appName dependencies. +func isSpecialDependency(serviceInfo v1alpha1.ServiceInfo, creds *serviceMetaInfo) bool { + return serviceInfo.Class == "destination" || + serviceInfo.Class == "connectivity" || + (serviceInfo.Class == "auditlog" && creds.Plan == "oauth2") +} + func (s *SubscriptionHandler) getDependencies(req *http.Request, subscriptionType subscriptionType) ([]byte, error) { var dependenciesArray []map[string]string diff --git a/cmd/server/internal/handler_test.go b/cmd/server/internal/handler_test.go index 226df1c3..a48a13e8 100644 --- a/cmd/server/internal/handler_test.go +++ b/cmd/server/internal/handler_test.go @@ -111,7 +111,7 @@ func createSecrets() []runtime.Object { "sburl": "internal.auth.service.local", "url": "https://app-domain.auth.service.local", "saasregistryenabled": true, - "uaa": {"xsappname": "appname!b15" }, + "uaa": {"xsappname": "saasappname!b15" }, "credential-type": "instance-secret" }`), }, @@ -130,7 +130,7 @@ func createSecrets() []runtime.Object { "sburl": "internal.auth.service.local", "url": "https://app-domain.auth.service.local", "saasregistryenabled": true, - "uaa": {"xsappname": "appname!b15" }, + "uaa": {"xsappname": "destappname!b15" }, "credential-type": "instance-secret" }`), }, @@ -149,7 +149,7 @@ func createSecrets() []runtime.Object { "sburl": "internal.auth.service.local", "url": "https://app-domain.auth.service.local", "saasregistryenabled": true, - "uaa": {"xsappname": "appname!b15" }, + "uaa": {"xsappname": "rtappname!b15" }, "credential-type": "instance-secret" }`), }, @@ -168,7 +168,7 @@ func createSecrets() []runtime.Object { "sburl": "internal.auth.service.local", "url": "https://app-domain.auth.service.local", "saasregistryenabled": true, - "xsappname": "appname!b15", + "xsappname": "smappname!b15", "credential-type": "instance-secret" }`), }, @@ -1641,10 +1641,10 @@ func TestGetDependencies(t *testing.T) { method: http.MethodGet, expectedStatusCode: http.StatusOK, expectedResponse: []map[string]string{ - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"appId": "destappname!b15", "appName": "destination"}, + {"xsappname": "rtappname!b15"}, }, }, { @@ -1657,10 +1657,10 @@ func TestGetDependencies(t *testing.T) { }, expectedResponse: []map[string]string{ {"xsappname": "appname!b14"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"appId": "destappname!b15", "appName": "destination"}, + {"xsappname": "rtappname!b15"}, }, }, { @@ -1672,10 +1672,10 @@ func TestGetDependencies(t *testing.T) { ca.Spec.BTP.Services[0].SubscriptionDependency = &dep // xsuaa: explicit Auto, still not qualified by class/credentials }, expectedResponse: []map[string]string{ - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"appId": "destappname!b15", "appName": "destination"}, + {"xsappname": "rtappname!b15"}, }, }, { @@ -1687,9 +1687,9 @@ func TestGetDependencies(t *testing.T) { ca.Spec.BTP.Services[4].SubscriptionDependency = &dep // destination: auto-qualified by class, but Never prevents inclusion }, expectedResponse: []map[string]string{ - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"xsappname": "rtappname!b15"}, }, }, } @@ -1776,10 +1776,10 @@ func TestGetSMSDependencies(t *testing.T) { method: http.MethodGet, expectedStatusCode: http.StatusOK, expectedResponse: []map[string]string{ - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"appId": "destappname!b15", "appName": "destination"}, + {"xsappname": "rtappname!b15"}, }, }, { @@ -1792,10 +1792,10 @@ func TestGetSMSDependencies(t *testing.T) { }, expectedResponse: []map[string]string{ {"xsappname": "appname!b14"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"appId": "destappname!b15", "appName": "destination"}, + {"xsappname": "rtappname!b15"}, }, }, { @@ -1807,10 +1807,10 @@ func TestGetSMSDependencies(t *testing.T) { ca.Spec.BTP.Services[0].SubscriptionDependency = &dep // xsuaa: explicit Auto, still not qualified by class/credentials }, expectedResponse: []map[string]string{ - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"appId": "destappname!b15", "appName": "destination"}, + {"xsappname": "rtappname!b15"}, }, }, { @@ -1822,9 +1822,9 @@ func TestGetSMSDependencies(t *testing.T) { ca.Spec.BTP.Services[4].SubscriptionDependency = &dep // destination: auto-qualified by class, but Never prevents inclusion }, expectedResponse: []map[string]string{ - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, - {"xsappname": "appname!b15"}, + {"xsappname": "saasappname!b15"}, + {"xsappname": "smappname!b15"}, + {"xsappname": "rtappname!b15"}, }, }, } diff --git a/internal/util/vcap-credentials.go b/internal/util/vcap-credentials.go index 508b6b35..4e7b080f 100644 --- a/internal/util/vcap-credentials.go +++ b/internal/util/vcap-credentials.go @@ -66,16 +66,21 @@ const ( PropertyFormatJSON PropertyFormat = "json" ) -func ReadServiceCredentialsFromSecret[T any](serviceInfo *v1alpha1.ServiceInfo, ns string, kubeClient kubernetes.Interface) (*T, error) { +func ReadServiceCredentialsFromSecret[T any](serviceInfo *v1alpha1.ServiceInfo, ns string, kubeClient kubernetes.Interface, withMeta bool) (*T, error) { entry, err := CreateVCAPEntryFromSecret(serviceInfo, ns, kubeClient, nil) if err != nil { return nil, err } - b, err := json.Marshal(entry["credentials"]) + var serviceCredInfo []byte + if withMeta { + serviceCredInfo, err = json.Marshal(entry) + } else { + serviceCredInfo, err = json.Marshal(entry["credentials"]) + } if err != nil { return nil, fmt.Errorf("could not serialize credentials for service %s: %s", serviceInfo.Name, err) } - return ParseJSON[T](b) + return ParseJSON[T](serviceCredInfo) } func CreateVCAPEntryFromSecret(serviceInfo *v1alpha1.ServiceInfo, ns string, kubeClient kubernetes.Interface, kubeInformerFactory informers.SharedInformerFactory) (entry map[string]any, err error) { diff --git a/internal/util/vcap-credentials_test.go b/internal/util/vcap-credentials_test.go index 436b0fdf..6a359c44 100644 --- a/internal/util/vcap-credentials_test.go +++ b/internal/util/vcap-credentials_test.go @@ -194,7 +194,7 @@ func testReadServiceCredentialsFromSecret(t *testing.T) { // test successful read secretName := "metadata-with-credential-key" - credentials, err := ReadServiceCredentialsFromSecret[map[string]string](&v1alpha1.ServiceInfo{Name: "service-a", Class: "xyz", Secret: secretName}, "default", c) + credentials, err := ReadServiceCredentialsFromSecret[map[string]string](&v1alpha1.ServiceInfo{Name: "service-a", Class: "xyz", Secret: secretName}, "default", c, false) if err != nil { t.Errorf("could not read credentials from secret %s", secretName) } @@ -203,7 +203,7 @@ func testReadServiceCredentialsFromSecret(t *testing.T) { } // test with type mismatch - _, err = ReadServiceCredentialsFromSecret[[]string](&v1alpha1.ServiceInfo{Name: "service-a", Class: "xyz", Secret: secretName}, "default", c) + _, err = ReadServiceCredentialsFromSecret[[]string](&v1alpha1.ServiceInfo{Name: "service-a", Class: "xyz", Secret: secretName}, "default", c, false) if err == nil { t.Errorf("expected error when reading credentials as array from secret %s", secretName) }