1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package externalaccount
16
17import (
18 "bytes"
19 "context"
20 "encoding/json"
21 "errors"
22 "fmt"
23 "os"
24
25 "cloud.google.com/go/auth/internal"
26 "cloud.google.com/go/auth/internal/credsfile"
27)
28
29const (
30 fileProviderType = "file"
31)
32
33type fileSubjectProvider struct {
34 File string
35 Format *credsfile.Format
36}
37
38func (sp *fileSubjectProvider) subjectToken(context.Context) (string, error) {
39 tokenFile, err := os.Open(sp.File)
40 if err != nil {
41 return "", fmt.Errorf("credentials: failed to open credential file %q: %w", sp.File, err)
42 }
43 defer tokenFile.Close()
44 tokenBytes, err := internal.ReadAll(tokenFile)
45 if err != nil {
46 return "", fmt.Errorf("credentials: failed to read credential file: %w", err)
47 }
48 tokenBytes = bytes.TrimSpace(tokenBytes)
49
50 if sp.Format == nil {
51 return string(tokenBytes), nil
52 }
53 switch sp.Format.Type {
54 case fileTypeJSON:
55 jsonData := make(map[string]interface{})
56 err = json.Unmarshal(tokenBytes, &jsonData)
57 if err != nil {
58 return "", fmt.Errorf("credentials: failed to unmarshal subject token file: %w", err)
59 }
60 val, ok := jsonData[sp.Format.SubjectTokenFieldName]
61 if !ok {
62 return "", errors.New("credentials: provided subject_token_field_name not found in credentials")
63 }
64 token, ok := val.(string)
65 if !ok {
66 return "", errors.New("credentials: improperly formatted subject token")
67 }
68 return token, nil
69 case fileTypeText:
70 return string(tokenBytes), nil
71 default:
72 return "", errors.New("credentials: invalid credential_source file format type: " + sp.Format.Type)
73 }
74}
75
76func (sp *fileSubjectProvider) providerType() string {
77 return fileProviderType
78}