mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 07:06:36 +08:00
refactor(backend): rename OpenFlare directory to lowercase openflare
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package credential seals and opens OpenFlare integration credentials.
|
||||
package credential
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"Wavelet/openflare/plugins/server/kernel/runtimeconfig"
|
||||
"Wavelet/pkg/util"
|
||||
)
|
||||
|
||||
// Prefix identifies values encrypted with the current credential format.
|
||||
const Prefix = "enc:v1:"
|
||||
|
||||
var sessionSecret string
|
||||
|
||||
// SetSessionSecret binds the host session secret used to seal credentials.
|
||||
func SetSessionSecret(secret string) {
|
||||
sessionSecret = strings.TrimSpace(secret)
|
||||
}
|
||||
|
||||
func encryptionKey() string {
|
||||
secret := strings.TrimSpace(sessionSecret)
|
||||
if secret == "" {
|
||||
secret = strings.TrimSpace(runtimeconfig.SessionSecret())
|
||||
}
|
||||
if secret == "" {
|
||||
return ""
|
||||
}
|
||||
sum := sha256.Sum256([]byte(secret))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// Seal trims and encrypts plaintext when a session secret is configured.
|
||||
// Plaintext storage is preserved for installations without a session secret.
|
||||
func Seal(plaintext string) (string, error) {
|
||||
plaintext = strings.TrimSpace(plaintext)
|
||||
if plaintext == "" {
|
||||
return "", nil
|
||||
}
|
||||
key := encryptionKey()
|
||||
if key == "" {
|
||||
return plaintext, nil
|
||||
}
|
||||
encrypted, err := util.Encrypt(key, plaintext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return Prefix + encrypted, nil
|
||||
}
|
||||
|
||||
// Open decrypts a sealed value and accepts legacy plaintext values.
|
||||
func Open(stored string) (string, error) {
|
||||
stored = strings.TrimSpace(stored)
|
||||
if stored == "" {
|
||||
return "", nil
|
||||
}
|
||||
if !strings.HasPrefix(stored, Prefix) {
|
||||
return stored, nil
|
||||
}
|
||||
key := encryptionKey()
|
||||
if key == "" {
|
||||
return "", errors.New("cannot decrypt sensitive field without session secret")
|
||||
}
|
||||
return util.Decrypt(key, strings.TrimPrefix(stored, Prefix))
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package credential
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"Wavelet/openflare/plugins/server/kernel/runtimeconfig"
|
||||
)
|
||||
|
||||
func TestSealAndOpenSensitiveValue(t *testing.T) {
|
||||
previous := runtimeconfig.Get()
|
||||
SetSessionSecret("cloudflare-pointing-test-secret")
|
||||
t.Cleanup(func() {
|
||||
SetSessionSecret(previous.SessionSecret)
|
||||
runtimeconfig.Set(previous)
|
||||
})
|
||||
|
||||
sealed, err := Seal(`{"api_token":"secret-token"}`)
|
||||
if err != nil {
|
||||
t.Fatalf("Seal() error = %v", err)
|
||||
}
|
||||
if !strings.HasPrefix(sealed, Prefix) {
|
||||
t.Fatalf("Seal() = %q, want prefix %q", sealed, Prefix)
|
||||
}
|
||||
if strings.Contains(sealed, "secret-token") {
|
||||
t.Fatalf("Seal() = %q, want token redacted", sealed)
|
||||
}
|
||||
|
||||
opened, err := Open(sealed)
|
||||
if err != nil {
|
||||
t.Fatalf("Open() error = %v", err)
|
||||
}
|
||||
if want := `{"api_token":"secret-token"}`; opened != want {
|
||||
t.Errorf("Open(Seal(value)) = %q, want %q", opened, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSealWithoutSessionSecretKeepsPlaintextCompatibility(t *testing.T) {
|
||||
previous := runtimeconfig.Get()
|
||||
SetSessionSecret("")
|
||||
runtimeconfig.Set(runtimeconfig.Snapshot{})
|
||||
t.Cleanup(func() {
|
||||
SetSessionSecret(previous.SessionSecret)
|
||||
runtimeconfig.Set(previous)
|
||||
})
|
||||
|
||||
sealed, err := Seal(" legacy-value ")
|
||||
if err != nil {
|
||||
t.Fatalf("Seal() error = %v", err)
|
||||
}
|
||||
if sealed != "legacy-value" {
|
||||
t.Errorf("Seal() = %q, want %q", sealed, "legacy-value")
|
||||
}
|
||||
|
||||
opened, err := Open(sealed)
|
||||
if err != nil {
|
||||
t.Fatalf("Open(plaintext) error = %v", err)
|
||||
}
|
||||
if opened != "legacy-value" {
|
||||
t.Errorf("Open(plaintext) = %q, want %q", opened, "legacy-value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenEncryptedValueRequiresSessionSecret(t *testing.T) {
|
||||
previous := runtimeconfig.Get()
|
||||
SetSessionSecret("cloudflare-pointing-test-secret")
|
||||
sealed, err := Seal("secret")
|
||||
if err != nil {
|
||||
t.Fatalf("Seal() error = %v", err)
|
||||
}
|
||||
|
||||
SetSessionSecret("")
|
||||
runtimeconfig.Set(runtimeconfig.Snapshot{})
|
||||
t.Cleanup(func() {
|
||||
SetSessionSecret(previous.SessionSecret)
|
||||
runtimeconfig.Set(previous)
|
||||
})
|
||||
if _, err := Open(sealed); err == nil {
|
||||
t.Fatal("Open(encrypted) error = nil, want missing session secret error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user