Files
kandev/apps/backend/internal/canvas/release_projection.go
T
2026-09-23 20:51:55 +01:00

116 lines
3.7 KiB
Go

package canvas
import (
"encoding/json"
"sort"
plugininstances "github.com/kandev/kandev/internal/plugins/instances"
"github.com/kandev/kandev/internal/plugins/manifest"
)
func releaseMetadata(release plugininstances.Release, scope string, grants []plugininstances.Grant) *ReleaseMetadata {
permissions := ReleasePermissionSummary(release)
seed := releaseManifestSeed(release.ManifestJSON)
return &ReleaseMetadata{
ID: release.ID,
PackageID: seed.PackageID,
Version: seed.Version,
DisplayName: seed.DisplayName,
Description: seed.Description,
Author: seed.Author,
License: seed.License,
SourceMode: seed.SourceMode,
MinKandevVersion: seed.MinKandevVersion,
RepoURL: seed.RepoURL,
PackageDigest: release.PackageDigest,
ValidationStatus: release.ValidationStatus,
ValidationError: release.ValidationError,
Permissions: &permissions,
MissingPermissions: MissingPermissionKeys(permissions, scope, grants),
PermissionDigest: PermissionDigest(release),
SourceActorKind: release.SourceActorKind,
SourceUserID: release.SourceUserID,
SourceTaskID: release.SourceTaskID,
SourceSessionID: release.SourceSessionID,
ProtocolVersion: release.ProtocolVersion,
CreatedAt: release.CreatedAt,
}
}
// ReleaseMetadataForHTTP returns safe manifest fields for host projections.
func ReleaseMetadataForHTTP(release plugininstances.Release, scope string, grants []plugininstances.Grant) *ReleaseMetadata {
return releaseMetadata(release, scope, grants)
}
type manifestSeed struct {
PackageID string
Version string
DisplayName string
Description string
Author string
License string
SourceMode string
MinKandevVersion string
RepoURL string
}
func releaseManifestSeed(data json.RawMessage) manifestSeed {
var value manifest.Manifest
if err := json.Unmarshal(data, &value); err != nil {
return manifestSeed{}
}
seed := manifestSeed{
PackageID: value.ID,
Version: value.Version,
DisplayName: value.DisplayName,
Description: value.Description,
Author: value.Author,
MinKandevVersion: value.MinKandevVersion,
RepoURL: value.RepoURL,
}
if value.Distribution != nil {
seed.License = value.Distribution.License
seed.SourceMode = value.Distribution.SourceMode
}
return seed
}
func effectiveGrantProjection(instance plugininstances.Instance, summary PermissionSummary, grants []plugininstances.Grant) []GrantProjection {
if instance.ActiveReleaseID == "" {
return nil
}
declared := permissionKeys(summary)
result := make([]GrantProjection, 0, len(grants))
for _, grant := range grants {
if !grantScopeCovers(grant.ScopeCeiling, instance.EffectiveDataScopeKind()) {
continue
}
permission := grant.PermissionKind + ":" + grant.Resource
if grant.PermissionKind == permissionKindNetwork {
permission = permissionKindNetwork + ":" + grant.NetworkOrigin
}
if grant.PermissionKind == permissionKindState {
permission = permissionKindState
}
if !containsString(declared, permission) {
continue
}
result = append(result, GrantProjection{
PermissionKind: grant.PermissionKind,
Resource: grant.Resource,
NetworkOrigin: grant.NetworkOrigin,
ScopeCeiling: grant.ScopeCeiling,
})
}
sort.Slice(result, func(i, j int) bool {
if result[i].PermissionKind != result[j].PermissionKind {
return result[i].PermissionKind < result[j].PermissionKind
}
if result[i].Resource != result[j].Resource {
return result[i].Resource < result[j].Resource
}
return result[i].NetworkOrigin < result[j].NetworkOrigin
})
return result
}