diff --git a/openapi/kb/collection.go b/openapi/kb/collection.go index fef37ffd..741b0d29 100644 --- a/openapi/kb/collection.go +++ b/openapi/kb/collection.go @@ -309,6 +309,10 @@ func GetCollection(c *gin.Context) { // ListCollections lists collections with pagination func ListCollections(c *gin.Context) { + + // Get authorized information + authInfo := authorized.GetInfo(c) + // Check if kb.Instance is available if kb.Instance == nil { errorResp := &response.ErrorResponse{ @@ -364,13 +368,14 @@ func ListCollections(c *gin.Context) { } // Build query parameters - param := model.QueryParam{ - Select: selectFields, - } + param := model.QueryParam{Select: selectFields} // Add filters var wheres []model.QueryWhere + // Apply permission-based filtering + wheres = append(wheres, AuthFilter(c, authInfo)...) + // Filter by keywords (search in name and description) if keywords := strings.TrimSpace(c.Query("keywords")); keywords != "" { wheres = append(wheres, model.QueryWhere{ diff --git a/openapi/kb/document.go b/openapi/kb/document.go index 0676e244..3ff45cf6 100644 --- a/openapi/kb/document.go +++ b/openapi/kb/document.go @@ -10,6 +10,7 @@ import ( "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/attachment" "github.com/yaoapp/yao/kb" + "github.com/yaoapp/yao/openapi/oauth/authorized" "github.com/yaoapp/yao/openapi/response" ) @@ -102,9 +103,15 @@ func ListDocuments(c *gin.Context) { Select: selectFields, } + // Get authorized information + authInfo := authorized.GetInfo(c) + // Add filters var wheres []model.QueryWhere + // Apply permission-based filtering + wheres = append(wheres, AuthFilter(c, authInfo)...) + // Filter by keywords (search in name and description) if keywords := strings.TrimSpace(c.Query("keywords")); keywords != "" { wheres = append(wheres, model.QueryWhere{ diff --git a/openapi/kb/filter.go b/openapi/kb/filter.go new file mode 100644 index 00000000..baed5585 --- /dev/null +++ b/openapi/kb/filter.go @@ -0,0 +1,68 @@ +package kb + +import ( + "github.com/gin-gonic/gin" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/yao/openapi/oauth/authorized" + "github.com/yaoapp/yao/openapi/oauth/types" +) + +// AuthFilter applies permission-based filtering to query wheres +// This function builds where clauses based on the user's authorization constraints +// It supports TeamOnly and OwnerOnly constraints for data access control +// +// Parameters: +// - c: gin.Context containing authorization information +// - authInfo: authorized information extracted from the context +// +// Returns: +// - []model.QueryWhere: array of where clauses to apply to the query +func AuthFilter(c *gin.Context, authInfo *types.AuthorizedInfo) []model.QueryWhere { + if authInfo == nil { + return []model.QueryWhere{} + } + + var wheres []model.QueryWhere + scope := authInfo.AccessScope() + + // Team only - User can access: + // 1. Public records (public = true) + // 2. Records in their team where: + // - They created the record (__yao_created_by matches) + // - OR the record is shared with team (share = "team") + if authInfo.Constraints.TeamOnly && authorized.IsTeamMember(c) { + wheres = append(wheres, model.QueryWhere{ + Wheres: []model.QueryWhere{ + {Column: "public", Value: true, Method: "orwhere"}, + {Wheres: []model.QueryWhere{ + {Column: "__yao_team_id", Value: scope.TeamID}, + {Wheres: []model.QueryWhere{ + {Column: "__yao_created_by", Value: scope.CreatedBy}, + {Column: "share", Value: "team", Method: "orwhere"}, + }}, + }, Method: "orwhere"}, + }, + }) + return wheres + } + + // Owner only - User can access: + // 1. Public records (public = true) + // 2. Records they created where: + // - __yao_team_id is null (not team records) + // - __yao_created_by matches their user ID + if authInfo.Constraints.OwnerOnly && authInfo.UserID != "" { + wheres = append(wheres, model.QueryWhere{ + Wheres: []model.QueryWhere{ + {Column: "public", Value: true, Method: "orwhere"}, + {Wheres: []model.QueryWhere{ + {Column: "__yao_team_id", OP: "null"}, + {Column: "__yao_created_by", Value: scope.CreatedBy}, + }, Method: "orwhere"}, + }, + }) + return wheres + } + + return wheres +} diff --git a/openapi/oauth/authorized/utils.go b/openapi/oauth/authorized/utils.go index 8c7fe898..0e371a6c 100644 --- a/openapi/oauth/authorized/utils.go +++ b/openapi/oauth/authorized/utils.go @@ -50,6 +50,12 @@ func GetInfo(c *gin.Context) *types.AuthorizedInfo { return info } +// IsTeamMember checks if the user is a team member +func IsTeamMember(c *gin.Context) bool { + authInfo := GetInfo(c) + return authInfo != nil && authInfo.TeamID != "" && authInfo.UserID != "" +} + // GetConstraints extracts data access constraints from the gin context // Returns a DataConstraints struct with all constraint flags func GetConstraints(c *gin.Context) types.DataConstraints {