google.golang.org/grpc@v1.72.2/credentials/tls/certprovider/pemfile/builder.go (about) 1 /* 2 * 3 * Copyright 2020 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package pemfile 20 21 import ( 22 "encoding/json" 23 "fmt" 24 "time" 25 26 "google.golang.org/grpc/credentials/tls/certprovider" 27 "google.golang.org/protobuf/encoding/protojson" 28 "google.golang.org/protobuf/types/known/durationpb" 29 ) 30 31 const ( 32 // PluginName is the name of the PEM file watcher plugin. 33 PluginName = "file_watcher" 34 defaultRefreshInterval = 10 * time.Minute 35 ) 36 37 func init() { 38 certprovider.Register(&pluginBuilder{}) 39 } 40 41 type pluginBuilder struct{} 42 43 func (p *pluginBuilder) ParseConfig(c any) (*certprovider.BuildableConfig, error) { 44 data, ok := c.(json.RawMessage) 45 if !ok { 46 return nil, fmt.Errorf("meshca: unsupported config type: %T", c) 47 } 48 opts, err := pluginConfigFromJSON(data) 49 if err != nil { 50 return nil, err 51 } 52 return certprovider.NewBuildableConfig(PluginName, opts.canonical(), func(certprovider.BuildOptions) certprovider.Provider { 53 return newProvider(opts) 54 }), nil 55 } 56 57 func (p *pluginBuilder) Name() string { 58 return PluginName 59 } 60 61 func pluginConfigFromJSON(jd json.RawMessage) (Options, error) { 62 // The only difference between this anonymous struct and the Options struct 63 // is that the refresh_interval is represented here as a duration proto, 64 // while in the latter a time.Duration is used. 65 cfg := &struct { 66 CertificateFile string `json:"certificate_file,omitempty"` 67 PrivateKeyFile string `json:"private_key_file,omitempty"` 68 CACertificateFile string `json:"ca_certificate_file,omitempty"` 69 RefreshInterval json.RawMessage `json:"refresh_interval,omitempty"` 70 }{} 71 if err := json.Unmarshal(jd, cfg); err != nil { 72 return Options{}, fmt.Errorf("pemfile: json.Unmarshal(%s) failed: %v", string(jd), err) 73 } 74 75 opts := Options{ 76 CertFile: cfg.CertificateFile, 77 KeyFile: cfg.PrivateKeyFile, 78 RootFile: cfg.CACertificateFile, 79 // Refresh interval is the only field in the configuration for which we 80 // support a default value. We cannot possibly have valid defaults for 81 // file paths to watch. Also, it is valid to specify an empty path for 82 // some of those fields if the user does not want to watch them. 83 RefreshDuration: defaultRefreshInterval, 84 } 85 if cfg.RefreshInterval != nil { 86 dur := &durationpb.Duration{} 87 if err := protojson.Unmarshal(cfg.RefreshInterval, dur); err != nil { 88 return Options{}, fmt.Errorf("pemfile: protojson.Unmarshal(%+v) failed: %v", cfg.RefreshInterval, err) 89 } 90 opts.RefreshDuration = dur.AsDuration() 91 } 92 93 if err := opts.validate(); err != nil { 94 return Options{}, err 95 } 96 return opts, nil 97 }