pii handling for restore config (#3896)

add pii concealer compliance for restore config structs.

---

#### Does this PR need a docs update or release note?

- [x]  No

#### Type of change

- [x] 🌻 Feature
This commit is contained in:
Keepers 2023-07-25 11:25:34 -06:00 committed by GitHub
parent c75ca7e41c
commit 92412645ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 9 deletions

View File

@ -36,7 +36,7 @@ func (ctrl *Controller) ConsumeRestoreCollections(
defer end() defer end()
ctx = graph.BindRateLimiterConfig(ctx, graph.LimiterCfg{Service: sels.PathService()}) ctx = graph.BindRateLimiterConfig(ctx, graph.LimiterCfg{Service: sels.PathService()})
ctx = clues.Add(ctx, "restore_config", restoreCfg) // TODO(rkeepers): needs PII control ctx = clues.Add(ctx, "restore_config", restoreCfg)
if len(dcs) == 0 { if len(dcs) == 0 {
return nil, clues.New("no data collections to restore") return nil, clues.New("no data collections to restore")

View File

@ -2,13 +2,17 @@ package control
import ( import (
"context" "context"
"encoding/json"
"fmt"
"strings" "strings"
"github.com/alcionai/clues"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"github.com/alcionai/corso/src/internal/common/dttm" "github.com/alcionai/corso/src/internal/common/dttm"
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/path"
) )
const ( const (
@ -39,24 +43,24 @@ const RootLocation = "/"
type RestoreConfig struct { type RestoreConfig struct {
// Defines the per-item collision handling policy. // Defines the per-item collision handling policy.
// Defaults to Skip. // Defaults to Skip.
OnCollision CollisionPolicy OnCollision CollisionPolicy `json:"onCollision"`
// ProtectedResource specifies which resource the data will be restored to. // ProtectedResource specifies which resource the data will be restored to.
// If empty, restores to the same resource that was backed up. // If empty, restores to the same resource that was backed up.
// Defaults to empty. // Defaults to empty.
ProtectedResource string ProtectedResource string `json:"protectedResource"`
// Location specifies the container into which the data will be restored. // Location specifies the container into which the data will be restored.
// Only accepts container names, does not accept IDs. // Only accepts container names, does not accept IDs.
// If empty or "/", data will get restored in place, beginning at the root. // If empty or "/", data will get restored in place, beginning at the root.
// Defaults to "Corso_Restore_<current_dttm>" // Defaults to "Corso_Restore_<current_dttm>"
Location string Location string `json:"location"`
// Drive specifies the name of the drive into which the data will be // Drive specifies the name of the drive into which the data will be
// restored. If empty, data is restored to the same drive that was backed // restored. If empty, data is restored to the same drive that was backed
// up. // up.
// Defaults to empty. // Defaults to empty.
Drive string Drive string `json:"drive"`
} }
func DefaultRestoreConfig(timeFormat dttm.TimeFormat) RestoreConfig { func DefaultRestoreConfig(timeFormat dttm.TimeFormat) RestoreConfig {
@ -90,3 +94,58 @@ func EnsureRestoreConfigDefaults(
return rc return rc
} }
// ---------------------------------------------------------------------------
// pii control
// ---------------------------------------------------------------------------
var (
// interface compliance required for handling PII
_ clues.Concealer = &RestoreConfig{}
_ fmt.Stringer = &RestoreConfig{}
// interface compliance for the observe package to display
// values without concealing PII.
_ clues.PlainStringer = &RestoreConfig{}
)
func (rc RestoreConfig) marshal() string {
bs, err := json.Marshal(rc)
if err != nil {
return "err marshalling"
}
return string(bs)
}
func (rc RestoreConfig) concealed() RestoreConfig {
return RestoreConfig{
OnCollision: rc.OnCollision,
ProtectedResource: clues.Hide(rc.ProtectedResource).Conceal(),
Location: path.LoggableDir(rc.Location),
Drive: clues.Hide(rc.Drive).Conceal(),
}
}
// Conceal produces a concealed representation of the config, suitable for
// logging, storing in errors, and other output.
func (rc RestoreConfig) Conceal() string {
return rc.concealed().marshal()
}
// Format produces a concealed representation of the config, even when
// used within a PrintF, suitable for logging, storing in errors,
// and other output.
func (rc RestoreConfig) Format(fs fmt.State, _ rune) {
fmt.Fprint(fs, rc.concealed())
}
// String returns a plain text version of the restoreConfig.
func (rc RestoreConfig) String() string {
return rc.PlainString()
}
// PlainString returns an unescaped, unmodified string of the restore configuration.
func (rc RestoreConfig) PlainString() string {
return rc.marshal()
}

View File

@ -225,13 +225,13 @@ func ValidateSite(item models.Siteable) error {
wURL := ptr.Val(item.GetWebUrl()) wURL := ptr.Val(item.GetWebUrl())
if len(wURL) == 0 { if len(wURL) == 0 {
return clues.New("missing webURL").With("site_id", id) // TODO: pii return clues.New("missing webURL").With("site_id", clues.Hide(id))
} }
// personal (ie: oneDrive) sites have to be filtered out server-side. // personal (ie: oneDrive) sites have to be filtered out server-side.
if strings.Contains(wURL, PersonalSitePath) { if strings.Contains(wURL, PersonalSitePath) {
return clues.Stack(ErrKnownSkippableCase). return clues.Stack(ErrKnownSkippableCase).
With("site_id", id, "site_web_url", wURL) // TODO: pii With("site_id", clues.Hide(id), "site_web_url", clues.Hide(wURL))
} }
name := ptr.Val(item.GetDisplayName()) name := ptr.Val(item.GetDisplayName())
@ -239,10 +239,10 @@ func ValidateSite(item models.Siteable) error {
// the built-in site at "https://{tenant-domain}/search" never has a name. // the built-in site at "https://{tenant-domain}/search" never has a name.
if strings.HasSuffix(wURL, "/search") { if strings.HasSuffix(wURL, "/search") {
return clues.Stack(ErrKnownSkippableCase). return clues.Stack(ErrKnownSkippableCase).
With("site_id", id, "site_web_url", wURL) // TODO: pii With("site_id", clues.Hide(id), "site_web_url", clues.Hide(wURL))
} }
return clues.New("missing site display name").With("site_id", id) return clues.New("missing site display name").With("site_id", clues.Hide(id))
} }
return nil return nil