From 82e63866f8bc91061be1358b7760a6c883e86408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Wed, 26 Mar 2025 18:55:47 +0100 Subject: [PATCH 1/2] Bump reva --- go.mod | 4 +- go.sum | 9 ++-- .../v2/pkg/appauth/manager/jsoncs3/jsoncs3.go | 33 ++++++++------ .../pkg/storage/pkg/decomposedfs/node/node.go | 36 +++++++++++----- vendor/github.com/rs/zerolog/CNAME | 1 - vendor/github.com/rs/zerolog/CONTRIBUTING.md | 43 +++++++++++++++++++ vendor/github.com/rs/zerolog/README.md | 31 +++++++++++++ vendor/github.com/rs/zerolog/_config.yml | 1 - vendor/github.com/rs/zerolog/console.go | 25 ++++++++--- vendor/github.com/rs/zerolog/log.go | 2 +- vendor/github.com/rs/zerolog/sampler.go | 5 ++- vendor/github.com/rs/zerolog/writer.go | 9 ++++ vendor/modules.txt | 4 +- 13 files changed, 162 insertions(+), 41 deletions(-) delete mode 100644 vendor/github.com/rs/zerolog/CNAME create mode 100644 vendor/github.com/rs/zerolog/CONTRIBUTING.md delete mode 100644 vendor/github.com/rs/zerolog/_config.yml diff --git a/go.mod b/go.mod index 589709c40..fc2b5ea3d 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( github.com/onsi/ginkgo/v2 v2.23.3 github.com/onsi/gomega v1.36.3 github.com/open-policy-agent/opa v1.2.0 - github.com/opencloud-eu/reva/v2 v2.28.1-0.20250325103543-f3ec73475a58 + github.com/opencloud-eu/reva/v2 v2.29.1 github.com/orcaman/concurrent-map v1.0.0 github.com/owncloud/libre-graph-api-go v1.0.5-0.20240829135935-80dc00d6f5ea github.com/pkg/errors v0.9.1 @@ -73,7 +73,7 @@ require ( github.com/riandyrn/otelchi v0.12.1 github.com/rogpeppe/go-internal v1.14.1 github.com/rs/cors v1.11.1 - github.com/rs/zerolog v1.33.0 + github.com/rs/zerolog v1.34.0 github.com/shamaton/msgpack/v2 v2.2.3 github.com/sirupsen/logrus v1.9.3 github.com/spf13/afero v1.14.0 diff --git a/go.sum b/go.sum index 1919099c0..255d2a280 100644 --- a/go.sum +++ b/go.sum @@ -865,8 +865,8 @@ github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/open-policy-agent/opa v1.2.0 h1:88NDVCM0of1eO6Z4AFeL3utTEtMuwloFmWWU7dRV1z0= github.com/open-policy-agent/opa v1.2.0/go.mod h1:30euUmOvuBoebRCcJ7DMF42bRBOPznvt0ACUMYDUGVY= -github.com/opencloud-eu/reva/v2 v2.28.1-0.20250325103543-f3ec73475a58 h1:sWVVkEAz3EQOigCRQqbpgd+YzArj6HWbVUyDqtj4Frw= -github.com/opencloud-eu/reva/v2 v2.28.1-0.20250325103543-f3ec73475a58/go.mod h1:BBTT/JIHofRQu1VdFStlXRlrwAMD3wCnVkNAx3jsfO8= +github.com/opencloud-eu/reva/v2 v2.29.1 h1:SgB2zn8d/3UWwFiJ0pUs85aDKJJ36JoKnyRM+iW+VoI= +github.com/opencloud-eu/reva/v2 v2.29.1/go.mod h1:+nkCU7w6E6cyNSsKRYj1rb0cCI7QswEQ7KOPljctebM= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -982,11 +982,10 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys= github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/appauth/manager/jsoncs3/jsoncs3.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/appauth/manager/jsoncs3/jsoncs3.go index 9ffc0de7c..ea3224978 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/appauth/manager/jsoncs3/jsoncs3.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/appauth/manager/jsoncs3/jsoncs3.go @@ -199,7 +199,8 @@ func (m *manager) ListAppPasswords(ctx context.Context) ([]*apppb.AppPassword, e userAppPasswordSlice := make([]*apppb.AppPassword, 0, len(userAppPasswords)) - for _, p := range userAppPasswords { + for id, p := range userAppPasswords { + p.Password = id userAppPasswordSlice = append(userAppPasswordSlice, p) } @@ -207,7 +208,7 @@ func (m *manager) ListAppPasswords(ctx context.Context) ([]*apppb.AppPassword, e } // InvalidateAppPassword invalidates a generated password. -func (m *manager) InvalidateAppPassword(ctx context.Context, secret string) error { +func (m *manager) InvalidateAppPassword(ctx context.Context, secretOrId string) error { log := appctx.GetLogger(ctx) ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "InvalidateAppPassword") defer span.End() @@ -225,17 +226,17 @@ func (m *manager) InvalidateAppPassword(ctx context.Context, secret string) erro } updater := func(a map[string]*apppb.AppPassword) (map[string]*apppb.AppPassword, error) { + // Allow deleting a token using the ID inside the password property. This is needed because of + // some shortcomings of the CS3 APIs. On the API level tokens don't have IDs + // ListAppPasswords in this backend returns the ID as the password value. + if _, ok := a[secretOrId]; ok { + delete(a, secretOrId) + return a, nil + } + + // Check if the supplied parameter matches any of the stored password tokens for key, pw := range a { - // Allow deleting a token using the password hash. This is needed because of - // some shortcomings of the CS3 APIs. On the API level tokens don't have IDs - // ListAppPasswords only returns the hashed password. So allowing to delete - // using the hashed password as the key is the only way to delete tokens for - // which the user does not remember the password. - if secret == pw.Password { - delete(a, key) - return a, nil - } - ok, err := argon2id.ComparePasswordAndHash(secret, pw.Password) + ok, err := argon2id.ComparePasswordAndHash(secretOrId, pw.Password) switch { case err != nil: log.Debug().Err(err).Msg("Error comparing password and hash") @@ -268,7 +269,10 @@ func (m *manager) GetAppPassword(ctx context.Context, user *userpb.UserId, secre errUpdateSkipped := errors.New("update skipped") - var matchedPw *apppb.AppPassword + var ( + matchedPw *apppb.AppPassword + matchedID string + ) updater := func(a map[string]*apppb.AppPassword) (map[string]*apppb.AppPassword, error) { matchedPw = nil for id, pw := range a { @@ -284,6 +288,7 @@ func (m *manager) GetAppPassword(ctx context.Context, user *userpb.UserId, secre } matchedPw = pw + matchedID = id // password not expired // Updating the Utime will cause an Upload for every single GetAppPassword request. We are limiting this to one // update per 5 minutes otherwise this backend will become unusable. @@ -302,6 +307,8 @@ func (m *manager) GetAppPassword(ctx context.Context, user *userpb.UserId, secre case err == nil: fallthrough case errors.Is(err, errUpdateSkipped): + // Don't return the hashed password, put the ID into the password field + matchedPw.Password = matchedID return matchedPw, nil } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go index 01167ec47..d087380cc 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node/node.go @@ -435,16 +435,32 @@ func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID string, canLis return nil, errtypes.InternalError("Missing parent ID on node") } - if revisionSuffix == "" { - n.BlobID, n.Blobsize, err = lu.ReadBlobIDAndSizeAttr(ctx, n, attrs) - if err != nil { - return nil, err - } - } else { - versionNode := NewBaseNode(spaceID, nodeID+RevisionIDDelimiter+revisionSuffix, lu) - n.BlobID, n.Blobsize, err = lu.ReadBlobIDAndSizeAttr(ctx, versionNode, nil) - if err != nil { - return nil, err + if n.Type(ctx) == provider.ResourceType_RESOURCE_TYPE_FILE { + if revisionSuffix == "" { + var ( + idOk, sizeOk bool + d []byte + err error + ) + + if d, idOk = attrs[prefixes.BlobIDAttr]; idOk { + n.BlobID = string(d) + } + if d, sizeOk = attrs[prefixes.BlobsizeAttr]; sizeOk { + n.Blobsize, err = strconv.ParseInt(string(d), 10, 64) + } + if err != nil || !idOk || !sizeOk { + n.BlobID, n.Blobsize, err = lu.ReadBlobIDAndSizeAttr(ctx, n, attrs) + if err != nil { + return nil, err + } + } + } else { + versionNode := NewBaseNode(spaceID, nodeID+RevisionIDDelimiter+revisionSuffix, lu) + n.BlobID, n.Blobsize, err = lu.ReadBlobIDAndSizeAttr(ctx, versionNode, nil) + if err != nil { + return nil, err + } } } diff --git a/vendor/github.com/rs/zerolog/CNAME b/vendor/github.com/rs/zerolog/CNAME deleted file mode 100644 index 9ce57a6eb..000000000 --- a/vendor/github.com/rs/zerolog/CNAME +++ /dev/null @@ -1 +0,0 @@ -zerolog.io \ No newline at end of file diff --git a/vendor/github.com/rs/zerolog/CONTRIBUTING.md b/vendor/github.com/rs/zerolog/CONTRIBUTING.md new file mode 100644 index 000000000..a09e9e219 --- /dev/null +++ b/vendor/github.com/rs/zerolog/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing to Zerolog + +Thank you for your interest in contributing to **Zerolog**! + +Zerolog is a **feature-complete**, high-performance logging library designed to be **lean** and **non-bloated**. The focus of ongoing development is on **bug fixes**, **performance improvements**, and **modernization efforts** (such as keeping up with Go best practices and compatibility with newer Go versions). + +## What We're Looking For + +We welcome contributions in the following areas: + +- **Bug Fixes**: If you find an issue or unexpected behavior, please open an issue and/or submit a fix. +- **Performance Optimizations**: Improvements that reduce memory usage, allocation count, or CPU cycles without introducing complexity are appreciated. +- **Modernization**: Compatibility updates for newer Go versions or idiomatic improvements that do not increase library size or complexity. +- **Documentation Enhancements**: Corrections, clarifications, and improvements to documentation or code comments. + +## What We're *Not* Looking For + +Zerolog is intended to remain **minimalistic and efficient**. Therefore, we are **not accepting**: + +- New features that add optional behaviors or extend API surface area. +- Built-in support for frameworks or external systems (e.g., bindings, integrations). +- General-purpose abstractions or configuration helpers. + +If you're unsure whether a change aligns with the project's philosophy, feel free to open an issue for discussion before submitting a PR. + +## Contributing Guidelines + +1. **Fork the repository** +2. **Create a branch** for your fix or improvement +3. **Write tests** to cover your changes +4. Ensure `go test ./...` passes +5. Run `go fmt` and `go vet` to ensure code consistency +6. **Submit a pull request** with a clear explanation of the motivation and impact + +## Code Style + +- Keep the code simple, efficient, and idiomatic. +- Avoid introducing new dependencies. +- Preserve backwards compatibility unless explicitly discussed. + +--- + +We appreciate your effort in helping us keep Zerolog fast, minimal, and reliable! diff --git a/vendor/github.com/rs/zerolog/README.md b/vendor/github.com/rs/zerolog/README.md index 1306a6c1c..9d4e8e8d2 100644 --- a/vendor/github.com/rs/zerolog/README.md +++ b/vendor/github.com/rs/zerolog/README.md @@ -366,6 +366,37 @@ log.Info().Str("foo", "bar").Msg("Hello World") // Output: 2006-01-02T15:04:05Z07:00 | INFO | ***Hello World**** foo:BAR ``` +To use custom advanced formatting: + +```go +output := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: true, + PartsOrder: []string{"level", "one", "two", "three", "message"}, + FieldsExclude: []string{"one", "two", "three"}} +output.FormatLevel = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("%-6s", i)) } +output.FormatFieldName = func(i interface{}) string { return fmt.Sprintf("%s:", i) } +output.FormatPartValueByName = func(i interface{}, s string) string { + var ret string + switch s { + case "one": + ret = strings.ToUpper(fmt.Sprintf("%s", i)) + case "two": + ret = strings.ToLower(fmt.Sprintf("%s", i)) + case "three": + ret = strings.ToLower(fmt.Sprintf("(%s)", i)) + } + return ret +} +log := zerolog.New(output) + +log.Info().Str("foo", "bar"). + Str("two", "TEST_TWO"). + Str("one", "test_one"). + Str("three", "test_three"). + Msg("Hello World") + +// Output: INFO TEST_ONE test_two (test_three) Hello World foo:bar +``` + ### Sub dictionary ```go diff --git a/vendor/github.com/rs/zerolog/_config.yml b/vendor/github.com/rs/zerolog/_config.yml deleted file mode 100644 index a1e896d7b..000000000 --- a/vendor/github.com/rs/zerolog/_config.yml +++ /dev/null @@ -1 +0,0 @@ -remote_theme: rs/gh-readme diff --git a/vendor/github.com/rs/zerolog/console.go b/vendor/github.com/rs/zerolog/console.go index 7e65e86f9..6c881ef60 100644 --- a/vendor/github.com/rs/zerolog/console.go +++ b/vendor/github.com/rs/zerolog/console.go @@ -47,6 +47,10 @@ const ( // Formatter transforms the input into a formatted string. type Formatter func(interface{}) string +// FormatterByFieldName transforms the input into a formatted string, +// being able to differentiate formatting based on field name. +type FormatterByFieldName func(interface{}, string) string + // ConsoleWriter parses the JSON input and writes it in an // (optionally) colorized, human-friendly format to Out. type ConsoleWriter struct { @@ -85,6 +89,9 @@ type ConsoleWriter struct { FormatFieldValue Formatter FormatErrFieldName Formatter FormatErrFieldValue Formatter + // If this is configured it is used for "part" values and + // has precedence on FormatFieldValue + FormatPartValueByName FormatterByFieldName FormatExtra func(map[string]interface{}, *bytes.Buffer) error @@ -282,8 +289,9 @@ func (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer // writePart appends a formatted part to buf. func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, p string) { var f Formatter + var fvn FormatterByFieldName - if w.PartsExclude != nil && len(w.PartsExclude) > 0 { + if len(w.PartsExclude) > 0 { for _, exclude := range w.PartsExclude { if exclude == p { return @@ -317,14 +325,21 @@ func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, f = w.FormatCaller } default: - if w.FormatFieldValue == nil { - f = consoleDefaultFormatFieldValue - } else { + if w.FormatPartValueByName != nil { + fvn = w.FormatPartValueByName + } else if w.FormatFieldValue != nil { f = w.FormatFieldValue + } else { + f = consoleDefaultFormatFieldValue } } - var s = f(evt[p]) + var s string + if f == nil { + s = fvn(evt[p], p) + } else { + s = f(evt[p]) + } if len(s) > 0 { if buf.Len() > 0 { diff --git a/vendor/github.com/rs/zerolog/log.go b/vendor/github.com/rs/zerolog/log.go index 6c1d4ead7..ea2cd62b5 100644 --- a/vendor/github.com/rs/zerolog/log.go +++ b/vendor/github.com/rs/zerolog/log.go @@ -494,7 +494,7 @@ func (l *Logger) newEvent(level Level, done func(string)) *Event { if level != NoLevel && LevelFieldName != "" { e.Str(LevelFieldName, LevelFieldMarshalFunc(level)) } - if l.context != nil && len(l.context) > 1 { + if len(l.context) > 1 { e.buf = enc.AppendObjectData(e.buf, l.context) } if l.stack { diff --git a/vendor/github.com/rs/zerolog/sampler.go b/vendor/github.com/rs/zerolog/sampler.go index 83ce2ed3a..19e04bd4b 100644 --- a/vendor/github.com/rs/zerolog/sampler.go +++ b/vendor/github.com/rs/zerolog/sampler.go @@ -47,6 +47,9 @@ type BasicSampler struct { // Sample implements the Sampler interface. func (s *BasicSampler) Sample(lvl Level) bool { n := s.N + if n == 0 { + return false + } if n == 1 { return true } @@ -87,7 +90,7 @@ func (s *BurstSampler) inc() uint32 { now := TimestampFunc().UnixNano() resetAt := atomic.LoadInt64(&s.resetAt) var c uint32 - if now > resetAt { + if now >= resetAt { c = 1 atomic.StoreUint32(&s.counter, c) newResetAt := now + s.Period.Nanoseconds() diff --git a/vendor/github.com/rs/zerolog/writer.go b/vendor/github.com/rs/zerolog/writer.go index 41b394d71..0fc5ff597 100644 --- a/vendor/github.com/rs/zerolog/writer.go +++ b/vendor/github.com/rs/zerolog/writer.go @@ -213,6 +213,15 @@ func (w *FilteredLevelWriter) WriteLevel(level Level, p []byte) (int, error) { return len(p), nil } +// Call the underlying writer's Close method if it is an io.Closer. Otherwise +// does nothing. +func (w *FilteredLevelWriter) Close() error { + if closer, ok := w.Writer.(io.Closer); ok { + return closer.Close() + } + return nil +} + var triggerWriterPool = &sync.Pool{ New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, 1024)) diff --git a/vendor/modules.txt b/vendor/modules.txt index 9f8595887..89ccbf919 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1198,7 +1198,7 @@ github.com/open-policy-agent/opa/v1/types github.com/open-policy-agent/opa/v1/util github.com/open-policy-agent/opa/v1/util/decoding github.com/open-policy-agent/opa/v1/version -# github.com/opencloud-eu/reva/v2 v2.28.1-0.20250325103543-f3ec73475a58 +# github.com/opencloud-eu/reva/v2 v2.29.1 ## explicit; go 1.24.1 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime @@ -1696,7 +1696,7 @@ github.com/rs/cors/internal # github.com/rs/xid v1.6.0 ## explicit; go 1.16 github.com/rs/xid -# github.com/rs/zerolog v1.33.0 +# github.com/rs/zerolog v1.34.0 ## explicit; go 1.15 github.com/rs/zerolog github.com/rs/zerolog/internal/cbor From d18549e3fa690ff7f2cb0be85407116e9f829694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Wed, 26 Mar 2025 19:22:24 +0100 Subject: [PATCH 2/2] Adapt test, the tokens API returns the ids as the tokens now --- tests/acceptance/features/apiAuthApp/token.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/acceptance/features/apiAuthApp/token.feature b/tests/acceptance/features/apiAuthApp/token.feature index 9545f6b82..e57586dec 100644 --- a/tests/acceptance/features/apiAuthApp/token.feature +++ b/tests/acceptance/features/apiAuthApp/token.feature @@ -58,7 +58,7 @@ Feature: create auth-app token ], "properties": { "token": { - "pattern": "^\\$argon2id\\$v=19\\$m=65536,t=1,p=16\\$.+$" + "pattern": "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$" }, "label": { "const": "Generated via API" @@ -75,7 +75,7 @@ Feature: create auth-app token ], "properties": { "token": { - "pattern": "^\\$argon2id\\$v=19\\$m=65536,t=1,p=16\\$.+$" + "pattern": "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$" }, "label": { "const": "Generated via CLI" @@ -92,7 +92,7 @@ Feature: create auth-app token ], "properties": { "token": { - "pattern": "^\\$argon2id\\$v=19\\$m=65536,t=1,p=16\\$.+$" + "pattern": "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$" }, "label": { "const": "Generated via API (Impersonation)"