Federation Refactoring & Architecture Improvements (#930)
* initial commit * add lists * update permissions * fix waypoint create * needs_full_sync for activitypub trails * fixes build issues * fix list get * further CSRF protection * update API docs * adds rate limiter * fix tiptap mentions * a bit more cleanup of main.go * Fix migration order * fixes reviewed notes * require context for activitpub server calls * improve hashing for identifier * fix copy paste error * fix sync trail/list issues * fix summit log/comment duplicates * adaptions after trail merge * fix dockerignore --------- Co-authored-by: Christian Beutel <> Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
This commit is contained in:
146
db/hooks/integrations.go
Normal file
146
db/hooks/integrations.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"pocketbase/util"
|
||||
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
func ListIntegrationHandler() func(e *core.RecordsListRequestEvent) error {
|
||||
return func(e *core.RecordsListRequestEvent) error {
|
||||
if e.HasSuperuserAuth() {
|
||||
return e.Next()
|
||||
}
|
||||
for _, r := range e.Records {
|
||||
|
||||
err := censorIntegrationSecrets(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func CreateIntegrationHandler() func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
err := encryptIntegrationSecrets(e.App, e.Record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func CreateUpdateIntegrationSuccessHandler() func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
err := censorIntegrationSecrets(e.Record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateIntegrationHandler() func(e *core.RecordEvent) error {
|
||||
return func(e *core.RecordEvent) error {
|
||||
err := encryptIntegrationSecrets(e.App, e.Record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func censorIntegrationSecrets(r *core.Record) error {
|
||||
secrets := map[string][]string{
|
||||
"strava": {"clientSecret", "refreshToken", "accessToken", "expiresAt"},
|
||||
"komoot": {"password"},
|
||||
"hammerhead": {"password"},
|
||||
}
|
||||
for key, secretKeys := range secrets {
|
||||
if integrationString := r.GetString(key); integrationString != "" {
|
||||
var integration map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(integrationString), &integration); err != nil {
|
||||
return err
|
||||
}
|
||||
if integration == nil {
|
||||
continue
|
||||
}
|
||||
for _, secretKey := range secretKeys {
|
||||
integration[secretKey] = ""
|
||||
}
|
||||
b, err := json.Marshal(integration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Set(key, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func encryptIntegrationSecrets(app core.App, r *core.Record) error {
|
||||
encryptionKey := os.Getenv("POCKETBASE_ENCRYPTION_KEY")
|
||||
if len(encryptionKey) == 0 {
|
||||
return apis.NewBadRequestError("POCKETBASE_ENCRYPTION_KEY not set", nil)
|
||||
}
|
||||
|
||||
secrets := map[string][]string{
|
||||
"strava": {"clientSecret", "refreshToken", "accessToken", "expiresAt"},
|
||||
"komoot": {"password"},
|
||||
"hammerhead": {"password"},
|
||||
}
|
||||
|
||||
original, _ := app.FindRecordById("integrations", r.Id)
|
||||
|
||||
for key, secretKeys := range secrets {
|
||||
if integrationString := r.GetString(key); integrationString != "" {
|
||||
var integration map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(integrationString), &integration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, secretKey := range secretKeys {
|
||||
// If the secret is already encrypted, we don't re-encrypt it.
|
||||
// TODO: This is a bit of a hack, we should handle this in a more robust way (e.g.
|
||||
// storing flag on the record or prefixing encrypted strings with enc: or smilar).
|
||||
// Doing that would also potentially allow us to support key rotation in the future.
|
||||
if secret, ok := integration[secretKey].(string); ok && len(secret) > 0 && !util.CanDecryptSecret(secret) {
|
||||
encryptedSecret, err := security.Encrypt([]byte(secret), encryptionKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
integration[secretKey] = encryptedSecret
|
||||
} else if original != nil {
|
||||
|
||||
originalString := original.GetString(key)
|
||||
var originalIntegration map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(originalString), &originalIntegration); err != nil {
|
||||
return err
|
||||
}
|
||||
if integration == nil {
|
||||
continue
|
||||
}
|
||||
integration[secretKey] = originalIntegration[secretKey]
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.Marshal(integration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Set(key, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user