Files
ziti/controller/db/util.go
Paul Lorenz a582fe5d0f Add role-attribute usage queries. Fixes #1593
- adds management API endpoints listing role-attribute usage for identities,
  edge routers, services, and posture checks, reporting per-source counts
  (and optionally ids via withIds) across home-entity collections and the
  policies that reference each attribute
- adds RoleAttributeUsage model with QueryRoleAttributeUsage, mapping each
  RoleAttributeKind to its contributing sources, with all reads in a single
  transaction so counts stay consistent with the attribute list; returns an
  empty result (not a panic) for kinds with no attributes and an error for
  unknown kinds
- adds boltz.SetIndexValueTransform and AddSetIndexWithTransform, letting a
  SetIndex filter and rewrite symbol values without persisting a derived field
- adds derived role-attribute set indexes to the service policy, edge router
  policy, and service edge router policy stores, built from existing role
  fields via a role-attribute-only transform that excludes the #all wildcard
- adds a db migration that backfills the new indexes via each index's own
  CheckIntegrity(fix=true), scoped to the role-attribute indexes only, with a
  per-index summary log, stopping at the first error
- tests the new endpoints via the typed edge-api management client
2026-06-10 12:39:26 -04:00

105 lines
3.2 KiB
Go

/*
Copyright NetFoundry 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 db
import (
"fmt"
"strings"
"github.com/openziti/foundation/v2/errorz"
"github.com/openziti/foundation/v2/stringz"
"github.com/openziti/ziti/v2/controller/storage/boltz"
"github.com/pkg/errors"
)
const (
RolePrefix = "#"
EntityPrefix = "@"
AllRole = "#all"
)
func validateRolesAndIds(field string, values []string) error {
if len(values) > 1 && stringz.Contains(values, AllRole) {
return errorz.NewFieldError(fmt.Sprintf("if using %v, it should be the only role specified", AllRole), field, values)
}
var invalidKeys []string
for _, entry := range values {
if !strings.HasPrefix(entry, RolePrefix) && !strings.HasPrefix(entry, EntityPrefix) {
invalidKeys = append(invalidKeys, entry)
}
}
if len(invalidKeys) > 0 {
return errorz.NewFieldError("role entries must prefixed with # (to indicate role attributes) or @ (to indicate a name or id)", field, invalidKeys)
}
return nil
}
func splitRolesAndIds(values []string) ([]string, []string, error) {
var roles []string
var ids []string
for _, entry := range values {
if strings.HasPrefix(entry, RolePrefix) {
entry = strings.TrimPrefix(entry, RolePrefix)
roles = append(roles, entry)
} else if strings.HasPrefix(entry, EntityPrefix) {
entry = strings.TrimPrefix(entry, EntityPrefix)
ids = append(ids, entry)
} else {
return nil, nil, errors.Errorf("'%v' is neither role attribute (prefixed with %v) or an entity id or name (prefixed with %v)",
entry, RolePrefix, EntityPrefix)
}
}
return roles, ids, nil
}
// roleAttributeOnlyTransform is a boltz SetIndex value transform that keeps
// only role-attribute entries (values prefixed with RolePrefix, with a
// non-empty suffix) and strips the prefix. It's used to build derived set
// indexes over policy role fields that mix role-attribute refs ("#attr")
// and entity refs ("@id").
//
// The "#all" wildcard is excluded: it is not a reference to a role attribute
// named "all" but a policy-level "match everything" marker, so indexing it
// would surface a phantom "all" attribute and miscount any real entity
// attribute that happens to be named "all".
var roleAttributeOnlyTransform boltz.SetIndexValueTransform = func(ft boltz.FieldType, v []byte) (bool, boltz.FieldType, []byte) {
if ft != boltz.TypeString || len(v) < 2 || v[0] != RolePrefix[0] {
return false, ft, v
}
if string(v) == AllRole {
return false, ft, v
}
return true, ft, v[1:]
}
func FieldValuesToIds(new []boltz.FieldTypeAndValue) []string {
var entityRoles []string
for _, fv := range new {
entityRoles = append(entityRoles, string(fv.Value))
}
return entityRoles
}
func roleRef(val string) string {
return RolePrefix + val
}
func entityRef(val string) string {
return EntityPrefix + val
}