V0.38.5 git board rewrite (#238)
Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
@@ -63,13 +63,28 @@ func (r *ConnectionResolverAdapter) decryptInPlace(ctx context.Context, conn *mo
|
||||
if len(enc) == 0 {
|
||||
continue
|
||||
}
|
||||
plaintext, err := r.vault.Decrypt(enc, nonce, conn.Scope, conn.OwnerID)
|
||||
if err == nil {
|
||||
plaintext := r.safeDecrypt(enc, nonce, conn.Scope, conn.OwnerID)
|
||||
if plaintext != "" {
|
||||
conn.Config[k] = plaintext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// safeDecrypt wraps vault.Decrypt to recover from GCM panics caused by
|
||||
// invalid nonce lengths (e.g. stale vault keys after container restart).
|
||||
func (r *ConnectionResolverAdapter) safeDecrypt(enc, nonce []byte, scope, ownerID string) (plaintext string) {
|
||||
defer func() {
|
||||
if rv := recover(); rv != nil {
|
||||
plaintext = ""
|
||||
}
|
||||
}()
|
||||
result, err := r.vault.Decrypt(enc, nonce, scope, ownerID)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// lookupSecretFields reads the package manifest to identify secret fields.
|
||||
func (r *ConnectionResolverAdapter) lookupSecretFields(ctx context.Context, packageID, connType string) map[string]bool {
|
||||
result := map[string]bool{}
|
||||
|
||||
@@ -4,6 +4,7 @@ package handlers
|
||||
// Pattern follows apiconfigs.go (ProviderConfigHandler).
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
@@ -27,7 +28,9 @@ func NewConnectionHandler(s store.Stores, vault *crypto.KeyResolver) *Connection
|
||||
// ListConnections returns the user's personal connections.
|
||||
func (h *ConnectionHandler) ListConnections(c *gin.Context) {
|
||||
userID := getUserID(c)
|
||||
conns, err := h.stores.Connections.ListForUser(c.Request.Context(), userID)
|
||||
// v0.38.5: Show all accessible connections (personal + team + global)
|
||||
// so users can see which connections are available to them.
|
||||
conns, err := h.stores.Connections.ListAccessible(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list connections"})
|
||||
return
|
||||
@@ -252,13 +255,23 @@ func (h *ConnectionHandler) decryptSecrets(config models.JSONMap, secretFields m
|
||||
}
|
||||
|
||||
// toBytes coerces an interface{} to []byte. Handles both []byte (from Go)
|
||||
// and string (from JSON unmarshal of base64).
|
||||
// and string (from JSON unmarshal — base64 encoded by json.Marshal for []byte).
|
||||
func toBytes(v interface{}) []byte {
|
||||
switch t := v.(type) {
|
||||
case []byte:
|
||||
return t
|
||||
case string:
|
||||
return []byte(t)
|
||||
if t == "" {
|
||||
return nil
|
||||
}
|
||||
// JSON round-trip: json.Marshal encodes []byte as base64 strings.
|
||||
// Decode back to the original bytes.
|
||||
decoded, err := base64.StdEncoding.DecodeString(t)
|
||||
if err != nil {
|
||||
// Fallback: might be raw string (shouldn't happen, but be safe)
|
||||
return []byte(t)
|
||||
}
|
||||
return decoded
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -100,7 +100,12 @@ func (h *ExtAPIHandler) Handle(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// ── 3. Check api.http permission ───────────
|
||||
// ── 3. Check permissions ──────────────────
|
||||
// The package needs at least one granted permission to be active.
|
||||
// api.http is NOT required here — a consumer package may delegate
|
||||
// HTTP to a library via lib.require(). The sandbox wires the http
|
||||
// module only when api.http is granted; without it the script simply
|
||||
// cannot call http.get() directly, which is correct for consumers.
|
||||
if h.stores.ExtPermissions == nil {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "permission system unavailable"})
|
||||
return
|
||||
@@ -110,8 +115,8 @@ func (h *ExtAPIHandler) Handle(c *gin.Context) {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "permission check failed"})
|
||||
return
|
||||
}
|
||||
if !hasPermission(granted, models.ExtPermAPIHTTP) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "extension does not have api.http permission"})
|
||||
if len(granted) == 0 {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "extension has no granted permissions"})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -469,6 +469,11 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// v0.38.5: Declare manifest permissions (same as AdminInstallExtension).
|
||||
// This creates the permission rows and sets status to pending_review
|
||||
// if the package declares permissions.
|
||||
SyncManifestPermissions(c, h.stores, pkgID, manifest)
|
||||
|
||||
// v0.30.0: Run schema migrations if declared.
|
||||
newSchemaVersion := ParseSchemaVersion(manifest)
|
||||
if newSchemaVersion > 0 && h.sandbox != nil {
|
||||
|
||||
Reference in New Issue
Block a user