You've already forked pocketbase
mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-11-27 16:28:27 +02:00
initial v0.8 pre-release
This commit is contained in:
186
apis/record_helpers.go
Normal file
186
apis/record_helpers.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/resolvers"
|
||||
"github.com/pocketbase/pocketbase/tools/rest"
|
||||
"github.com/pocketbase/pocketbase/tools/search"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// exportRequestData exports a map with common request fields.
|
||||
//
|
||||
// @todo consider changing the map to a typed struct after v0.8 and the
|
||||
// IN operator support.
|
||||
func exportRequestData(c echo.Context) map[string]any {
|
||||
result := map[string]any{}
|
||||
queryParams := map[string]any{}
|
||||
bodyData := map[string]any{}
|
||||
method := c.Request().Method
|
||||
|
||||
echo.BindQueryParams(c, &queryParams)
|
||||
|
||||
rest.BindBody(c, &bodyData)
|
||||
|
||||
result["method"] = method
|
||||
result["query"] = queryParams
|
||||
result["data"] = bodyData
|
||||
result["auth"] = nil
|
||||
|
||||
auth, _ := c.Get(ContextAuthRecordKey).(*models.Record)
|
||||
if auth != nil {
|
||||
result["auth"] = auth.PublicExport()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// expandFetch is the records fetch function that is used to expand related records.
|
||||
func expandFetch(
|
||||
dao *daos.Dao,
|
||||
isAdmin bool,
|
||||
requestData map[string]any,
|
||||
) daos.ExpandFetchFunc {
|
||||
return func(relCollection *models.Collection, relIds []string) ([]*models.Record, error) {
|
||||
records, err := dao.FindRecordsByIds(relCollection.Id, relIds, func(q *dbx.SelectQuery) error {
|
||||
if isAdmin {
|
||||
return nil // admins can access everything
|
||||
}
|
||||
|
||||
if relCollection.ViewRule == nil {
|
||||
return fmt.Errorf("Only admins can view collection %q records", relCollection.Name)
|
||||
}
|
||||
|
||||
if *relCollection.ViewRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(dao, relCollection, requestData, true)
|
||||
expr, err := search.FilterData(*(relCollection.ViewRule)).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resolver.UpdateQuery(q)
|
||||
q.AndWhere(expr)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err == nil && len(records) > 0 {
|
||||
autoIgnoreAuthRecordsEmailVisibility(dao, records, isAdmin, requestData)
|
||||
}
|
||||
|
||||
return records, err
|
||||
}
|
||||
}
|
||||
|
||||
// autoIgnoreAuthRecordsEmailVisibility ignores the email visibility check for
|
||||
// the provided record if the current auth model is admin, owner or a "manager".
|
||||
//
|
||||
// Note: Expects all records to be from the same auth collection!
|
||||
func autoIgnoreAuthRecordsEmailVisibility(
|
||||
dao *daos.Dao,
|
||||
records []*models.Record,
|
||||
isAdmin bool,
|
||||
requestData map[string]any,
|
||||
) error {
|
||||
if len(records) == 0 || !records[0].Collection().IsAuth() {
|
||||
return nil // nothing to check
|
||||
}
|
||||
|
||||
if isAdmin {
|
||||
for _, rec := range records {
|
||||
rec.IgnoreEmailVisibility(true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
collection := records[0].Collection()
|
||||
|
||||
mappedRecords := make(map[string]*models.Record, len(records))
|
||||
recordIds := make([]any, 0, len(records))
|
||||
for _, rec := range records {
|
||||
mappedRecords[rec.Id] = rec
|
||||
recordIds = append(recordIds, rec.Id)
|
||||
}
|
||||
|
||||
if auth, ok := requestData["auth"].(map[string]any); ok && mappedRecords[cast.ToString(auth["id"])] != nil {
|
||||
mappedRecords[cast.ToString(auth["id"])].IgnoreEmailVisibility(true)
|
||||
}
|
||||
|
||||
authOptions := collection.AuthOptions()
|
||||
if authOptions.ManageRule == nil || *authOptions.ManageRule == "" {
|
||||
return nil // no manage rule to check
|
||||
}
|
||||
|
||||
// fetch the ids of the managed records
|
||||
// ---
|
||||
managedIds := []string{}
|
||||
|
||||
query := dao.RecordQuery(collection).
|
||||
Select(dao.DB().QuoteSimpleColumnName(collection.Name) + ".id").
|
||||
AndWhere(dbx.In(dao.DB().QuoteSimpleColumnName(collection.Name)+".id", recordIds...))
|
||||
|
||||
resolver := resolvers.NewRecordFieldResolver(dao, collection, requestData, true)
|
||||
expr, err := search.FilterData(*authOptions.ManageRule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resolver.UpdateQuery(query)
|
||||
query.AndWhere(expr)
|
||||
|
||||
if err := query.Column(&managedIds); err != nil {
|
||||
return err
|
||||
}
|
||||
// ---
|
||||
|
||||
// ignore the email visibility check for the managed records
|
||||
for _, id := range managedIds {
|
||||
if rec, ok := mappedRecords[id]; ok {
|
||||
rec.IgnoreEmailVisibility(true)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasAuthManageAccess checks whether the client is allowed to have full
|
||||
// [forms.RecordUpsert] auth management permissions
|
||||
// (aka. allowing to change system auth fields without oldPassword).
|
||||
func hasAuthManageAccess(
|
||||
dao *daos.Dao,
|
||||
record *models.Record,
|
||||
requestData map[string]any,
|
||||
) bool {
|
||||
if !record.Collection().IsAuth() {
|
||||
return false
|
||||
}
|
||||
|
||||
manageRule := record.Collection().AuthOptions().ManageRule
|
||||
|
||||
if manageRule == nil || *manageRule == "" {
|
||||
return false // only for admins (manageRule can't be empty)
|
||||
}
|
||||
|
||||
if auth, ok := requestData["auth"].(map[string]any); !ok || cast.ToString(auth["id"]) == "" {
|
||||
return false // no auth record
|
||||
}
|
||||
|
||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||
resolver := resolvers.NewRecordFieldResolver(dao, record.Collection(), requestData, true)
|
||||
expr, err := search.FilterData(*manageRule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resolver.UpdateQuery(q)
|
||||
q.AndWhere(expr)
|
||||
return nil
|
||||
}
|
||||
|
||||
_, findErr := dao.FindRecordById(record.Collection().Id, record.Id, ruleFunc)
|
||||
|
||||
return findErr == nil
|
||||
}
|
||||
Reference in New Issue
Block a user