feat(api tokens): check permissions when saving
This commit is contained in:
parent
e4c71123ef
commit
e3dac16398
@ -14,14 +14,12 @@
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package routes
|
||||
package models
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
@ -64,8 +62,8 @@ func getRouteGroupName(path string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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) {
|
||||
|
||||
if !strings.Contains(route.Name, "(*WebHandler)") {
|
||||
return
|
||||
@ -130,7 +128,7 @@ func GetAvailableAPIRoutesForToken(c echo.Context) error {
|
||||
}
|
||||
|
||||
// CanDoAPIRoute checks if a token is allowed to use the current api route
|
||||
func CanDoAPIRoute(c echo.Context, token *models.APIToken) (can bool) {
|
||||
func CanDoAPIRoute(c echo.Context, token *APIToken) (can bool) {
|
||||
routeGroupName := getRouteGroupName(c.Path())
|
||||
|
||||
group, hasGroup := token.Permissions[routeGroupName]
|
||||
@ -168,3 +166,50 @@ func CanDoAPIRoute(c echo.Context, token *models.APIToken) (can bool) {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func PermissionsAreValid(permissions APIPermissions) (err error) {
|
||||
|
||||
for key, methods := range permissions {
|
||||
routes, has := apiTokenRoutes[key]
|
||||
if !has {
|
||||
return &ErrInvalidAPITokenPermission{
|
||||
Group: key,
|
||||
}
|
||||
}
|
||||
|
||||
for _, method := range methods {
|
||||
if method == "create" && routes.Create == nil {
|
||||
return &ErrInvalidAPITokenPermission{
|
||||
Group: key,
|
||||
Permission: method,
|
||||
}
|
||||
}
|
||||
if method == "read_one" && routes.ReadOne == nil {
|
||||
return &ErrInvalidAPITokenPermission{
|
||||
Group: key,
|
||||
Permission: method,
|
||||
}
|
||||
}
|
||||
if method == "read_all" && routes.ReadAll == nil {
|
||||
return &ErrInvalidAPITokenPermission{
|
||||
Group: key,
|
||||
Permission: method,
|
||||
}
|
||||
}
|
||||
if method == "update" && routes.Update == nil {
|
||||
return &ErrInvalidAPITokenPermission{
|
||||
Group: key,
|
||||
Permission: method,
|
||||
}
|
||||
}
|
||||
if method == "delete" && routes.Delete == nil {
|
||||
return &ErrInvalidAPITokenPermission{
|
||||
Group: key,
|
||||
Permission: method,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -102,7 +102,9 @@ func (t *APIToken) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
t.OwnerID = a.GetID()
|
||||
|
||||
// TODO: validate permissions
|
||||
if err := PermissionsAreValid(t.Permissions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.Insert(t)
|
||||
return err
|
||||
|
@ -1685,3 +1685,31 @@ func (err ErrAPITokenInvalid) HTTPError() web.HTTPError {
|
||||
Message: "The provided api token is invalid.",
|
||||
}
|
||||
}
|
||||
|
||||
// ErrInvalidAPITokenPermission represents an error where an api token is invalid
|
||||
type ErrInvalidAPITokenPermission struct {
|
||||
Group string
|
||||
Permission string
|
||||
}
|
||||
|
||||
// IsErrInvalidAPITokenPermission checks if an error is ErrInvalidAPITokenPermission.
|
||||
func IsErrInvalidAPITokenPermission(err error) bool {
|
||||
_, ok := err.(*ErrInvalidAPITokenPermission)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err *ErrInvalidAPITokenPermission) Error() string {
|
||||
return fmt.Sprintf("API token permission %s of group %s is invalid", err.Permission, err.Group)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidAPITokenPermission holds the unique world-error code of this error
|
||||
const ErrCodeInvalidAPITokenPermission = 14002
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidAPITokenPermission) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
Code: ErrCodeInvalidAPITokenPermission,
|
||||
Message: fmt.Sprintf("The permission %s of group %s is invalid.", err.Permission, err.Group),
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ func RegisterRoutes(e *echo.Echo) {
|
||||
// API Routes
|
||||
a := e.Group("/api/v1")
|
||||
e.OnAddRouteHandler = func(host string, route echo.Route, handler echo.HandlerFunc, middleware []echo.MiddlewareFunc) {
|
||||
collectRoutesForAPITokenUsage(route)
|
||||
models.CollectRoutesForAPITokenUsage(route)
|
||||
}
|
||||
registerAPIRoutes(a)
|
||||
}
|
||||
@ -316,7 +316,7 @@ func registerAPIRoutes(a *echo.Group) {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
if !CanDoAPIRoute(c, token) {
|
||||
if !models.CanDoAPIRoute(c, token) {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
@ -333,7 +333,7 @@ func registerAPIRoutes(a *echo.Group) {
|
||||
setupMetricsMiddleware(a)
|
||||
|
||||
a.POST("/tokenTest", apiv1.CheckToken)
|
||||
a.GET("/routes", GetAvailableAPIRoutesForToken)
|
||||
a.GET("/routes", models.GetAvailableAPIRoutesForToken)
|
||||
|
||||
// User stuff
|
||||
u := a.Group("/user")
|
||||
|
Loading…
x
Reference in New Issue
Block a user