Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion docs/grpc/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions docs/openapi/policy/objects.openapi.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

86 changes: 59 additions & 27 deletions lib/identifier/registered_resource_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,90 @@ import (
)

type FullyQualifiedRegisteredResourceValue struct {
Name string
Value string
Namespace string
Name string
Value string
}

// protovalidate already validates the FQN format in the service request
// for parsing purposes, we can just look for any non-whitespace characters
// e.g. should be in format of "https://reg_res/<name>/value/<value>"
// New FQN format: https://<namespace>/reg_res/<name>/value/<value>
var registeredResourceValueFqnRegex = regexp.MustCompile(
`^https:\/\/reg_res\/(?<name>[^\/]+)\/value\/(?<value>[^\/]+)$`,
`^https:\/\/(?P<namespace>[^\/]+)\/reg_res\/(?P<name>[^\/]+)\/value\/(?P<value>[^\/]+)$`,
)

// parseRegisteredResourceValueFqn parses a registered resource value FQN string into a FullyQualifiedRegisteredResourceValue struct.
// The FQN must be in the format: https://reg_res/<name>/value/<value>
func parseRegisteredResourceValueFqn(fqn string) (*FullyQualifiedRegisteredResourceValue, error) {
matches := registeredResourceValueFqnRegex.FindStringSubmatch(fqn)
// Legacy FQN format: https://reg_res/<name>/value/<value>
var legacyRegisteredResourceValueFqnRegex = regexp.MustCompile(
`^https:\/\/reg_res\/(?P<name>[^\/]+)\/value\/(?P<value>[^\/]+)$`,
)

// Check if we have matches first
// matchFqnParts attempts to match fqn against re, extracts named groups, lowercases them,
// and validates name/value with validObjectNameRegex. Returns nil if the regex doesn't match.
func matchFqnParts(re *regexp.Regexp, fqn string, groups []string) (map[string]string, error) {
matches := re.FindStringSubmatch(fqn)
if len(matches) == 0 {
return nil, fmt.Errorf("%w: FQN must be in format https://reg_res/<name>/value/<value>", ErrInvalidFQNFormat)
return nil, nil //nolint:nilnil // nil means no match, not an error
}
result := make(map[string]string, len(groups))
for _, g := range groups {
idx := re.SubexpIndex(g)
if idx == -1 || idx >= len(matches) {
return nil, fmt.Errorf("%w: missing group %s", ErrInvalidFQNFormat, g)
}
result[g] = strings.ToLower(matches[idx])
}
Comment thread
alkalescent marked this conversation as resolved.
if namespace, ok := result["namespace"]; ok {
if !validNamespaceRegex.MatchString(namespace) {
return nil, fmt.Errorf("%w: invalid namespace format %s", ErrInvalidFQNFormat, namespace)
}
}
name, value := result["name"], result["value"]
if !validObjectNameRegex.MatchString(name) || !validObjectNameRegex.MatchString(value) {
return nil, fmt.Errorf("%w: found name %s with value %s", ErrInvalidFQNFormat, name, value)
}
Comment thread
alkalescent marked this conversation as resolved.
return result, nil
}

nameIdx := registeredResourceValueFqnRegex.SubexpIndex("name")
valueIdx := registeredResourceValueFqnRegex.SubexpIndex("value")

if nameIdx == -1 || valueIdx == -1 || len(matches) <= nameIdx || len(matches) <= valueIdx {
return nil, fmt.Errorf("%w: valid FQN format of https://reg_res/<name>/value/<value> must be provided", ErrInvalidFQNFormat)
// parseRegisteredResourceValueFqn parses a registered resource value FQN string into a FullyQualifiedRegisteredResourceValue struct.
// Supports both the new format: https://<namespace>/reg_res/<name>/value/<value>
// and the legacy format: https://reg_res/<name>/value/<value>
func parseRegisteredResourceValueFqn(fqn string) (*FullyQualifiedRegisteredResourceValue, error) {
// Try new format: https://<namespace>/reg_res/<name>/value/<value>
if parts, err := matchFqnParts(registeredResourceValueFqnRegex, fqn, []string{"namespace", "name", "value"}); err != nil {
return nil, err
} else if parts != nil {
return &FullyQualifiedRegisteredResourceValue{Namespace: parts["namespace"], Name: parts["name"], Value: parts["value"]}, nil
}

name := strings.ToLower(matches[nameIdx])
value := strings.ToLower(matches[valueIdx])
isValid := validObjectNameRegex.MatchString(name) && validObjectNameRegex.MatchString(value)
if !isValid {
return nil, fmt.Errorf("%w: found name %s with value %s", ErrInvalidFQNFormat, name, value)
// Try legacy format: https://reg_res/<name>/value/<value>
if parts, err := matchFqnParts(legacyRegisteredResourceValueFqnRegex, fqn, []string{"name", "value"}); err != nil {
return nil, err
} else if parts != nil {
return &FullyQualifiedRegisteredResourceValue{Name: parts["name"], Value: parts["value"]}, nil
}

return &FullyQualifiedRegisteredResourceValue{
Name: name,
Value: value,
}, nil
return nil, fmt.Errorf("%w: FQN must be in format https://<namespace>/reg_res/<name>/value/<value>", ErrInvalidFQNFormat)
}

// Implementing FullyQualified interface for FullyQualifiedRegisteredResourceValue
func (rrv *FullyQualifiedRegisteredResourceValue) FQN() string {
builder := strings.Builder{}
builder.WriteString("https://reg_res/")
if rrv.Namespace != "" {
builder.WriteString("https://")
builder.WriteString(rrv.Namespace)
builder.WriteString("/reg_res/")
} else {
// Legacy format for backward compatibility
builder.WriteString("https://reg_res/")
}
builder.WriteString(rrv.Name)
builder.WriteString("/value/")
builder.WriteString(rrv.Value)
Comment thread
alkalescent marked this conversation as resolved.
return strings.ToLower(builder.String())
}

func (rrv *FullyQualifiedRegisteredResourceValue) Validate() error {
if rrv.Namespace != "" && !validNamespaceRegex.MatchString(rrv.Namespace) {
return fmt.Errorf("%w: invalid namespace format %s", ErrInvalidFQNFormat, rrv.Namespace)
}
if !validObjectNameRegex.MatchString(rrv.Name) {
return fmt.Errorf("%w: invalid resource name format %s", ErrInvalidFQNFormat, rrv.Name)
}
Expand Down
Loading
Loading