github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/remote/remote.go (about)

     1  // Package remote is the used for the /remote routes. They are intended for
     2  // requesting data that is not in the Cozy itself, but in a remote place.
     3  package remote
     4  
     5  import (
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/cozy/cozy-stack/model/permission"
    10  	"github.com/cozy/cozy-stack/model/remote"
    11  	"github.com/cozy/cozy-stack/pkg/consts"
    12  	"github.com/cozy/cozy-stack/pkg/jsonapi"
    13  	"github.com/cozy/cozy-stack/web/middlewares"
    14  	"github.com/labstack/echo/v4"
    15  )
    16  
    17  func allDoctypes(c echo.Context) error {
    18  	if err := middlewares.AllowWholeType(c, permission.GET, consts.Doctypes); err != nil {
    19  		return wrapRemoteErr(err)
    20  	}
    21  
    22  	inst := middlewares.GetInstance(c)
    23  	doctypes, err := remote.ListDoctypes(inst)
    24  	if err != nil {
    25  		return wrapRemoteErr(err)
    26  	}
    27  	return c.JSON(http.StatusOK, doctypes)
    28  }
    29  
    30  func remoteGet(c echo.Context) error {
    31  	doctype := c.Param("doctype")
    32  	slug, err := allowWholeType(c, permission.GET, doctype)
    33  	if err != nil {
    34  		return wrapRemoteErr(err)
    35  	}
    36  	instance := middlewares.GetInstance(c)
    37  	remote, err := remote.Find(instance, doctype)
    38  	if err != nil {
    39  		return wrapRemoteErr(err)
    40  	}
    41  	if remote.Verb != "GET" {
    42  		return jsonapi.MethodNotAllowed("GET")
    43  	}
    44  	err = remote.ProxyTo(instance, c.Response(), c.Request(), slug)
    45  	if err != nil {
    46  		return wrapRemoteErr(err)
    47  	}
    48  	return nil
    49  }
    50  
    51  func remotePost(c echo.Context) error {
    52  	doctype := c.Param("doctype")
    53  	slug, err := allowWholeType(c, permission.POST, doctype)
    54  	if err != nil {
    55  		return wrapRemoteErr(err)
    56  	}
    57  	instance := middlewares.GetInstance(c)
    58  	remote, err := remote.Find(instance, doctype)
    59  	if err != nil {
    60  		return wrapRemoteErr(err)
    61  	}
    62  	if remote.Verb != "POST" {
    63  		return jsonapi.MethodNotAllowed("POST")
    64  	}
    65  	err = remote.ProxyTo(instance, c.Response(), c.Request(), slug)
    66  	if err != nil {
    67  		return wrapRemoteErr(err)
    68  	}
    69  	return nil
    70  }
    71  
    72  func remoteAsset(c echo.Context) error {
    73  	_, err := middlewares.GetPermission(c)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	return wrapRemoteErr(remote.
    78  		ProxyRemoteAsset(c.Param("asset-name"), c.Response()))
    79  }
    80  
    81  // Routes set the routing for the remote service
    82  func Routes(router *echo.Group) {
    83  	router.GET("/_all_doctypes", allDoctypes)
    84  	router.GET("/:doctype", remoteGet)
    85  	router.POST("/:doctype", remotePost)
    86  	router.GET("/assets/:asset-name", remoteAsset)
    87  
    88  	nextcloudRoutes(router)
    89  }
    90  
    91  func wrapRemoteErr(err error) error {
    92  	switch err {
    93  	case remote.ErrNotFoundRemote:
    94  		return jsonapi.NotFound(err)
    95  	case remote.ErrInvalidRequest:
    96  		return jsonapi.BadRequest(err)
    97  	case remote.ErrRequestFailed:
    98  		return jsonapi.BadGateway(err)
    99  	case remote.ErrInvalidVariables:
   100  		return jsonapi.BadRequest(err)
   101  	case remote.ErrMissingVar:
   102  		return jsonapi.BadRequest(err)
   103  	case remote.ErrInvalidContentType:
   104  		return jsonapi.BadGateway(err)
   105  	case remote.ErrRemoteAssetNotFound:
   106  		return jsonapi.NotFound(err)
   107  	}
   108  	return err
   109  }
   110  
   111  func allowWholeType(c echo.Context, v permission.Verb, doctype string) (string, error) {
   112  	pdoc, err := middlewares.GetPermission(c)
   113  	if err != nil {
   114  		return "", err
   115  	}
   116  	if !pdoc.Permissions.AllowWholeType(v, doctype) {
   117  		return "", middlewares.ErrForbidden
   118  	}
   119  	slug := ""
   120  	if parts := strings.SplitN(pdoc.SourceID, "/", 2); len(parts) > 1 {
   121  		slug = parts[1]
   122  	}
   123  	return slug, nil
   124  }