github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/authenticator/claims/validator.go (about) 1 package claims 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "fmt" 7 "strings" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/idtokenclaims" 10 11 "github.com/kyma-incubator/compass/components/director/pkg/str" 12 13 "github.com/kyma-incubator/compass/components/director/internal/domain/scenarioassignment" 14 15 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 16 17 "github.com/kyma-incubator/compass/components/director/internal/domain/tenant" 18 "github.com/kyma-incubator/compass/components/director/internal/labelfilter" 19 "github.com/kyma-incubator/compass/components/director/internal/model" 20 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 21 "github.com/kyma-incubator/compass/components/director/pkg/consumer" 22 "github.com/kyma-incubator/compass/components/director/pkg/log" 23 "github.com/kyma-incubator/compass/components/hydrator/pkg/tenantmapping" 24 "github.com/pkg/errors" 25 ) 26 27 // RuntimeService is used to interact with runtimes. 28 //go:generate mockery --name=RuntimeService --output=automock --outpkg=automock --case=underscore --disable-version-string 29 type RuntimeService interface { 30 GetLabel(context.Context, string, string) (*model.Label, error) 31 GetByFilters(ctx context.Context, filters []*labelfilter.LabelFilter) (*model.Runtime, error) 32 } 33 34 // RuntimeCtxService is used to interact with runtime contexts. 35 //go:generate mockery --name=RuntimeCtxService --output=automock --outpkg=automock --case=underscore --disable-version-string 36 type RuntimeCtxService interface { 37 ListByFilter(ctx context.Context, runtimeID string, filter []*labelfilter.LabelFilter, pageSize int, cursor string) (*model.RuntimeContextPage, error) 38 } 39 40 // ApplicationTemplateService is used to interact with application templates. 41 //go:generate mockery --name=ApplicationTemplateService --output=automock --outpkg=automock --case=underscore --disable-version-string 42 type ApplicationTemplateService interface { 43 GetByFilters(ctx context.Context, filter []*labelfilter.LabelFilter) (*model.ApplicationTemplate, error) 44 } 45 46 // ApplicationService is responsible for the service-layer Application operations. 47 //go:generate mockery --name=ApplicationService --output=automock --outpkg=automock --case=underscore --disable-version-string 48 type ApplicationService interface { 49 ListAll(ctx context.Context) ([]*model.Application, error) 50 } 51 52 // IntegrationSystemService is used to check if integration system with a given ID exists. 53 //go:generate mockery --name=IntegrationSystemService --output=automock --outpkg=automock --case=underscore --disable-version-string 54 type IntegrationSystemService interface { 55 Exists(context.Context, string) (bool, error) 56 } 57 58 type validator struct { 59 transact persistence.Transactioner 60 runtimesSvc RuntimeService 61 runtimeCtxSvc RuntimeCtxService 62 appTemplateSvc ApplicationTemplateService 63 applicationSvc ApplicationService 64 intSystemSvc IntegrationSystemService 65 subscriptionProviderLabelKey string 66 consumerSubaccountLabelKey string 67 tokenPrefix string 68 } 69 70 // NewValidator creates new claims validator 71 func NewValidator(transact persistence.Transactioner, runtimesSvc RuntimeService, runtimeCtxSvc RuntimeCtxService, appTemplateSvc ApplicationTemplateService, applicationSvc ApplicationService, intSystemSvc IntegrationSystemService, subscriptionProviderLabelKey, consumerSubaccountLabelKey, tokenPrefix string) *validator { 72 return &validator{ 73 transact: transact, 74 runtimesSvc: runtimesSvc, 75 runtimeCtxSvc: runtimeCtxSvc, 76 appTemplateSvc: appTemplateSvc, 77 applicationSvc: applicationSvc, 78 intSystemSvc: intSystemSvc, 79 subscriptionProviderLabelKey: subscriptionProviderLabelKey, 80 consumerSubaccountLabelKey: consumerSubaccountLabelKey, 81 tokenPrefix: tokenPrefix, 82 } 83 } 84 85 // Validate validates given id_token claims 86 func (v *validator) Validate(ctx context.Context, claims idtokenclaims.Claims) error { 87 if err := claims.Valid(); err != nil { 88 return errors.Wrapf(err, "while validating claims") 89 } 90 91 if claims.Tenant[tenantmapping.ConsumerTenantKey] == "" && claims.Tenant[tenantmapping.ExternalTenantKey] != "" { 92 return apperrors.NewTenantNotFoundError(claims.Tenant[tenantmapping.ExternalTenantKey]) 93 } 94 95 if claims.OnBehalfOf == "" { 96 return nil 97 } 98 99 log.C(ctx).Infof("Consumer-Provider call by %s on behalf of REDACTED_%x. Proceeding with double authentication crosscheck...", claims.Tenant[tenantmapping.ProviderTenantKey], sha256.Sum256([]byte(claims.Tenant[tenantmapping.ConsumerTenantKey]))) 100 switch claims.ConsumerType { 101 case consumer.Runtime, consumer.ExternalCertificate, consumer.SuperAdmin: // SuperAdmin consumer is needed only for testing purposes 102 errRuntimeConsumer := v.validateRuntimeConsumer(ctx, claims) 103 if errRuntimeConsumer == nil { 104 return nil 105 } 106 errAppProvider := v.validateApplicationProvider(ctx, claims) 107 if errAppProvider == nil { 108 return nil 109 } 110 return apperrors.NewUnauthorizedError(fmt.Sprintf("subscription record not found neither for application: %q nor for runtime: %q", errAppProvider.Error(), errRuntimeConsumer.Error())) 111 case consumer.IntegrationSystem: 112 return v.validateIntegrationSystemConsumer(ctx, claims) 113 default: 114 return apperrors.NewUnauthorizedError(fmt.Sprintf("consumer with type %s is not supported", claims.ConsumerType)) 115 } 116 } 117 118 func (v *validator) validateRuntimeConsumer(ctx context.Context, claims idtokenclaims.Claims) error { 119 tx, err := v.transact.Begin() 120 if err != nil { 121 log.C(ctx).Errorf("An error has occurred while opening transaction: %v", err) 122 return errors.Wrapf(err, "An error has occurred while opening transaction") 123 } 124 defer v.transact.RollbackUnlessCommitted(ctx, tx) 125 126 ctx = persistence.SaveToContext(ctx, tx) 127 128 if len(claims.TokenClientID) == 0 { 129 log.C(ctx).Errorf("Could not find consumer token client ID") 130 return apperrors.NewUnauthorizedError("could not find consumer token client ID") 131 } 132 if len(claims.Region) == 0 { 133 log.C(ctx).Errorf("Could not determine consumer token's region") 134 return apperrors.NewUnauthorizedError("could not determine token's region") 135 } 136 137 tokenClientID := strings.TrimPrefix(claims.TokenClientID, v.tokenPrefix) 138 filters := []*labelfilter.LabelFilter{ 139 labelfilter.NewForKeyWithQuery(v.subscriptionProviderLabelKey, fmt.Sprintf("\"%s\"", tokenClientID)), 140 labelfilter.NewForKeyWithQuery(tenant.RegionLabelKey, fmt.Sprintf("\"%s\"", claims.Region)), 141 } 142 143 providerInternalTenantID := claims.Tenant[tenantmapping.ProviderTenantKey] 144 providerExternalTenantID := claims.Tenant[tenantmapping.ProviderExternalTenantKey] 145 ctxWithProviderTenant := tenant.SaveToContext(ctx, providerInternalTenantID, providerExternalTenantID) 146 147 log.C(ctx).Infof("Getting runtime in provider tenant %s for labels %s: %s and %s: %s", providerInternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID) 148 runtime, err := v.runtimesSvc.GetByFilters(ctxWithProviderTenant, filters) 149 if err != nil { 150 log.C(ctx).WithError(err).Errorf("Error while getting runtime in provider tenant %s for labels %s: %s and %s: %s: %v", providerInternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID, err) 151 return errors.Wrapf(err, "failed to get runtime in tenant %s for labels %s: %s and %s: %s", providerInternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID) 152 } 153 log.C(ctx).Infof("Found runtime with ID: %s in provider tenant %s for labels %s: %s and %s: %s", runtime.ID, providerInternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID) 154 155 consumerInternalTenantID := claims.Tenant[tenantmapping.ConsumerTenantKey] 156 consumerExternalTenantID := claims.Tenant[tenantmapping.ExternalTenantKey] 157 ctxWithConsumerTenant := tenant.SaveToContext(ctx, consumerInternalTenantID, consumerExternalTenantID) 158 159 rtmCtxFilter := []*labelfilter.LabelFilter{ 160 labelfilter.NewForKeyWithQuery(v.consumerSubaccountLabelKey, fmt.Sprintf("\"%s\"", consumerExternalTenantID)), 161 } 162 163 log.C(ctx).Infof("Listing runtime context(s) in the consumer tenant %q for runtime with ID: %q and label with key: %q and value: %q", consumerExternalTenantID, runtime.ID, v.consumerSubaccountLabelKey, consumerExternalTenantID) 164 rtmCtxPage, err := v.runtimeCtxSvc.ListByFilter(ctxWithConsumerTenant, runtime.ID, rtmCtxFilter, 100, "") 165 if err != nil { 166 log.C(ctx).Errorf("An error occurred while listing runtime context for runtime with ID: %q and filter with key: %q and value: %q", runtime.ID, v.consumerSubaccountLabelKey, consumerExternalTenantID) 167 return errors.Wrapf(err, "while listing runtime context for runtime with ID: %q and filter with key: %q and value: %q", runtime.ID, v.consumerSubaccountLabelKey, consumerExternalTenantID) 168 } 169 log.C(ctx).Infof("Found %d runtime context(s) for runtime with ID: %q", len(rtmCtxPage.Data), runtime.ID) 170 171 if len(rtmCtxPage.Data) == 0 { 172 log.C(ctx).Errorf("Consumer's external tenant %s was not found as subscription record in the runtime context table for the runtime with ID: %s in the provider tenant %s", consumerExternalTenantID, runtime.ID, providerInternalTenantID) 173 return apperrors.NewUnauthorizedError(fmt.Sprintf("Consumer's external tenant %s was not found as subscription record in the runtime context table for the runtime in the provider tenant %s", consumerExternalTenantID, providerInternalTenantID)) 174 } 175 176 return tx.Commit() 177 } 178 179 func (v *validator) validateApplicationProvider(ctx context.Context, claims idtokenclaims.Claims) error { 180 tx, err := v.transact.Begin() 181 if err != nil { 182 log.C(ctx).Errorf("An error has occurred while opening transaction: %v", err) 183 return errors.Wrapf(err, "An error has occurred while opening transaction") 184 } 185 defer v.transact.RollbackUnlessCommitted(ctx, tx) 186 187 ctx = persistence.SaveToContext(ctx, tx) 188 189 if len(claims.TokenClientID) == 0 { 190 log.C(ctx).Errorf("Could not find consumer token client ID") 191 return apperrors.NewUnauthorizedError("could not find consumer token client ID") 192 } 193 if len(claims.Region) == 0 { 194 log.C(ctx).Errorf("Could not determine consumer token's region") 195 return apperrors.NewUnauthorizedError("could not determine token's region") 196 } 197 198 providerInternalTenantID := claims.Tenant[tenantmapping.ProviderTenantKey] 199 providerExternalTenantID := claims.Tenant[tenantmapping.ProviderExternalTenantKey] 200 201 tokenClientID := strings.TrimPrefix(claims.TokenClientID, v.tokenPrefix) 202 filters := []*labelfilter.LabelFilter{ 203 labelfilter.NewForKeyWithQuery(v.subscriptionProviderLabelKey, fmt.Sprintf("\"%s\"", tokenClientID)), 204 labelfilter.NewForKeyWithQuery(tenant.RegionLabelKey, fmt.Sprintf("\"%s\"", claims.Region)), 205 labelfilter.NewForKeyWithQuery(scenarioassignment.SubaccountIDKey, fmt.Sprintf("\"%s\"", providerExternalTenantID)), 206 } 207 208 ctxWithProviderTenant := tenant.SaveToContext(ctx, providerInternalTenantID, providerExternalTenantID) 209 210 log.C(ctx).Infof("Get application template in provider tenant %s for labels %s: %s and %s: %s", providerInternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID) 211 applicationTemplate, err := v.appTemplateSvc.GetByFilters(ctxWithProviderTenant, filters) 212 if err != nil { 213 log.C(ctx).WithError(err).Errorf("Error while getting application template in provider tenant %s for labels %s: %s and %s: %s: %v", providerInternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID, err) 214 return errors.Wrapf(err, "failed to find application template in tenant %s associated with %s: %q and %s: %q", providerExternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID) 215 } 216 log.C(ctx).Infof("Found application template with ID %q in provider tenant %s for labels %s: %s and %s: %s", applicationTemplate.ID, providerInternalTenantID, tenant.RegionLabelKey, claims.Region, v.subscriptionProviderLabelKey, tokenClientID) 217 218 consumerInternalTenantID := claims.Tenant[tenantmapping.ConsumerTenantKey] 219 consumerExternalTenantID := claims.Tenant[tenantmapping.ExternalTenantKey] 220 ctxWithConsumerTenant := tenant.SaveToContext(ctx, consumerInternalTenantID, consumerExternalTenantID) 221 222 log.C(ctx).Infof("Listing applications in the consumer tenant %q for application template with ID: %q and label with key: %q and value: %q", consumerExternalTenantID, applicationTemplate.ID, v.consumerSubaccountLabelKey, consumerExternalTenantID) 223 applications, err := v.applicationSvc.ListAll(ctxWithConsumerTenant) 224 if err != nil { 225 log.C(ctx).Errorf("An error occurred while listing applications for filter with key: %q and value: %q", v.consumerSubaccountLabelKey, consumerExternalTenantID) 226 return errors.Wrapf(err, "while listing applications for filter with key: %q and value: %q", v.consumerSubaccountLabelKey, consumerExternalTenantID) 227 } 228 229 log.C(ctx).Infof("Found %d applications in consumer tenant using label: %q and external tenant ID: %q", len(applications), v.consumerSubaccountLabelKey, consumerExternalTenantID) 230 231 appFound := false 232 for _, application := range applications { 233 if str.PtrStrToStr(application.ApplicationTemplateID) == applicationTemplate.ID { 234 appFound = true 235 break 236 } 237 } 238 239 if !appFound { 240 log.C(ctx).Errorf("Consumer's external tenant %s was not found as subscription record in the applications table for any application templates in the provider tenant %s", consumerExternalTenantID, providerInternalTenantID) 241 return apperrors.NewUnauthorizedError(fmt.Sprintf("Consumer's external tenant %s was not found as subscription record in the applications table for any application templates in the provider tenant %s", consumerExternalTenantID, providerInternalTenantID)) 242 } 243 244 return tx.Commit() 245 } 246 247 func (v *validator) validateIntegrationSystemConsumer(ctx context.Context, claims idtokenclaims.Claims) error { 248 if claims.Tenant[tenantmapping.ProviderExternalTenantKey] == claims.ConsumerID { 249 return nil // consumer ID is a subaccount tenant 250 } 251 252 exists, err := v.intSystemSvc.Exists(ctx, claims.ConsumerID) 253 if err != nil { 254 return errors.Wrapf(err, "while checking if integration system with ID %s exists", claims.ConsumerID) 255 } 256 if !exists { 257 return apperrors.NewUnauthorizedError(fmt.Sprintf("integration system with ID %s does not exist", claims.ConsumerID)) 258 } 259 260 return nil 261 }