diff --git a/internal/dinosaur/pkg/api/admin/private/api/openapi.yaml b/internal/dinosaur/pkg/api/admin/private/api/openapi.yaml index 8cefa279c..0e9ad735a 100644 --- a/internal/dinosaur/pkg/api/admin/private/api/openapi.yaml +++ b/internal/dinosaur/pkg/api/admin/private/api/openapi.yaml @@ -376,6 +376,44 @@ paths: security: - Bearer: [] summary: Update a Central instance by ID + /api/rhacs/v1/admin/centrals/{id}/rotate-secrets: + post: + parameters: + - description: The ID of record + in: path + name: id + required: true + schema: + type: string + responses: + "200": + description: RHSSO client successfully rotated + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: User is not authorised to access the service + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: No Central found with the specified ID or dynamic clients are + not configured + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + summary: Rotate RHSSO client of a central tenant /api/rhacs/v1/admin/centrals/{id}/restore: post: parameters: diff --git a/internal/dinosaur/pkg/api/admin/private/api_default.go b/internal/dinosaur/pkg/api/admin/private/api_default.go index a64273e8b..484f0e878 100644 --- a/internal/dinosaur/pkg/api/admin/private/api_default.go +++ b/internal/dinosaur/pkg/api/admin/private/api_default.go @@ -142,6 +142,111 @@ func (a *DefaultApiService) ApiRhacsV1AdminCentralsIdRestorePost(ctx _context.Co return localVarHTTPResponse, nil } +/* +ApiRhacsV1AdminCentralsIdRotateSecretsPost Rotate RHSSO client of a central tenant + - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + - @param id The ID of record +*/ +func (a *DefaultApiService) ApiRhacsV1AdminCentralsIdRotateSecretsPost(ctx _context.Context, id string) (*_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPost + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/rhacs/v1/admin/centrals/{id}/rotate-secrets" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.QueryEscape(parameterToString(id, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 403 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} + /* CreateCentral Creates a Central request Creates a new Central that is owned by the user and organisation authenticated for the request. Each Central has a single owner organisation and a single owner user. This API allows providing custom resource settings for the new Central instance. diff --git a/internal/dinosaur/pkg/handlers/admin_dinosaur.go b/internal/dinosaur/pkg/handlers/admin_dinosaur.go index af75d94ec..4c16ab8cb 100644 --- a/internal/dinosaur/pkg/handlers/admin_dinosaur.go +++ b/internal/dinosaur/pkg/handlers/admin_dinosaur.go @@ -44,6 +44,8 @@ type AdminCentralHandler interface { GetCentralDefaultVersion(w http.ResponseWriter, r *http.Request) // Restore restores a tenant that was already marked as deleted Restore(w http.ResponseWriter, r *http.Request) + // RotateSecrets rotates secrets within central + RotateSecrets(w http.ResponseWriter, r *http.Request) } type adminCentralHandler struct { @@ -427,6 +429,22 @@ func (h adminCentralHandler) GetCentralDefaultVersion(w http.ResponseWriter, r * handlers.Handle(w, r, cfg, http.StatusOK) } +func (h adminCentralHandler) RotateSecrets(w http.ResponseWriter, r *http.Request) { + cfg := &handlers.HandlerConfig{ + Action: func() (i interface{}, serviceError *errors.ServiceError) { + id := mux.Vars(r)["id"] + ctx := r.Context() + centralRequest, err := h.service.Get(ctx, id) + if err != nil { + return nil, err + } + err = h.service.RotateCentralRHSSOClient(ctx, centralRequest) + return nil, err + }, + } + handlers.Handle(w, r, cfg, http.StatusOK) +} + type gitOpsAdminHandler struct{} var _ AdminCentralHandler = (*gitOpsAdminHandler)(nil) @@ -466,3 +484,7 @@ func (g gitOpsAdminHandler) GetCentralDefaultVersion(w http.ResponseWriter, r *h func (g gitOpsAdminHandler) Restore(w http.ResponseWriter, r *http.Request) { http.Error(w, "not implemented", http.StatusNotImplemented) } + +func (g gitOpsAdminHandler) RotateSecrets(w http.ResponseWriter, r *http.Request) { + http.Error(w, "not implemented", http.StatusNotImplemented) +} diff --git a/internal/dinosaur/pkg/rhsso/augment.go b/internal/dinosaur/pkg/rhsso/augment.go new file mode 100644 index 000000000..ab18aa3b3 --- /dev/null +++ b/internal/dinosaur/pkg/rhsso/augment.go @@ -0,0 +1,38 @@ +package rhsso + +import ( + "context" + "fmt" + "github.com/pkg/errors" + "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" + "github.com/stackrox/acs-fleet-manager/pkg/client/iam" + "github.com/stackrox/acs-fleet-manager/pkg/client/redhatsso/api" + "github.com/stackrox/rox/pkg/stringutils" +) + +const ( + oidcProviderCallbackPath = "/sso/providers/oidc/callback" + dynamicClientsNameMaxLength = 50 +) + +func AugmentWithDynamicAuthConfig(ctx context.Context, r *dbapi.CentralRequest, realmConfig *iam.IAMRealmConfig, apiClient *api.AcsTenantsApiService) error { + // There is a limit on name length of the dynamic client. To avoid unnecessary errors, + // we truncate name here. + name := stringutils.Truncate(fmt.Sprintf("acscs-%s", r.Name), dynamicClientsNameMaxLength) + orgID := r.OrganisationID + redirectURIs := []string{fmt.Sprintf("https://%s%s", r.GetUIHost(), oidcProviderCallbackPath)} + + dynamicClientData, _, err := apiClient.CreateAcsClient(ctx, api.AcsClientRequestData{ + Name: name, + OrgId: orgID, + RedirectUris: redirectURIs, + }) + if err != nil { + return errors.Wrapf(err, "failed to create RHSSO dynamic client for %s", r.ID) + } + + r.AuthConfig.ClientID = dynamicClientData.ClientId + r.AuthConfig.ClientSecret = dynamicClientData.Secret // pragma: allowlist secret + r.AuthConfig.Issuer = realmConfig.ValidIssuerURI + return nil +} diff --git a/internal/dinosaur/pkg/routes/route_loader.go b/internal/dinosaur/pkg/routes/route_loader.go index 439587aad..426866815 100644 --- a/internal/dinosaur/pkg/routes/route_loader.go +++ b/internal/dinosaur/pkg/routes/route_loader.go @@ -266,6 +266,9 @@ func (s *options) buildAPIBaseRouter(mainRouter *mux.Router, basePath string, op adminCentralsRouter.HandleFunc("/{id}/restore", adminCentralHandler.Restore). Name(logger.NewLogEvent("admin-restore-central", "[admin] restore central by id").ToString()). Methods(http.MethodPost) + adminCentralsRouter.HandleFunc("/{id}/rotate-secrets", adminCentralHandler.RotateSecrets). + Name(logger.NewLogEvent("admin-rotate-central-secrets", "[admin] rotate central secrets by id").ToString()). + Methods(http.MethodPost) adminCreateRouter := adminCentralsRouter.NewRoute().Subrouter() adminCreateRouter.HandleFunc("", adminCentralHandler.Create).Methods(http.MethodPost) diff --git a/internal/dinosaur/pkg/services/dinosaur.go b/internal/dinosaur/pkg/services/dinosaur.go index 4ecaa9af2..70a72aea2 100644 --- a/internal/dinosaur/pkg/services/dinosaur.go +++ b/internal/dinosaur/pkg/services/dinosaur.go @@ -3,6 +3,10 @@ package services import ( "context" "fmt" + "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/rhsso" + "github.com/stackrox/acs-fleet-manager/pkg/client/iam" + dynamicClientAPI "github.com/stackrox/acs-fleet-manager/pkg/client/redhatsso/api" + "github.com/stackrox/acs-fleet-manager/pkg/client/redhatsso/dynamicclients" "sync" "time" @@ -106,6 +110,7 @@ type DinosaurService interface { ListCentralsWithoutAuthConfig() ([]*dbapi.CentralRequest, *errors.ServiceError) VerifyAndUpdateDinosaurAdmin(ctx context.Context, dinosaurRequest *dbapi.CentralRequest) *errors.ServiceError Restore(ctx context.Context, id string) *errors.ServiceError + RotateCentralRHSSOClient(ctx context.Context, centralRequest *dbapi.CentralRequest) *errors.ServiceError } var _ DinosaurService = &dinosaurService{} @@ -124,17 +129,20 @@ type dinosaurService struct { clusterPlacementStrategy ClusterPlacementStrategy amsClient ocm.AMSClient centralDefaultVersionService CentralDefaultVersionService + iamConfig *iam.IAMConfig + rhSSODynamicClientsAPI *dynamicClientAPI.AcsTenantsApiService } // NewDinosaurService ... func NewDinosaurService(connectionFactory *db.ConnectionFactory, clusterService ClusterService, iamService sso.IAMService, - dinosaurConfig *config.CentralConfig, dataplaneClusterConfig *config.DataplaneClusterConfig, awsConfig *config.AWSConfig, + iamConfig *iam.IAMConfig, dinosaurConfig *config.CentralConfig, dataplaneClusterConfig *config.DataplaneClusterConfig, awsConfig *config.AWSConfig, quotaServiceFactory QuotaServiceFactory, awsClientFactory aws.ClientFactory, authorizationService authorization.Authorization, clusterPlacementStrategy ClusterPlacementStrategy, amsClient ocm.AMSClient, centralDefaultVersionService CentralDefaultVersionService) *dinosaurService { return &dinosaurService{ connectionFactory: connectionFactory, clusterService: clusterService, iamService: iamService, + iamConfig: iamConfig, dinosaurConfig: dinosaurConfig, awsConfig: awsConfig, quotaServiceFactory: quotaServiceFactory, @@ -144,9 +152,35 @@ func NewDinosaurService(connectionFactory *db.ConnectionFactory, clusterService clusterPlacementStrategy: clusterPlacementStrategy, amsClient: amsClient, centralDefaultVersionService: centralDefaultVersionService, + rhSSODynamicClientsAPI: dynamicclients.NewDynamicClientsAPI(iamConfig.RedhatSSORealm), } } +func (k *dinosaurService) RotateCentralRHSSOClient(ctx context.Context, centralRequest *dbapi.CentralRequest) *errors.ServiceError { + realmConfig := k.iamConfig.RedhatSSORealm + if k.dinosaurConfig.HasStaticAuth() { + return errors.New(errors.ErrorDynamicClientsNotUsed, "RHSSO is configured via static configuration") + } + if !realmConfig.IsConfigured() { + return errors.New(errors.ErrorDynamicClientsNotUsed, "RHSSO dynamic client configuration is not present") + } + + previousAuthConfig := centralRequest.AuthConfig + if err := rhsso.AugmentWithDynamicAuthConfig(ctx, centralRequest, k.iamConfig.RedhatSSORealm, k.rhSSODynamicClientsAPI); err != nil { + return errors.NewWithCause(errors.ErrorClientRotationFailed, err, "failed to augment auth config") + + } + if err := k.Update(centralRequest); err != nil { + glog.Errorf("Rotating RHSSO client failed: created new RHSSO dynamic client, but failed to update central record, client ID is %s", centralRequest.AuthConfig.ClientID) + return errors.NewWithCause(errors.ErrorClientRotationFailed, err, "failed to update database record") + } + if _, err := k.rhSSODynamicClientsAPI.DeleteAcsClient(ctx, previousAuthConfig.ClientID); err != nil { + glog.Errorf("Rotating RHSSO client failed: failed to delete RHSSO dynamic client, client ID is %s", centralRequest.AuthConfig.ClientID) + return errors.NewWithCause(errors.ErrorClientRotationFailed, err, "failed to delete previous RHSSO dynamic client") + } + return nil +} + // HasAvailableCapacityInRegion ... func (k *dinosaurService) HasAvailableCapacityInRegion(dinosaurRequest *dbapi.CentralRequest) (bool, *errors.ServiceError) { regionCapacity := int64(k.dataplaneClusterConfig.ClusterConfig.GetCapacityForRegion(dinosaurRequest.Region)) diff --git a/internal/dinosaur/pkg/services/dinosaurservice_moq.go b/internal/dinosaur/pkg/services/dinosaurservice_moq.go index 83c8b02d5..a9231f48b 100644 --- a/internal/dinosaur/pkg/services/dinosaurservice_moq.go +++ b/internal/dinosaur/pkg/services/dinosaurservice_moq.go @@ -88,6 +88,9 @@ var _ DinosaurService = &DinosaurServiceMock{} // RestoreFunc: func(ctx context.Context, id string) *serviceError.ServiceError { // panic("mock out the Restore method") // }, +// RotateCentralRHSSOClientFunc: func(ctx context.Context, centralRequest *dbapi.CentralRequest) *serviceError.ServiceError { +// panic("mock out the RotateCentralRHSSOClient method") +// }, // UpdateFunc: func(dinosaurRequest *dbapi.CentralRequest) *serviceError.ServiceError { // panic("mock out the Update method") // }, @@ -170,6 +173,9 @@ type DinosaurServiceMock struct { // RestoreFunc mocks the Restore method. RestoreFunc func(ctx context.Context, id string) *serviceError.ServiceError + // RotateCentralRHSSOClientFunc mocks the RotateCentralRHSSOClient method. + RotateCentralRHSSOClientFunc func(ctx context.Context, centralRequest *dbapi.CentralRequest) *serviceError.ServiceError + // UpdateFunc mocks the Update method. UpdateFunc func(dinosaurRequest *dbapi.CentralRequest) *serviceError.ServiceError @@ -295,6 +301,13 @@ type DinosaurServiceMock struct { // ID is the id argument value. ID string } + // RotateCentralRHSSOClient holds details about calls to the RotateCentralRHSSOClient method. + RotateCentralRHSSOClient []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // CentralRequest is the centralRequest argument value. + CentralRequest *dbapi.CentralRequest + } // Update holds details about calls to the Update method. Update []struct { // DinosaurRequest is the dinosaurRequest argument value. @@ -343,6 +356,7 @@ type DinosaurServiceMock struct { lockRegisterDinosaurDeprovisionJob sync.RWMutex lockRegisterDinosaurJob sync.RWMutex lockRestore sync.RWMutex + lockRotateCentralRHSSOClient sync.RWMutex lockUpdate sync.RWMutex lockUpdateStatus sync.RWMutex lockUpdates sync.RWMutex @@ -1030,6 +1044,42 @@ func (mock *DinosaurServiceMock) RestoreCalls() []struct { return calls } +// RotateCentralRHSSOClient calls RotateCentralRHSSOClientFunc. +func (mock *DinosaurServiceMock) RotateCentralRHSSOClient(ctx context.Context, centralRequest *dbapi.CentralRequest) *serviceError.ServiceError { + if mock.RotateCentralRHSSOClientFunc == nil { + panic("DinosaurServiceMock.RotateCentralRHSSOClientFunc: method is nil but DinosaurService.RotateCentralRHSSOClient was just called") + } + callInfo := struct { + Ctx context.Context + CentralRequest *dbapi.CentralRequest + }{ + Ctx: ctx, + CentralRequest: centralRequest, + } + mock.lockRotateCentralRHSSOClient.Lock() + mock.calls.RotateCentralRHSSOClient = append(mock.calls.RotateCentralRHSSOClient, callInfo) + mock.lockRotateCentralRHSSOClient.Unlock() + return mock.RotateCentralRHSSOClientFunc(ctx, centralRequest) +} + +// RotateCentralRHSSOClientCalls gets all the calls that were made to RotateCentralRHSSOClient. +// Check the length with: +// +// len(mockedDinosaurService.RotateCentralRHSSOClientCalls()) +func (mock *DinosaurServiceMock) RotateCentralRHSSOClientCalls() []struct { + Ctx context.Context + CentralRequest *dbapi.CentralRequest +} { + var calls []struct { + Ctx context.Context + CentralRequest *dbapi.CentralRequest + } + mock.lockRotateCentralRHSSOClient.RLock() + calls = mock.calls.RotateCentralRHSSOClient + mock.lockRotateCentralRHSSOClient.RUnlock() + return calls +} + // Update calls UpdateFunc. func (mock *DinosaurServiceMock) Update(dinosaurRequest *dbapi.CentralRequest) *serviceError.ServiceError { if mock.UpdateFunc == nil { diff --git a/internal/dinosaur/pkg/workers/dinosaurmgrs/dinosaurs_auth_config_mgr.go b/internal/dinosaur/pkg/workers/dinosaurmgrs/dinosaurs_auth_config_mgr.go index 90553a6b6..26084449a 100644 --- a/internal/dinosaur/pkg/workers/dinosaurmgrs/dinosaurs_auth_config_mgr.go +++ b/internal/dinosaur/pkg/workers/dinosaurmgrs/dinosaurs_auth_config_mgr.go @@ -2,11 +2,6 @@ package dinosaurmgrs import ( "context" - "fmt" - - "github.com/stackrox/acs-fleet-manager/pkg/metrics" - - "github.com/stackrox/rox/pkg/stringutils" "github.com/golang/glog" "github.com/google/uuid" @@ -14,19 +9,17 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" + "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/rhsso" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services" "github.com/stackrox/acs-fleet-manager/pkg/client/iam" "github.com/stackrox/acs-fleet-manager/pkg/client/redhatsso/api" "github.com/stackrox/acs-fleet-manager/pkg/client/redhatsso/dynamicclients" + "github.com/stackrox/acs-fleet-manager/pkg/metrics" "github.com/stackrox/acs-fleet-manager/pkg/workers" "github.com/stackrox/rox/pkg/ternary" ) -const ( - centralAuthConfigManagerWorkerType = "central_auth_config" - oidcProviderCallbackPath = "/sso/providers/oidc/callback" - dynamicClientsNameMaxLength = 50 -) +const centralAuthConfigManagerWorkerType = "central_auth_config" // CentralAuthConfigManager updates CentralRequests with auth configuration. type CentralAuthConfigManager struct { @@ -111,7 +104,7 @@ func (k *CentralAuthConfigManager) reconcileCentralRequest(cr *dbapi.CentralRequ err = augmentWithStaticAuthConfig(cr, k.centralConfig) } else { glog.V(7).Infoln("no static config found; attempting to obtain one from the IdP") - err = augmentWithDynamicAuthConfig(cr, k.realmConfig, k.dynamicClientsAPIClient) + err = rhsso.AugmentWithDynamicAuthConfig(context.Background(), cr, k.realmConfig, k.dynamicClientsAPIClient) } if err != nil { return errors.Wrap(err, "failed to augment central request with auth config") @@ -136,27 +129,3 @@ func augmentWithStaticAuthConfig(r *dbapi.CentralRequest, centralConfig *config. return nil } - -// augmentWithDynamicAuthConfig performs all necessary rituals to obtain auth -// configuration via RHSSO API. -func augmentWithDynamicAuthConfig(r *dbapi.CentralRequest, realmConfig *iam.IAMRealmConfig, apiClient *api.AcsTenantsApiService) error { - // There is a limit on name length of the dynamic client. To avoid unnecessary errors, - // we truncate name here. - name := stringutils.Truncate(fmt.Sprintf("acscs-%s", r.Name), dynamicClientsNameMaxLength) - orgID := r.OrganisationID - redirectURIs := []string{fmt.Sprintf("https://%s%s", r.GetUIHost(), oidcProviderCallbackPath)} - - dynamicClientData, _, err := apiClient.CreateAcsClient(context.Background(), api.AcsClientRequestData{ - Name: name, - OrgId: orgID, - RedirectUris: redirectURIs, - }) - if err != nil { - return errors.Wrapf(err, "failed to create RHSSO dynamic client for %s", r.ID) - } - - r.AuthConfig.ClientID = dynamicClientData.ClientId - r.AuthConfig.ClientSecret = dynamicClientData.Secret // pragma: allowlist secret - r.AuthConfig.Issuer = realmConfig.ValidIssuerURI - return nil -} diff --git a/openapi/fleet-manager-private-admin.yaml b/openapi/fleet-manager-private-admin.yaml index 824b821dc..07cb12729 100644 --- a/openapi/fleet-manager-private-admin.yaml +++ b/openapi/fleet-manager-private-admin.yaml @@ -259,6 +259,38 @@ paths: application/json: schema: $ref: 'fleet-manager.yaml#/components/schemas/Error' + '/api/rhacs/v1/admin/centrals/{id}/rotate-secrets': + post: + summary: Rotate RHSSO client of a central tenant + parameters: + - $ref: "fleet-manager.yaml#/components/parameters/id" + responses: + "200": + description: RHSSO client successfully rotated + "401": + description: Auth token is invalid + content: + application/json: + schema: + $ref: 'fleet-manager.yaml#/components/schemas/Error' + "403": + description: User is not authorised to access the service + content: + application/json: + schema: + $ref: 'fleet-manager.yaml#/components/schemas/Error' + "404": + description: No Central found with the specified ID or dynamic clients are not configured + content: + application/json: + schema: + $ref: 'fleet-manager.yaml#/components/schemas/Error' + "500": + description: Unexpected error occurred + content: + application/json: + schema: + $ref: 'fleet-manager.yaml#/components/schemas/Error' '/api/rhacs/v1/admin/centrals/{id}/restore': post: summary: Restore a central tenant that was already deleted diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 576f4d102..d1881a437 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -128,6 +128,14 @@ const ( ErrorMaxLimitForServiceAccountsReached ServiceErrorCode = 115 ErrorMaxLimitForServiceAccountsReachedReason string = "Max limit for the service account creation has reached" + // RHSSO dynamic clients are not configured for this particular fleet-manager instance + ErrorDynamicClientsNotUsed ServiceErrorCode = 116 + ErrorDynamicClientsNotUsedReason string = "RHSSO dynamic clients are not used" + + // RHSSO client rotation attempted and failed + ErrorClientRotationFailed ServiceErrorCode = 117 + ErrorClientRotationFailedReason string = "RHSSO client rotation failed" + // Insufficient quota ErrorInsufficientQuota ServiceErrorCode = 120 ErrorInsufficientQuotaReason string = "Insufficient quota" @@ -274,6 +282,8 @@ func Errors() ServiceErrors { ServiceError{ErrorMaxLimitForServiceAccountsReached, ErrorMaxLimitForServiceAccountsReachedReason, http.StatusForbidden, nil}, ServiceError{ErrorInstancePlanNotSupported, ErrorInstancePlanNotSupportedReason, http.StatusBadRequest, nil}, ServiceError{ErrorInvalidCloudAccountID, ErrorInvalidCloudAccountIDReason, http.StatusBadRequest, nil}, + ServiceError{ErrorDynamicClientsNotUsed, ErrorDynamicClientsNotUsedReason, http.StatusNotFound, nil}, + ServiceError{ErrorClientRotationFailed, ErrorClientRotationFailedReason, http.StatusInternalServerError, nil}, } }