From 8b028dbc4b3cd027af194e5c8a85c09460887514 Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 18 Jun 2024 14:33:57 +0200 Subject: [PATCH] feat(api): add bulk endpoints to api tokens Previously, the bulk api endpoint were explicitly filtered out. This meant that you couldn't use them with api tokens. This change adds them to their "parent" token types as another option, allowing users to select and use them when creating api tokens. Resolves https://community.vikunja.io/t/help-with-bulk-api-complete/2461 --- pkg/models/api_routes.go | 108 ++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/pkg/models/api_routes.go b/pkg/models/api_routes.go index 6917d18ff..28609e0e5 100644 --- a/pkg/models/api_routes.go +++ b/pkg/models/api_routes.go @@ -62,6 +62,44 @@ func getRouteGroupName(path string) (finalName string, filteredParts []string) { } } +func getRouteDetail(route echo.Route) (method string, detail *RouteDetail) { + if strings.Contains(route.Name, "CreateWeb") { + return "create", &RouteDetail{ + Path: route.Path, + Method: route.Method, + } + } + if strings.Contains(route.Name, "ReadOneWeb") { + return "read_one", &RouteDetail{ + Path: route.Path, + Method: route.Method, + } + } + if strings.Contains(route.Name, "ReadAllWeb") { + return "read_all", &RouteDetail{ + Path: route.Path, + Method: route.Method, + } + } + if strings.Contains(route.Name, "UpdateWeb") { + return "update", &RouteDetail{ + Path: route.Path, + Method: route.Method, + } + } + if strings.Contains(route.Name, "DeleteWeb") { + return "delete", &RouteDetail{ + Path: route.Path, + Method: route.Method, + } + } + + return "", &RouteDetail{ + Path: route.Path, + Method: route.Method, + } +} + // CollectRoutesForAPITokenUsage gets called for every added APITokenRoute and builds a list of all routes we can use for the api tokens. func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.MiddlewareFunc) { @@ -87,8 +125,7 @@ func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.Middlewa routeGroupName == "subscriptions" || routeGroupName == "tokens" || routeGroupName == "*" || - strings.HasPrefix(routeGroupName, "user_") || - strings.HasSuffix(routeGroupName, "_bulk") { + strings.HasPrefix(routeGroupName, "user_") { return } @@ -130,40 +167,26 @@ func CollectRoutesForAPITokenUsage(route echo.Route, middlewares []echo.Middlewa return } + if strings.HasSuffix(routeGroupName, "_bulk") { + parent := strings.TrimSuffix(routeGroupName, "_bulk") + _, has := apiTokenRoutes[parent] + if !has { + apiTokenRoutes[parent] = make(APITokenRoute) + } + + method, routeDetail := getRouteDetail(route) + apiTokenRoutes[parent][method+"_bulk"] = routeDetail + return + } + _, has := apiTokenRoutes[routeGroupName] if !has { apiTokenRoutes[routeGroupName] = make(APITokenRoute) } - if strings.Contains(route.Name, "CreateWeb") { - apiTokenRoutes[routeGroupName]["create"] = &RouteDetail{ - Path: route.Path, - Method: route.Method, - } - } - if strings.Contains(route.Name, "ReadOneWeb") { - apiTokenRoutes[routeGroupName]["read_one"] = &RouteDetail{ - Path: route.Path, - Method: route.Method, - } - } - if strings.Contains(route.Name, "ReadAllWeb") { - apiTokenRoutes[routeGroupName]["read_all"] = &RouteDetail{ - Path: route.Path, - Method: route.Method, - } - } - if strings.Contains(route.Name, "UpdateWeb") { - apiTokenRoutes[routeGroupName]["update"] = &RouteDetail{ - Path: route.Path, - Method: route.Method, - } - } - if strings.Contains(route.Name, "DeleteWeb") { - apiTokenRoutes[routeGroupName]["delete"] = &RouteDetail{ - Path: route.Path, - Method: route.Method, - } + method, routeDetail := getRouteDetail(route) + if method != "" { + apiTokenRoutes[routeGroupName][method] = routeDetail } if routeGroupName == "tasks_attachments" { @@ -205,6 +228,8 @@ func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) { routeGroupName, routeParts := getRouteGroupName(path) + routeGroupName = strings.TrimSuffix(routeGroupName, "_bulk") + group, hasGroup := token.Permissions[routeGroupName] if !hasGroup { group, hasGroup = token.Permissions[routeParts[0]] @@ -223,29 +248,16 @@ func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) { route = strings.Join(routeParts[1:], "_") } - if routes["create"] != nil && routes["create"].Path == path && routes["create"].Method == c.Request().Method { - route = "create" - } - if routes["read_one"] != nil && routes["read_one"].Path == path && routes["read_one"].Method == c.Request().Method { - route = "read_one" - } - if routes["read_all"] != nil && routes["read_all"].Path == path && routes["read_all"].Method == c.Request().Method { - route = "read_all" - } - if routes["update"] != nil && routes["update"].Path == path && routes["update"].Method == c.Request().Method { - route = "update" - } - if routes["delete"] != nil && routes["delete"].Path == path && routes["delete"].Method == c.Request().Method { - route = "delete" - } - // The tasks read_all route is available as /:project/tasks and /tasks/all - therefore we need this workaround here. if routeGroupName == "tasks" && path == "/api/v1/projects/:project/tasks" && c.Request().Method == http.MethodGet { route = "read_all" } for _, p := range group { - if p == route { + if route == "" && routes[p] != nil && routes[p].Path == path && routes[p].Method == c.Request().Method { + return true + } + if route != "" && p == route { return true } }