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:
parent
c75ca7e41c
commit
92412645ec
@ -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")
|
||||||
|
|||||||
@ -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()
|
||||||
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user