forked from remote/oauth2
Update Output File behavior to match spec
This commit is contained in:
@@ -28,35 +28,36 @@ const (
|
||||
outputFileSource = "output file"
|
||||
)
|
||||
|
||||
type nonCacheableError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (nce nonCacheableError) Error() string {
|
||||
return nce.message
|
||||
}
|
||||
|
||||
func missingFieldError(source, field string) error {
|
||||
return fmt.Errorf("oauth2/google: %v missing `%v` field", source, field)
|
||||
}
|
||||
|
||||
func jsonParsingError(source string) error {
|
||||
return fmt.Errorf("oauth2/google: unable to parse %v JSON", source)
|
||||
func jsonParsingError(source, data string) error {
|
||||
return fmt.Errorf("oauth2/google: unable to parse %v\nResponse: %v", source, data)
|
||||
}
|
||||
|
||||
func malformedFailureError(source string) error {
|
||||
return fmt.Errorf("oauth2/google: %v must include `error` and `message` fields when unsuccessful", source)
|
||||
func malformedFailureError() error {
|
||||
return nonCacheableError{"oauth2/google: response must include `error` and `message` fields when unsuccessful"}
|
||||
}
|
||||
|
||||
func userDefinedError(source, code, message string) error {
|
||||
return fmt.Errorf("oauth2/google: %v contains unsuccessful response: (%v) %v", source, code, message)
|
||||
func userDefinedError(code, message string) error {
|
||||
return nonCacheableError{fmt.Sprintf("oauth2/google: response contains unsuccessful response: (%v) %v", code, message)}
|
||||
}
|
||||
|
||||
func unsupportedVersionError(source string, version int) error {
|
||||
return fmt.Errorf("oauth2/google: %v contains unsupported version: %v", source, version)
|
||||
}
|
||||
|
||||
type timeoutException struct {
|
||||
}
|
||||
|
||||
func (t timeoutException) Error() string {
|
||||
return "oauth2/google: the token returned by the executable is expired"
|
||||
}
|
||||
|
||||
func tokenExpiredError() error {
|
||||
return timeoutException{}
|
||||
return nonCacheableError{"oauth2/google: the token returned by the executable is expired"}
|
||||
}
|
||||
|
||||
func tokenTypeError(source string) error {
|
||||
@@ -155,7 +156,7 @@ type executableResponse struct {
|
||||
func parseSubjectTokenFromSource(response []byte, source string) (string, error) {
|
||||
var result executableResponse
|
||||
if err := json.Unmarshal(response, &result); err != nil {
|
||||
return "", jsonParsingError(source)
|
||||
return "", jsonParsingError(source, string(response))
|
||||
}
|
||||
|
||||
if result.Version == 0 {
|
||||
@@ -168,9 +169,9 @@ func parseSubjectTokenFromSource(response []byte, source string) (string, error)
|
||||
|
||||
if !*result.Success {
|
||||
if result.Code == "" || result.Message == "" {
|
||||
return "", malformedFailureError(source)
|
||||
return "", malformedFailureError()
|
||||
}
|
||||
return "", userDefinedError(source, result.Code, result.Message)
|
||||
return "", userDefinedError(result.Code, result.Message)
|
||||
}
|
||||
|
||||
if result.Version > executableSupportedMaxVersion || result.Version < 0 {
|
||||
@@ -227,8 +228,8 @@ func (cs executableCredentialSource) getTokenFromOutputFile() (string, error, bo
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(io.LimitReader(file, 1<<20))
|
||||
if err != nil {
|
||||
// An error reading the file. Not necessarily under the developer's control, so ignore it
|
||||
if err != nil || len(data) == 0 {
|
||||
// Cachefile exists, but no data found. Get new credential.
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
@@ -239,8 +240,9 @@ func (cs executableCredentialSource) getTokenFromOutputFile() (string, error, bo
|
||||
return token, nil, true
|
||||
}
|
||||
|
||||
if _, ok := err.(timeoutException); ok {
|
||||
// Cached token expired. Go through regular flow to find new token.
|
||||
if _, ok := err.(nonCacheableError); ok {
|
||||
// If the cached token is expired we need a new token,
|
||||
// and if the cache contains a failure, we need to try again.
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ func TestRetrieveExecutableSubjectTokenInvalidFormat(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
}
|
||||
if got, want := err.Error(), jsonParsingError(executableSource).Error(); got != want {
|
||||
if got, want := err.Error(), jsonParsingError(executableSource, "tokentokentoken").Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nReceived: %s\nExpected: %s", got, want)
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ func TestRetrieveExecutableSubjectTokenUnsuccessfulResponseWithFields(t *testing
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
}
|
||||
if got, want := err.Error(), userDefinedError(executableSource, "404", "Token Not Found").Error(); got != want {
|
||||
if got, want := err.Error(), userDefinedError("404", "Token Not Found").Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nReceived: %s\nExpected: %s", got, want)
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ func TestRetrieveExecutableSubjectTokenUnsuccessfulResponseWithCode(t *testing.T
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
}
|
||||
if got, want := err.Error(), malformedFailureError(executableSource).Error(); got != want {
|
||||
if got, want := err.Error(), malformedFailureError().Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nReceived: %s\nExpected: %s", got, want)
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ func TestRetrieveExecutableSubjectTokenUnsuccessfulResponseWithMessage(t *testin
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
}
|
||||
if got, want := err.Error(), malformedFailureError(executableSource).Error(); got != want {
|
||||
if got, want := err.Error(), malformedFailureError().Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nReceived: %s\nExpected: %s", got, want)
|
||||
}
|
||||
|
||||
@@ -603,7 +603,7 @@ func TestRetrieveExecutableSubjectTokenUnsuccessfulResponseWithoutFields(t *test
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
}
|
||||
if got, want := err.Error(), malformedFailureError(executableSource).Error(); got != want {
|
||||
if got, want := err.Error(), malformedFailureError().Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nReceived: %s\nExpected: %s", got, want)
|
||||
}
|
||||
|
||||
@@ -1149,7 +1149,7 @@ func TestRetrieveOutputFileSubjectTokenInvalidFormat(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
}
|
||||
if got, want := err.Error(), jsonParsingError(outputFileSource).Error(); got != want {
|
||||
if got, want := err.Error(), jsonParsingError(outputFileSource, "tokentokentoken").Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
}
|
||||
}
|
||||
@@ -1279,9 +1279,16 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithFields(t *testing
|
||||
|
||||
getenv = setEnvironment(map[string]string{"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES": "1"})
|
||||
now = setTime(defaultTime)
|
||||
deadline, deadlineSet := now(), false
|
||||
runCommand = func(ctx context.Context, command string, env []string) ([]byte, error) {
|
||||
t.Fatalf("Executable called when it should not have been")
|
||||
return []byte{}, nil
|
||||
deadline, deadlineSet = ctx.Deadline()
|
||||
return json.Marshal(executableResponse{
|
||||
Success: Bool(true),
|
||||
Version: 1,
|
||||
ExpirationTime: now().Unix() + 3600,
|
||||
TokenType: "urn:ietf:params:oauth:token-type:jwt",
|
||||
IdToken: "tokentokentoken",
|
||||
})
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(outputFile).Encode(executableResponse{
|
||||
@@ -1298,12 +1305,19 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithFields(t *testing
|
||||
t.Fatalf("parse() failed %v", err)
|
||||
}
|
||||
|
||||
_, err = base.subjectToken()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
out, err := base.subjectToken()
|
||||
if err != nil {
|
||||
t.Fatalf("retrieveSubjectToken() failed: %v", err)
|
||||
}
|
||||
if got, want := err.Error(), userDefinedError(outputFileSource, "404", "Token Not Found").Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
|
||||
if !deadlineSet {
|
||||
t.Errorf("Command run without a deadline")
|
||||
} else if deadline != now().Add(5*time.Second) {
|
||||
t.Errorf("Command run with incorrect deadline")
|
||||
}
|
||||
|
||||
if got, want := out, "tokentokentoken"; got != want {
|
||||
t.Errorf("Incorrect token received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1332,9 +1346,16 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithCode(t *testing.T
|
||||
|
||||
getenv = setEnvironment(map[string]string{"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES": "1"})
|
||||
now = setTime(defaultTime)
|
||||
deadline, deadlineSet := now(), false
|
||||
runCommand = func(ctx context.Context, command string, env []string) ([]byte, error) {
|
||||
t.Fatalf("Executable called when it should not have been")
|
||||
return []byte{}, nil
|
||||
deadline, deadlineSet = ctx.Deadline()
|
||||
return json.Marshal(executableResponse{
|
||||
Success: Bool(true),
|
||||
Version: 1,
|
||||
ExpirationTime: now().Unix() + 3600,
|
||||
TokenType: "urn:ietf:params:oauth:token-type:jwt",
|
||||
IdToken: "tokentokentoken",
|
||||
})
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(outputFile).Encode(executableResponse{
|
||||
@@ -1350,12 +1371,19 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithCode(t *testing.T
|
||||
t.Fatalf("parse() failed %v", err)
|
||||
}
|
||||
|
||||
_, err = base.subjectToken()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
out, err := base.subjectToken()
|
||||
if err != nil {
|
||||
t.Fatalf("retrieveSubjectToken() failed: %v", err)
|
||||
}
|
||||
if got, want := err.Error(), malformedFailureError(outputFileSource).Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
|
||||
if !deadlineSet {
|
||||
t.Errorf("Command run without a deadline")
|
||||
} else if deadline != now().Add(5*time.Second) {
|
||||
t.Errorf("Command run with incorrect deadline")
|
||||
}
|
||||
|
||||
if got, want := out, "tokentokentoken"; got != want {
|
||||
t.Errorf("Incorrect token received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1384,9 +1412,16 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithMessage(t *testin
|
||||
|
||||
getenv = setEnvironment(map[string]string{"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES": "1"})
|
||||
now = setTime(defaultTime)
|
||||
deadline, deadlineSet := now(), false
|
||||
runCommand = func(ctx context.Context, command string, env []string) ([]byte, error) {
|
||||
t.Fatalf("Executable called when it should not have been")
|
||||
return []byte{}, nil
|
||||
deadline, deadlineSet = ctx.Deadline()
|
||||
return json.Marshal(executableResponse{
|
||||
Success: Bool(true),
|
||||
Version: 1,
|
||||
ExpirationTime: now().Unix() + 3600,
|
||||
TokenType: "urn:ietf:params:oauth:token-type:jwt",
|
||||
IdToken: "tokentokentoken",
|
||||
})
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(outputFile).Encode(executableResponse{
|
||||
@@ -1402,12 +1437,19 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithMessage(t *testin
|
||||
t.Fatalf("parse() failed %v", err)
|
||||
}
|
||||
|
||||
_, err = base.subjectToken()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
out, err := base.subjectToken()
|
||||
if err != nil {
|
||||
t.Fatalf("retrieveSubjectToken() failed: %v", err)
|
||||
}
|
||||
if got, want := err.Error(), malformedFailureError(outputFileSource).Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
|
||||
if !deadlineSet {
|
||||
t.Errorf("Command run without a deadline")
|
||||
} else if deadline != now().Add(5*time.Second) {
|
||||
t.Errorf("Command run with incorrect deadline")
|
||||
}
|
||||
|
||||
if got, want := out, "tokentokentoken"; got != want {
|
||||
t.Errorf("Incorrect token received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1436,9 +1478,16 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithoutFields(t *test
|
||||
|
||||
getenv = setEnvironment(map[string]string{"GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES": "1"})
|
||||
now = setTime(defaultTime)
|
||||
deadline, deadlineSet := now(), false
|
||||
runCommand = func(ctx context.Context, command string, env []string) ([]byte, error) {
|
||||
t.Fatalf("Executable called when it should not have been")
|
||||
return []byte{}, nil
|
||||
deadline, deadlineSet = ctx.Deadline()
|
||||
return json.Marshal(executableResponse{
|
||||
Success: Bool(true),
|
||||
Version: 1,
|
||||
ExpirationTime: now().Unix() + 3600,
|
||||
TokenType: "urn:ietf:params:oauth:token-type:jwt",
|
||||
IdToken: "tokentokentoken",
|
||||
})
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(outputFile).Encode(executableResponse{
|
||||
@@ -1453,12 +1502,19 @@ func TestRetrieveOutputFileSubjectTokenUnsuccessfulResponseWithoutFields(t *test
|
||||
t.Fatalf("parse() failed %v", err)
|
||||
}
|
||||
|
||||
_, err = base.subjectToken()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found none")
|
||||
out, err := base.subjectToken()
|
||||
if err != nil {
|
||||
t.Fatalf("retrieveSubjectToken() failed: %v", err)
|
||||
}
|
||||
if got, want := err.Error(), malformedFailureError(outputFileSource).Error(); got != want {
|
||||
t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
|
||||
if !deadlineSet {
|
||||
t.Errorf("Command run without a deadline")
|
||||
} else if deadline != now().Add(5*time.Second) {
|
||||
t.Errorf("Command run with incorrect deadline")
|
||||
}
|
||||
|
||||
if got, want := out, "tokentokentoken"; got != want {
|
||||
t.Errorf("Incorrect token received.\nExpected: %s\nRecieved: %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user