blob: f16bad711e1cbee5dfb7792a40cd6caa28a9b800 [file] [log] [blame]
package configschema
import (
"fmt"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/zclconf/go-cty/cty"
)
// copyAndExtendPath returns a copy of a cty.Path with some additional
// `cty.PathStep`s appended to its end, to simplify creating new child paths.
func copyAndExtendPath(path cty.Path, nextSteps ...cty.PathStep) cty.Path {
newPath := make(cty.Path, len(path), len(path)+len(nextSteps))
copy(newPath, path)
newPath = append(newPath, nextSteps...)
return newPath
}
// ValueMarks returns a set of path value marks for a given value and path,
// based on the sensitive flag for each attribute within the schema. Nested
// blocks are descended (if present in the given value).
func (b *Block) ValueMarks(val cty.Value, path cty.Path) []cty.PathValueMarks {
var pvm []cty.PathValueMarks
// We can mark attributes as sensitive even if the value is null
for name, attrS := range b.Attributes {
if attrS.Sensitive {
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
attrPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
pvm = append(pvm, cty.PathValueMarks{
Path: attrPath,
Marks: cty.NewValueMarks(marks.Sensitive),
})
}
}
// If the value is null, no other marks are possible
if val.IsNull() {
return pvm
}
// Extract marks for nested attribute type values
for name, attrS := range b.Attributes {
// If the attribute has no nested type, or the nested type doesn't
// contain any sensitive attributes, skip inspecting it
if attrS.NestedType == nil || !attrS.NestedType.ContainsSensitive() {
continue
}
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
attrPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
pvm = append(pvm, attrS.NestedType.ValueMarks(val.GetAttr(name), attrPath)...)
}
// Extract marks for nested blocks
for name, blockS := range b.BlockTypes {
// If our block doesn't contain any sensitive attributes, skip inspecting it
if !blockS.Block.ContainsSensitive() {
continue
}
blockV := val.GetAttr(name)
if blockV.IsNull() || !blockV.IsKnown() {
continue
}
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
blockPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
switch blockS.Nesting {
case NestingSingle, NestingGroup:
pvm = append(pvm, blockS.Block.ValueMarks(blockV, blockPath)...)
case NestingList, NestingMap, NestingSet:
for it := blockV.ElementIterator(); it.Next(); {
idx, blockEV := it.Element()
// Create a copy of the path, with this block instance's index
// step added, to add to our PathValueMarks slice
blockInstancePath := copyAndExtendPath(blockPath, cty.IndexStep{Key: idx})
morePaths := blockS.Block.ValueMarks(blockEV, blockInstancePath)
pvm = append(pvm, morePaths...)
}
default:
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
}
}
return pvm
}
// ValueMarks returns a set of path value marks for a given value and path,
// based on the sensitive flag for each attribute within the nested attribute.
// Attributes with nested types are descended (if present in the given value).
func (o *Object) ValueMarks(val cty.Value, path cty.Path) []cty.PathValueMarks {
var pvm []cty.PathValueMarks
if val.IsNull() || !val.IsKnown() {
return pvm
}
for name, attrS := range o.Attributes {
// Skip attributes which can never produce sensitive path value marks
if !attrS.Sensitive && (attrS.NestedType == nil || !attrS.NestedType.ContainsSensitive()) {
continue
}
switch o.Nesting {
case NestingSingle, NestingGroup:
// Create a path to this attribute
attrPath := copyAndExtendPath(path, cty.GetAttrStep{Name: name})
if attrS.Sensitive {
// If the entire attribute is sensitive, mark it so
pvm = append(pvm, cty.PathValueMarks{
Path: attrPath,
Marks: cty.NewValueMarks(marks.Sensitive),
})
} else {
// The attribute has a nested type which contains sensitive
// attributes, so recurse
pvm = append(pvm, attrS.NestedType.ValueMarks(val.GetAttr(name), attrPath)...)
}
case NestingList, NestingMap, NestingSet:
// For nested attribute types which have a non-single nesting mode,
// we add path value marks for each element of the collection
for it := val.ElementIterator(); it.Next(); {
idx, attrEV := it.Element()
attrV := attrEV.GetAttr(name)
// Create a path to this element of the attribute's collection. Note
// that the path is extended in opposite order to the iteration order
// of the loops: index into the collection, then the contained
// attribute name. This is because we have one type
// representing multiple collection elements.
attrPath := copyAndExtendPath(path, cty.IndexStep{Key: idx}, cty.GetAttrStep{Name: name})
if attrS.Sensitive {
// If the entire attribute is sensitive, mark it so
pvm = append(pvm, cty.PathValueMarks{
Path: attrPath,
Marks: cty.NewValueMarks(marks.Sensitive),
})
} else {
// The attribute has a nested type which contains sensitive
// attributes, so recurse
pvm = append(pvm, attrS.NestedType.ValueMarks(attrV, attrPath)...)
}
}
default:
panic(fmt.Sprintf("unsupported nesting mode %s", attrS.NestedType.Nesting))
}
}
return pvm
}