Files
versitygw/backend/azure/err.go
T
niksis02 859f81fb5d fix: map azure AuthorizationPermissionMismatch to AccessDenied
Originated from #2302

When the Azure credential is valid but its RBAC role does not grant a data action, Azure answers with `403 AuthorizationPermissionMismatch`. This code had no mapping, so the gateway returned `500 InternalError` to the S3 client. A common case is `CompleteMultipartUpload` under a managed identity with `Storage Blob Data Contributor`: the Get Blob Tags call on the `.sgwtmp` multipart staging blob needs `blobs/tags/read`, which that role does not include.

`azErrToS3err` now maps `AuthorizationPermissionMismatch` to `AccessDenied`. `parseMpError` used to return the raw Azure error for every code except `NoSuchKey`, so the new mapping never reached the multipart paths. It now also passes `AccessDenied` through, and the client gets a `403 AccessDenied` instead of a `500 InternalError`.
2026-09-11 16:41:41 +04:00

73 lines
1.9 KiB
Go

// Copyright 2023 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package azure
import (
"errors"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/versity/versitygw/s3err"
)
// Parses azure ResponseError into AWS APIError
func azureErrToS3Err(apiErr error) error {
var azErr *azcore.ResponseError
if !errors.As(apiErr, &azErr) {
return apiErr
}
return azErrToS3err(azErr)
}
func azErrToS3err(azErr *azcore.ResponseError) s3err.APIError {
switch azErr.ErrorCode {
case "ContainerAlreadyExists":
return s3err.GetAPIError(s3err.ErrBucketAlreadyExists)
case "InvalidResourceName", "ContainerNotFound":
return s3err.GetAPIError(s3err.ErrNoSuchBucket)
case "BlobNotFound":
return s3err.GetAPIError(s3err.ErrNoSuchKey)
case "TagsTooLarge":
return s3err.GetAPIError(s3err.ErrInvalidTagValue)
case "Requested Range Not Satisfiable":
return s3err.GetAPIError(s3err.ErrInvalidRange)
case "AuthorizationPermissionMismatch":
return s3err.GetAPIError(s3err.ErrAccessDenied)
}
return s3err.APIError{
Code: azErr.ErrorCode,
Description: azErr.RawResponse.Status,
HTTPStatusCode: azErr.StatusCode,
}
}
func parseMpError(mpErr error) error {
err := azureErrToS3Err(mpErr)
serr, ok := err.(s3err.APIError)
if !ok {
return mpErr
}
switch serr.Code {
case "NoSuchKey":
return s3err.GetAPIError(s3err.ErrNoSuchUpload)
case "AccessDenied":
return serr
}
return mpErr
}