mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-19 11:00:37 +08:00
158 lines
5.7 KiB
Go
158 lines
5.7 KiB
Go
// Teleport
|
|
// Copyright (C) 2024 Gravitational, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package msgraph
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gravitational/trace"
|
|
)
|
|
|
|
// ErrMissingDeltaLink is returned if a delta link is missing from the delta store.
|
|
var ErrMissingDeltaLink = &trace.BadParameterError{Message: "missing delta link"}
|
|
|
|
type graphErrorResponse struct {
|
|
Error *GraphError `json:"error,omitempty"`
|
|
}
|
|
|
|
// GraphError defines the structure of errors returned from MS Graph API.
|
|
// https://learn.microsoft.com/en-us/graph/errors#json-representation
|
|
type GraphError struct {
|
|
// Code is the code for the error, e.g. "UnknownError", "BadRequest".
|
|
Code string `json:"code,omitempty"`
|
|
// Message is a developer ready message about the error that occurred. This shouldn't be displayed
|
|
// to the user directly.
|
|
Message string `json:"message,omitempty"`
|
|
// InnerError is an optional additional error object that is more specific than the top-level
|
|
// error.
|
|
InnerError *GraphError `json:"innerError,omitempty"`
|
|
// Details is an optional list of more error objects that provide a breakdown of multiple errors
|
|
// encountered while processing the request.
|
|
Details []GraphError `json:"details,omitempty"`
|
|
// StatusCode is the status code of the HTTP response that GraphError arrived with.
|
|
StatusCode int `json:"-"`
|
|
// RetryAfter holds the value of "Retry-After" header as sent by the Graph API.
|
|
RetryAfter time.Duration `json:"-"`
|
|
}
|
|
|
|
func (g *GraphError) Error() string {
|
|
var parts []string
|
|
if g.Code != "" {
|
|
parts = append(parts, strings.TrimPrefix(g.Code, "Request_"))
|
|
}
|
|
|
|
if g.Message != "" {
|
|
parts = append(parts, g.Message)
|
|
}
|
|
|
|
return strings.Join(parts, ": ")
|
|
}
|
|
|
|
func readError(body []byte, statusCode int) (*GraphError, error) {
|
|
var errResponse graphErrorResponse
|
|
if err := json.Unmarshal(body, &errResponse); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
if errResponse.Error == nil {
|
|
return nil, nil
|
|
}
|
|
graphError := errResponse.Error
|
|
graphError.StatusCode = statusCode
|
|
return graphError, nil
|
|
}
|
|
|
|
// AuthError is the error returned by [AzureTokenProvider.GetToken] indicating that an
|
|
// authentication request has failed.
|
|
// https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes
|
|
type AuthError struct {
|
|
// ErrorCode is the code string for the error, e.g. "invalid_client".
|
|
ErrorCode string `json:"error"`
|
|
// ErrorDescription is a specific error message that can help a developer identify the root cause
|
|
// of an authentication error.
|
|
ErrorDescription string `json:"error_description"`
|
|
// DiagCodes is a list of codes used by the security token service which map to specific reasons
|
|
// as to why a request have failed.
|
|
// https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes#aadsts-error-codes
|
|
DiagCodes []int `json:"error_codes"`
|
|
StatusCode int `json:"-"`
|
|
}
|
|
|
|
func (a *AuthError) Error() string {
|
|
var b strings.Builder
|
|
b.WriteString(a.ErrorDescription)
|
|
b.WriteString(" (")
|
|
b.WriteString(a.ErrorCode)
|
|
if len(a.DiagCodes) > 0 {
|
|
if len(a.DiagCodes) == 1 {
|
|
b.WriteString(", diag code ")
|
|
} else {
|
|
b.WriteString(", diag codes ")
|
|
}
|
|
for i, errorCode := range a.DiagCodes {
|
|
if i != 0 {
|
|
b.WriteString(", ")
|
|
}
|
|
b.WriteString(strconv.Itoa(errorCode))
|
|
}
|
|
}
|
|
b.WriteString(")")
|
|
return b.String()
|
|
}
|
|
|
|
func readAuthError(r io.Reader, statusCode int) (*AuthError, error) {
|
|
var authError AuthError
|
|
authError.StatusCode = statusCode
|
|
err := json.NewDecoder(r).Decode(&authError)
|
|
return &authError, trace.Wrap(err)
|
|
}
|
|
|
|
const (
|
|
// DiagCodeTenantNotFound is returned by the identity platform when the specific tenant doesn't
|
|
// exist. It might also mean that the tenant belongs to another cloud (see
|
|
// https://learn.microsoft.com/en-us/graph/deployments) or there are no active subscriptions for
|
|
// the tenant.
|
|
// https://login.microsoftonline.com/error?code=90002
|
|
DiagCodeTenantNotFound = 90002
|
|
// DiagCodeInvalidTenantIdentifier is returned by the identity platform when the identifier is
|
|
// neither a valid DNS name nor a valid external domain. This happes when the tenant is not a UUID
|
|
// and instead a regular string that doesn't match said requirements.
|
|
// https://login.microsoftonline.com/error?code=900023
|
|
DiagCodeInvalidTenantIdentifier = 900023
|
|
)
|
|
|
|
const (
|
|
// resyncRequired is returned by the msgraph delta API when a resync is required.
|
|
// https://learn.microsoft.com/en-us/graph/delta-query-overview#synchronization-reset
|
|
ErrCodeResyncRequired = "resyncRequired"
|
|
// syncStateNotFound is returned by the msgraph delta API when the provided
|
|
// delta token may have expired or no longer valid.
|
|
// https://learn.microsoft.com/en-us/graph/delta-query-overview#token-duration
|
|
ErrCodeSyncStateNotFound = "syncStateNotFound"
|
|
// resyncApplyDifferences is returned by the msgraph delta API, generally
|
|
// returned if there is an issue with the provided delta token.
|
|
ErrCodeResyncApplyDifferences = "resyncApplyDifferences"
|
|
|
|
// Throttled is returned when request throttled.
|
|
ErrCodeThrottled = "Request_ThrottledTemporarily"
|
|
// TooManyRequest is returned when client sends too many requests.
|
|
ErrCodeTooManyRequest = "TooManyRequests"
|
|
)
|