功能修改
This commit is contained in:
@@ -20,6 +20,7 @@ const (
|
||||
maxServerConfigContentSize = 64 * 1024
|
||||
maxJobExecutionContentSize = 64 * 1024
|
||||
maxLogicalFileKeyLength = 160
|
||||
maxProductionMessageLength = 320
|
||||
)
|
||||
|
||||
type ValidationError struct {
|
||||
@@ -146,11 +147,14 @@ func ValidateGamePlugin(plugin domain.GamePlugin) error {
|
||||
violations = append(violations, validatePluginPages(plugin.Pages)...)
|
||||
violations = append(violations, duplicateViolations("tags", plugin.Tags)...)
|
||||
violations = append(violations, validateAIPurposes(plugin.AIPurposes)...)
|
||||
violations = append(violations, validateProductionLifecycle("productionLifecycle", plugin.ProductionLifecycle, true)...)
|
||||
violations = append(violations, validateRemoteAccess("remoteAccess", plugin.RemoteAccess, plugin.RequiredRunCapabilities)...)
|
||||
if err := ValidateGamePluginRuntimeProfiles(plugin.RuntimeProfiles); err != nil {
|
||||
violations = append(violations, err.Error())
|
||||
}
|
||||
violations = append(violations, validateRuntimeProfileCapabilityDeclarations(plugin.RuntimeProfiles, plugin.RequiredRunCapabilities)...)
|
||||
violations = append(violations, validateRuntimeLogEventPermissionDeclarations("runtimeProfiles.logEvents", plugin.RuntimeProfiles, plugin.DeclaredPermissions)...)
|
||||
violations = append(violations, validateGameClientBridgeManifest("gameClientBridge", plugin.GameClientBridge, plugin.DeclaredPermissions, plugin.Pages, plugin.RuntimeProfiles)...)
|
||||
violations = append(violations, validateSafePluginStrings("gamePlugin", pluginSafeStrings(plugin))...)
|
||||
return finish(violations)
|
||||
}
|
||||
@@ -202,15 +206,262 @@ func ValidateGamePluginManifestRegistration(registration domain.GamePluginManife
|
||||
violations = append(violations, validatePluginPages(manifest.Pages)...)
|
||||
violations = append(violations, duplicateViolations("manifest.tags", manifest.Tags)...)
|
||||
violations = append(violations, validateAIPurposes(manifest.AI.Purposes)...)
|
||||
violations = append(violations, validateProductionLifecycle("manifest.productionLifecycle", manifest.ProductionLifecycle, true)...)
|
||||
if containsString(manifest.Permissions, "ai.invoke") || len(manifest.AI.Purposes) > 0 {
|
||||
if manifest.AI.Mediation != "platform" {
|
||||
violations = append(violations, "manifest.ai.mediation must be platform")
|
||||
}
|
||||
if manifest.AI.ConfigWritePolicy != "review-required" {
|
||||
violations = append(violations, "manifest.ai.configWritePolicy must be review-required")
|
||||
}
|
||||
}
|
||||
violations = append(violations, validateRemoteAccess("manifest.remoteAccess", manifest.RemoteAccess, manifest.Capabilities)...)
|
||||
if err := ValidateGamePluginRuntimeProfiles(manifest.RuntimeProfiles); err != nil {
|
||||
violations = append(violations, err.Error())
|
||||
}
|
||||
violations = append(violations, validateRuntimeProfileCapabilityDeclarations(manifest.RuntimeProfiles, manifest.Capabilities)...)
|
||||
violations = append(violations, validateRuntimeLogEventPermissionDeclarations("manifest.runtimeProfiles.logEvents", manifest.RuntimeProfiles, manifest.Permissions)...)
|
||||
violations = append(violations, validateGameClientBridgeManifest("manifest.gameClientBridge", manifest.GameClientBridge, manifest.Permissions, manifest.Pages, manifest.RuntimeProfiles)...)
|
||||
violations = append(violations, validateSafePluginStrings("manifest", manifestSafeStrings(registration))...)
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func validateGameClientBridgeManifest(field string, bridge domain.GameClientBridgeManifest, permissions []string, pages []domain.GamePluginPage, runtimeProfiles domain.GamePluginRuntimeProfiles) []string {
|
||||
companionPresent := bridge.Companion != (domain.GameClientBridgeCompanionDeclaration{})
|
||||
if len(bridge.Commands) == 0 && len(bridge.Snapshots) == 0 && len(bridge.QueryTemplates) == 0 && len(bridge.Pages) == 0 && bridge.Retention.KeepForSeconds == 0 && bridge.Retention.MaxRecords == 0 && !companionPresent {
|
||||
return nil
|
||||
}
|
||||
var violations []string
|
||||
if bridge.Retention.KeepForSeconds <= 0 || bridge.Retention.KeepForSeconds > 365*24*60*60 {
|
||||
violations = append(violations, field+".commandRetentionSeconds is invalid")
|
||||
}
|
||||
if bridge.Retention.MaxRecords <= 0 || bridge.Retention.MaxRecords > 100000 {
|
||||
violations = append(violations, field+".maxCommands is invalid")
|
||||
}
|
||||
if companionPresent {
|
||||
prefix := field + ".companion"
|
||||
companion := bridge.Companion
|
||||
if !clientManagerIdentifierPattern.MatchString(companion.ProfileKey) || !clientManagerIdentifierPattern.MatchString(companion.ConfigTemplateKey) {
|
||||
violations = append(violations, prefix+" profile or config template key is invalid")
|
||||
}
|
||||
if !safeRelativeJSONRef(companion.ConfigSchemaRef) {
|
||||
violations = append(violations, prefix+".configSchemaRef must be a safe relative JSON reference")
|
||||
}
|
||||
if companion.ConfigFormat != "yaml" || companion.PlatformBaseURLSource != "run-control" || companion.RegistrationProof != "hmac-sha256" || companion.ProofMaterialSource != "component-package" || companion.SessionMode != "component-session" || companion.TLSPolicy != "verify-system-roots" {
|
||||
violations = append(violations, prefix+" bootstrap security policy is invalid")
|
||||
}
|
||||
if !validCompanionProofEnvironment(companion.ProofMaterialEnv) {
|
||||
violations = append(violations, prefix+".proofMaterialEnv is invalid")
|
||||
}
|
||||
if companion.HeartbeatIntervalSeconds < 5 || companion.HeartbeatIntervalSeconds > 300 || companion.CommandPollIntervalSeconds < 1 || companion.CommandPollIntervalSeconds > 60 || companion.RequestTimeoutSeconds < 1 || companion.RequestTimeoutSeconds > 60 {
|
||||
violations = append(violations, prefix+" timing policy is invalid")
|
||||
}
|
||||
managerFound := false
|
||||
for _, manager := range runtimeProfiles.ClientManagers {
|
||||
if manager.Key != companion.ProfileKey {
|
||||
continue
|
||||
}
|
||||
managerFound = true
|
||||
if manager.Health.IntervalSeconds != companion.HeartbeatIntervalSeconds {
|
||||
violations = append(violations, prefix+".heartbeatIntervalSeconds must match the Client Manager health interval")
|
||||
}
|
||||
for _, capability := range []string{"component.register", "component.heartbeat", "component.health", "game-client.bridge"} {
|
||||
if !containsString(manager.Health.RequiredCapabilities, capability) {
|
||||
violations = append(violations, prefix+" requires Client Manager capability "+capability)
|
||||
}
|
||||
}
|
||||
templateFound := false
|
||||
for _, template := range manager.ConfigTemplates {
|
||||
if template.Key == companion.ConfigTemplateKey {
|
||||
templateFound = true
|
||||
if template.OutputRef != "config.yaml" {
|
||||
violations = append(violations, prefix+" config template must materialize config.yaml")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !templateFound {
|
||||
violations = append(violations, prefix+".configTemplateKey must reference the Client Manager profile")
|
||||
}
|
||||
}
|
||||
if !managerFound {
|
||||
violations = append(violations, prefix+".profileKey must reference a declared Client Manager profile")
|
||||
}
|
||||
}
|
||||
commandTypes := map[string]struct{}{}
|
||||
for index, command := range bridge.Commands {
|
||||
prefix := fmt.Sprintf("%s.commands[%d]", field, index)
|
||||
if !clientManagerIdentifierPattern.MatchString(command.Type) || unsafeGameClientBridgeCommandType(command.Type) {
|
||||
violations = append(violations, prefix+".type is invalid or unsafe")
|
||||
}
|
||||
if _, exists := commandTypes[command.Type]; exists {
|
||||
violations = append(violations, prefix+".type is duplicated")
|
||||
}
|
||||
commandTypes[command.Type] = struct{}{}
|
||||
if strings.TrimSpace(command.Title) == "" || len([]rune(command.Title)) > 80 {
|
||||
violations = append(violations, prefix+".title is invalid")
|
||||
}
|
||||
if !containsString(permissions, command.Permission) {
|
||||
violations = append(violations, prefix+".permission must be declared by the plugin")
|
||||
}
|
||||
if command.ApprovalLevel != domain.GameClientBridgeApprovalLevelNone && command.ApprovalLevel != domain.GameClientBridgeApprovalLevelOperator && command.ApprovalLevel != domain.GameClientBridgeApprovalLevelPlatformAdmin {
|
||||
violations = append(violations, prefix+".approvalLevel is invalid")
|
||||
}
|
||||
if !safeRelativeJSONRef(command.PayloadSchemaRef) || command.ResultSchemaRef != "" && !safeRelativeJSONRef(command.ResultSchemaRef) {
|
||||
violations = append(violations, prefix+" schema references must be safe relative JSON references")
|
||||
}
|
||||
if command.TimeoutSeconds <= 0 || command.TimeoutSeconds > 3600 {
|
||||
violations = append(violations, prefix+".timeoutSeconds is invalid")
|
||||
}
|
||||
if command.MaxPayloadBytes <= 0 || command.MaxPayloadBytes > maxGameClientBridgePayloadSize {
|
||||
violations = append(violations, prefix+".maxPayloadBytes is invalid")
|
||||
}
|
||||
}
|
||||
snapshotTypes := map[string]struct{}{}
|
||||
for index, snapshot := range bridge.Snapshots {
|
||||
prefix := fmt.Sprintf("%s.snapshots[%d]", field, index)
|
||||
key := snapshot.Type + "\x00" + snapshot.SchemaVersion
|
||||
if !clientManagerIdentifierPattern.MatchString(snapshot.Type) || !clientManagerIdentifierPattern.MatchString(snapshot.SchemaVersion) {
|
||||
violations = append(violations, prefix+" type or schemaVersion is invalid")
|
||||
}
|
||||
if _, exists := snapshotTypes[key]; exists {
|
||||
violations = append(violations, prefix+" type and schemaVersion are duplicated")
|
||||
}
|
||||
snapshotTypes[key] = struct{}{}
|
||||
if !safeRelativeJSONRef(snapshot.SchemaRef) {
|
||||
violations = append(violations, prefix+".schemaRef must be a safe relative JSON reference")
|
||||
}
|
||||
if snapshot.Retention.KeepForSeconds <= 0 || snapshot.Retention.KeepForSeconds > 31*24*60*60 || snapshot.Retention.MaxRecords <= 0 || snapshot.Retention.MaxRecords > 10000 {
|
||||
violations = append(violations, prefix+" retention is invalid")
|
||||
}
|
||||
}
|
||||
queryTemplates := map[string]domain.GameClientBridgeQueryTemplateDeclaration{}
|
||||
transports := map[string]domain.RuntimeTransportProfile{}
|
||||
for _, transport := range runtimeProfiles.TransportProfiles {
|
||||
transports[transport.Key] = transport
|
||||
}
|
||||
for index, template := range bridge.QueryTemplates {
|
||||
prefix := fmt.Sprintf("%s.queryTemplates[%d]", field, index)
|
||||
if !clientManagerIdentifierPattern.MatchString(template.Key) {
|
||||
violations = append(violations, prefix+".key is invalid")
|
||||
}
|
||||
if _, exists := queryTemplates[template.Key]; exists {
|
||||
violations = append(violations, prefix+".key is duplicated")
|
||||
}
|
||||
queryTemplates[template.Key] = template
|
||||
if strings.TrimSpace(template.Title) == "" || len([]rune(template.Title)) > 80 {
|
||||
violations = append(violations, prefix+".title is invalid")
|
||||
}
|
||||
if !containsString(permissions, template.Permission) {
|
||||
violations = append(violations, prefix+".permission must be declared by the plugin")
|
||||
}
|
||||
if template.Engine != "sqlite" {
|
||||
violations = append(violations, prefix+".engine must be sqlite")
|
||||
}
|
||||
if !safeRelativeJSONRef(template.ParameterSchemaRef) || !safeRelativeJSONRef(template.ResultSchemaRef) {
|
||||
violations = append(violations, prefix+" schema references must be safe relative JSON references")
|
||||
}
|
||||
if template.MaxRows < 1 || template.MaxRows > 500 {
|
||||
violations = append(violations, prefix+".maxRows is invalid")
|
||||
}
|
||||
if template.TimeoutSeconds < 1 || template.TimeoutSeconds > 60 {
|
||||
violations = append(violations, prefix+".timeoutSeconds is invalid")
|
||||
}
|
||||
transport, exists := transports[template.TransportKey]
|
||||
if !exists {
|
||||
violations = append(violations, prefix+".transportKey must reference a declared runtime transport profile")
|
||||
continue
|
||||
}
|
||||
if transport.TargetKey != template.TargetKey || strings.TrimSpace(template.TargetKey) == "" {
|
||||
violations = append(violations, prefix+".targetKey must match the declared runtime transport profile")
|
||||
}
|
||||
if transport.Kind != "sqlite" || !containsString(transport.Capabilities, domain.JobCapabilityRemoteRunDBSQLiteQuery) {
|
||||
violations = append(violations, prefix+" transport must be sqlite with remote.run.db.sqlite.query capability")
|
||||
}
|
||||
}
|
||||
pageDeclarations := map[string]domain.GamePluginPage{}
|
||||
for _, page := range pages {
|
||||
pageDeclarations[page.Key] = page
|
||||
}
|
||||
for index, page := range bridge.Pages {
|
||||
prefix := fmt.Sprintf("%s.pages[%d]", field, index)
|
||||
pageDeclaration, pageExists := pageDeclarations[page.PageKey]
|
||||
if !pageExists {
|
||||
violations = append(violations, prefix+".pageKey must reference a declared plugin page")
|
||||
}
|
||||
for _, commandType := range page.CommandTypes {
|
||||
if _, exists := commandTypes[commandType]; !exists {
|
||||
violations = append(violations, prefix+" references undeclared command "+commandType)
|
||||
}
|
||||
}
|
||||
for _, snapshotType := range page.SnapshotTypes {
|
||||
found := false
|
||||
for key := range snapshotTypes {
|
||||
if strings.HasPrefix(key, snapshotType+"\x00") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
violations = append(violations, prefix+" references undeclared snapshot "+snapshotType)
|
||||
}
|
||||
}
|
||||
for _, templateKey := range page.QueryTemplateKeys {
|
||||
template, exists := queryTemplates[templateKey]
|
||||
if !exists {
|
||||
violations = append(violations, prefix+" references undeclared query template "+templateKey)
|
||||
continue
|
||||
}
|
||||
if !containsString(pageDeclaration.Permissions, template.Permission) {
|
||||
violations = append(violations, prefix+" must declare query template permission "+template.Permission)
|
||||
}
|
||||
if !containsString(pageDeclaration.BridgeActions, string(domain.PluginBridgeActionRemoteAccessRequest)) {
|
||||
violations = append(violations, prefix+" must declare remote.access.request for query templates")
|
||||
}
|
||||
}
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validCompanionProofEnvironment(value string) bool {
|
||||
if len(value) < 3 || len(value) > 64 || value[0] < 'A' || value[0] > 'Z' {
|
||||
return false
|
||||
}
|
||||
for _, character := range value[1:] {
|
||||
if character != '_' && (character < 'A' || character > 'Z') && (character < '0' || character > '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
reserved := map[string]struct{}{
|
||||
"COMSPEC": {}, "DYLD_INSERT_LIBRARIES": {}, "DYLD_LIBRARY_PATH": {}, "HOME": {}, "LD_LIBRARY_PATH": {}, "LD_PRELOAD": {},
|
||||
"PATH": {}, "PATHEXT": {}, "SHELL": {}, "SYSTEMROOT": {}, "TEMP": {}, "TMP": {}, "USERPROFILE": {}, "WINDIR": {},
|
||||
}
|
||||
if _, exists := reserved[value]; exists {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func unsafeGameClientBridgeCommandType(value string) bool {
|
||||
tokens := gameClientBridgePayloadKeyTokens(value)
|
||||
tokenSet := make(map[string]struct{}, len(tokens))
|
||||
for _, token := range tokens {
|
||||
tokenSet[token] = struct{}{}
|
||||
}
|
||||
has := func(values ...string) bool {
|
||||
for _, candidate := range values {
|
||||
if _, ok := tokenSet[candidate]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if has("sql") || has("database", "db") && has("execute", "exec", "eval", "run", "query", "statement") || has("query") && has("execute", "exec", "eval", "raw", "statement") {
|
||||
return true
|
||||
}
|
||||
return has("shell", "powershell", "script", "terminal", "execute", "exec", "eval") || has("command", "cmd", "process", "system", "os", "executor") && has("run")
|
||||
}
|
||||
|
||||
func ValidatePluginBridgeAuthorizeRequest(request domain.PluginBridgeAuthorizeRequest) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "pluginId", request.PluginID)
|
||||
@@ -701,12 +952,7 @@ func ValidateRunEndpoint(endpoint domain.RunEndpoint) error {
|
||||
if !validRunEndpointStatus(endpoint.Status) {
|
||||
violations = append(violations, "status is invalid")
|
||||
}
|
||||
if endpoint.Capacity.MaxJobs < 0 || endpoint.Capacity.RunningJobs < 0 || endpoint.Capacity.QueuedJobs < 0 {
|
||||
violations = append(violations, "capacity counts must not be negative")
|
||||
}
|
||||
if endpoint.Capacity.MaxJobs > 0 && endpoint.Capacity.RunningJobs > endpoint.Capacity.MaxJobs {
|
||||
violations = append(violations, "runningJobs must not exceed maxJobs")
|
||||
}
|
||||
violations = appendCapacityViolations(violations, endpoint.Capacity)
|
||||
for i, capability := range endpoint.Capabilities {
|
||||
if strings.TrimSpace(capability) == "" {
|
||||
violations = append(violations, fmt.Sprintf("capabilities[%d] is required", i))
|
||||
@@ -766,6 +1012,7 @@ func ValidateJob(job domain.Job) error {
|
||||
if job.ExecutionInput.ExpectedChecksum != "" && !validSHA256Checksum(job.ExecutionInput.ExpectedChecksum) {
|
||||
violations = append(violations, "executionInput.expectedChecksum must be sha256:<hex>")
|
||||
}
|
||||
violations = append(violations, validateRemoteAdapterInputs("executionInput.inputs", job.ExecutionInput.Inputs)...)
|
||||
if job.ExecutionResult.Checksum != "" && !validSHA256Checksum(job.ExecutionResult.Checksum) {
|
||||
violations = append(violations, "executionResult.checksum must be sha256:<hex>")
|
||||
}
|
||||
@@ -1065,6 +1312,9 @@ func pluginSafeStrings(plugin domain.GamePlugin) []fieldString {
|
||||
values = appendStringSliceFields(values, "declaredPermissions", plugin.DeclaredPermissions)
|
||||
values = appendStringSliceFields(values, "tags", plugin.Tags)
|
||||
values = appendStringSliceFields(values, "aiPurposes", plugin.AIPurposes)
|
||||
values = appendStringSliceFields(values, "productionLifecycle.operations", plugin.ProductionLifecycle.Operations)
|
||||
values = appendStringSliceFields(values, "productionLifecycle.approvalRequired", plugin.ProductionLifecycle.ApprovalRequired)
|
||||
values = append(values, fieldString{field: "productionLifecycle.dependencyPolicy", value: plugin.ProductionLifecycle.DependencyPolicy})
|
||||
values = appendStringSliceFields(values, "bridgeActions", plugin.BridgeActions)
|
||||
values = appendStringSliceFields(values, "remoteAccess.methods", plugin.RemoteAccess.Methods)
|
||||
values = appendStringSliceFields(values, "remoteAccess.runCapabilities", plugin.RemoteAccess.RunCapabilities)
|
||||
@@ -1105,6 +1355,9 @@ func manifestSafeStrings(registration domain.GamePluginManifestRegistration) []f
|
||||
values = appendStringSliceFields(values, "capabilities", manifest.Capabilities)
|
||||
values = appendStringSliceFields(values, "permissions", manifest.Permissions)
|
||||
values = appendStringSliceFields(values, "ai.purposes", manifest.AI.Purposes)
|
||||
values = append(values, fieldString{field: "ai.mediation", value: manifest.AI.Mediation}, fieldString{field: "ai.configWritePolicy", value: manifest.AI.ConfigWritePolicy}, fieldString{field: "productionLifecycle.dependencyPolicy", value: manifest.ProductionLifecycle.DependencyPolicy})
|
||||
values = appendStringSliceFields(values, "productionLifecycle.operations", manifest.ProductionLifecycle.Operations)
|
||||
values = appendStringSliceFields(values, "productionLifecycle.approvalRequired", manifest.ProductionLifecycle.ApprovalRequired)
|
||||
values = appendStringSliceFields(values, "remoteAccess.methods", manifest.RemoteAccess.Methods)
|
||||
values = appendStringSliceFields(values, "remoteAccess.runCapabilities", manifest.RemoteAccess.RunCapabilities)
|
||||
values = appendStringSliceFields(values, "remoteAccess.databaseEngines", manifest.RemoteAccess.DatabaseEngines)
|
||||
@@ -1407,7 +1660,7 @@ func validScopedInputRef(ref string) bool {
|
||||
|
||||
func validPluginPermission(permission string) bool {
|
||||
switch permission {
|
||||
case "server.create", "server.read", "server.lifecycle", "server.files.read", "server.files.write", "server.logs.read", "server.artifacts.read", "server.artifacts.write", "server.remote.access", "server.run.distribution", "server.dependencies.manage", "server.client-manager.manage", "ai.invoke":
|
||||
case "server.create", "server.read", "server.lifecycle", "server.files.read", "server.files.write", "server.logs.read", "server.artifacts.read", "server.artifacts.write", "server.remote.access", "server.run.distribution", "server.dependencies.manage", "server.client-manager.manage", "server.game-client.read", "server.game-client.command", "server.game-client.maintenance", "ai.invoke":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -1426,6 +1679,7 @@ func validPluginBridgeAction(action domain.PluginBridgeAction) bool {
|
||||
domain.PluginBridgeActionDependenciesRequest,
|
||||
domain.PluginBridgeActionLogsBackfillRequest,
|
||||
domain.PluginBridgeActionClientManager,
|
||||
domain.PluginBridgeActionPluginLifecycle,
|
||||
domain.PluginBridgeActionAIInvoke:
|
||||
return true
|
||||
default:
|
||||
@@ -1455,6 +1709,8 @@ func requiredBridgePermissions(action domain.PluginBridgeAction) []string {
|
||||
return []string{"server.logs.read"}
|
||||
case domain.PluginBridgeActionClientManager:
|
||||
return []string{"server.client-manager.manage"}
|
||||
case domain.PluginBridgeActionPluginLifecycle:
|
||||
return []string{"server.lifecycle"}
|
||||
case domain.PluginBridgeActionAIInvoke:
|
||||
return []string{"ai.invoke"}
|
||||
default:
|
||||
@@ -1462,6 +1718,35 @@ func requiredBridgePermissions(action domain.PluginBridgeAction) []string {
|
||||
}
|
||||
}
|
||||
|
||||
func validateProductionLifecycle(field string, lifecycle domain.GamePluginProductionLifecycle, required bool) []string {
|
||||
var violations []string
|
||||
if required && len(lifecycle.Operations) == 0 {
|
||||
violations = append(violations, field+".operations must not be empty")
|
||||
}
|
||||
for i, operation := range lifecycle.Operations {
|
||||
switch domain.PluginLifecycleOperation(operation) {
|
||||
case domain.PluginLifecycleOperationInstall, domain.PluginLifecycleOperationEnable, domain.PluginLifecycleOperationDisable, domain.PluginLifecycleOperationUpgrade, domain.PluginLifecycleOperationRollback, domain.PluginLifecycleOperationRetire, domain.PluginLifecycleOperationDependencyCheck:
|
||||
default:
|
||||
violations = append(violations, fmt.Sprintf("%s.operations[%d] is invalid", field, i))
|
||||
}
|
||||
}
|
||||
violations = append(violations, duplicateViolations(field+".operations", lifecycle.Operations)...)
|
||||
if lifecycle.DependencyPolicy != "required" && lifecycle.DependencyPolicy != "optional" {
|
||||
violations = append(violations, field+".dependencyPolicy must be required or optional")
|
||||
}
|
||||
for i, operation := range lifecycle.ApprovalRequired {
|
||||
if operation != string(domain.PluginLifecycleOperationDisable) && operation != string(domain.PluginLifecycleOperationRollback) && operation != string(domain.PluginLifecycleOperationRetire) {
|
||||
violations = append(violations, fmt.Sprintf("%s.approvalRequired[%d] is invalid", field, i))
|
||||
}
|
||||
}
|
||||
for _, operation := range []string{string(domain.PluginLifecycleOperationDisable), string(domain.PluginLifecycleOperationRollback), string(domain.PluginLifecycleOperationRetire)} {
|
||||
if containsString(lifecycle.Operations, operation) && !containsString(lifecycle.ApprovalRequired, operation) {
|
||||
violations = append(violations, field+".approvalRequired must include "+operation)
|
||||
}
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func effectivePagePermissions(plugin domain.GamePlugin, routeKey string) []string {
|
||||
declared := plugin.DeclaredPermissions
|
||||
page, found := findPluginPage(plugin.Pages, routeKey)
|
||||
|
||||
Reference in New Issue
Block a user