/
errors.go
140 lines (122 loc) · 5 KB
/
errors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//go:build go1.18
// +build go1.18
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package azidentity
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/internal/errorinfo"
msal "github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
)
// errAuthenticationRequired indicates a credential's Authenticate method must be called to acquire a token
// because user interaction is required and the credential is configured not to automatically prompt the user.
var errAuthenticationRequired error = &credentialUnavailableError{"can't acquire a token without user interaction. Call Authenticate to interactively authenticate a user"}
// getResponseFromError retrieves the response carried by
// an AuthenticationFailedError or MSAL CallErr, if any
func getResponseFromError(err error) *http.Response {
var a *AuthenticationFailedError
var c msal.CallErr
var res *http.Response
if errors.As(err, &c) {
res = c.Resp
} else if errors.As(err, &a) {
res = a.RawResponse
}
return res
}
// AuthenticationFailedError indicates an authentication request has failed.
type AuthenticationFailedError struct {
// RawResponse is the HTTP response motivating the error, if available.
RawResponse *http.Response
credType string
message string
err error
}
func newAuthenticationFailedError(credType string, message string, resp *http.Response, err error) error {
return &AuthenticationFailedError{credType: credType, message: message, RawResponse: resp, err: err}
}
// Error implements the error interface. Note that the message contents are not contractual and can change over time.
func (e *AuthenticationFailedError) Error() string {
if e.RawResponse == nil {
return e.credType + ": " + e.message
}
msg := &bytes.Buffer{}
fmt.Fprintf(msg, e.credType+" authentication failed\n")
if e.RawResponse.Request != nil {
fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
} else {
// this happens when the response is created from a custom HTTP transporter,
// which doesn't guarantee to bind the original request to the response
fmt.Fprintln(msg, "Request information not available")
}
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
fmt.Fprintf(msg, "RESPONSE %s\n", e.RawResponse.Status)
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
body, err := runtime.Payload(e.RawResponse)
switch {
case err != nil:
fmt.Fprintf(msg, "Error reading response body: %v", err)
case len(body) > 0:
if err := json.Indent(msg, body, "", " "); err != nil {
// failed to pretty-print so just dump it verbatim
fmt.Fprint(msg, string(body))
}
default:
fmt.Fprint(msg, "Response contained no body")
}
fmt.Fprintln(msg, "\n--------------------------------------------------------------------------------")
var anchor string
switch e.credType {
case credNameAzureCLI:
anchor = "azure-cli"
case credNameAzureDeveloperCLI:
anchor = "azd"
case credNameCert:
anchor = "client-cert"
case credNameSecret:
anchor = "client-secret"
case credNameManagedIdentity:
anchor = "managed-id"
case credNameUserPassword:
anchor = "username-password"
case credNameWorkloadIdentity:
anchor = "workload"
}
if anchor != "" {
fmt.Fprintf(msg, "To troubleshoot, visit https://aka.ms/azsdk/go/identity/troubleshoot#%s", anchor)
}
return msg.String()
}
// NonRetriable indicates the request which provoked this error shouldn't be retried.
func (*AuthenticationFailedError) NonRetriable() {
// marker method
}
var _ errorinfo.NonRetriable = (*AuthenticationFailedError)(nil)
// credentialUnavailableError indicates a credential can't attempt authentication because it lacks required
// data or state
type credentialUnavailableError struct {
message string
}
// newCredentialUnavailableError is an internal helper that ensures consistent error message formatting
func newCredentialUnavailableError(credType, message string) error {
msg := fmt.Sprintf("%s: %s", credType, message)
return &credentialUnavailableError{msg}
}
// NewCredentialUnavailableError constructs an error indicating a credential can't attempt authentication
// because it lacks required data or state. When [ChainedTokenCredential] receives this error it will try
// its next credential, if any.
func NewCredentialUnavailableError(message string) error {
return &credentialUnavailableError{message}
}
// Error implements the error interface. Note that the message contents are not contractual and can change over time.
func (e *credentialUnavailableError) Error() string {
return e.message
}
// NonRetriable is a marker method indicating this error should not be retried. It has no implementation.
func (e *credentialUnavailableError) NonRetriable() {}
var _ errorinfo.NonRetriable = (*credentialUnavailableError)(nil)