config/identity.go (122 lines of code) (raw):

// Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package config import ( "encoding/base64" "encoding/json" "errors" "fmt" "reflect" "github.com/ghodss/yaml" ) // Type Identities is a Validatable collection of Identity objects. type Identities []Identity func (identities Identities) Validate(c *Config) error { usedNames := make(map[string]bool) for _, i := range identities { if usedNames[i.Name] { return fmt.Errorf("identity %v: multiple identities with the same name", i.Name) } if err := i.Validate(c); err != nil { return err } usedNames[i.Name] = true } return nil } func (identities Identities) Get(name string) *Identity { for i := range identities { if identities[i].Name == name { return &identities[i] } } return nil } type Identity struct { Name string `json:"name"` GCP *GCPIdentity `json:"gcp"` } func (i *Identity) Validate(c *Config) error { if i.Name == "" { return errors.New("identity: missing name") } types := 0 for _, v := range []Validatable{i.GCP} { if reflect.ValueOf(v).IsNil() { continue } if err := v.Validate(c); err != nil { return err } types++ } if types == 0 { return fmt.Errorf("identity %v: missing type configuration", i.Name) } if types > 1 { return fmt.Errorf("identity %v: multiple type configurations", i.Name) } return nil } // GCPIdentity holds configuration for identifying to Google Cloud Platform services. type GCPIdentity struct { ServiceAccountKey *LiteralServiceAccountKey `json:"serviceAccountKey"` EncodedServiceAccountKey *EncodedServiceAccountKey `json:"encodedServiceAccountKey"` } func (c *GCPIdentity) GetServiceAccountKey() []byte { if c.ServiceAccountKey != nil { return *c.ServiceAccountKey } if c.EncodedServiceAccountKey != nil { return *c.EncodedServiceAccountKey } return nil } func (i *GCPIdentity) Validate(c *Config) error { count := 0 if i.ServiceAccountKey != nil { count += 1 } if i.EncodedServiceAccountKey != nil { count += 1 } if count == 0 { return errors.New("identity: missing service account key") } if count > 1 { return errors.New("identity: too many service account keys") } return nil } // LiteralServiceAccountKey is a byte array type that can hold a literal json structure. // It validates that its value is valid json upon parsing. After parsing, the contents of the byte // array will be the original json text. type LiteralServiceAccountKey []byte func (k *LiteralServiceAccountKey) UnmarshalJSON(data []byte) error { if k == nil { return errors.New("UnmarshalJSON on nil pointer") } // First we try to parse the data as yaml/json tmp := make(map[string]interface{}) err := json.Unmarshal(data, &tmp) if err == nil { *k = append((*k)[0:0], data...) return nil } return errors.New("value is not valid json") } // EncodedServiceAccountKey is a byte array type that can hold a base64-encoded json structure. // It validates that its value is valid base64-encoded json upon parsing. Upon parsing, the contents // of the byte array will be the json text after base64 decoding is performed. type EncodedServiceAccountKey []byte // UnmarshalJSON sets *m to a copy of data. func (k *EncodedServiceAccountKey) UnmarshalJSON(data []byte) error { if k == nil { return errors.New("EncodedServiceAccountKey.UnmarshalJSON: nil pointer") } var decoded []byte var encodedStr string // First we decode the data into a string to get rid of any start and end quotes. err := yaml.Unmarshal(data, &encodedStr) if err != nil { return errors.New("EncodedServiceAccountKey.UnmarshalJSON: not a string value") } decoded, err = base64.StdEncoding.DecodeString(encodedStr) if err != nil { return errors.New("EncodedServiceAccountKey.UnmarshalJSON: not a valid base64 value") } // Make sure what we just decoded is valid json tmp := make(map[string]interface{}) err = json.Unmarshal(decoded, &tmp) if err != nil { return errors.New("EncodedServiceAccountKey.UnmarshalJSON: decoded value is not valid json") } *k = append((*k)[0:0], decoded...) return nil }