github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/auth/register.go (about) 1 package auth 2 3 import ( 4 "encoding/json" 5 "errors" 6 "net/http" 7 "strings" 8 9 "github.com/cozy/cozy-stack/model/oauth" 10 "github.com/cozy/cozy-stack/pkg/config/config" 11 "github.com/cozy/cozy-stack/pkg/consts" 12 "github.com/cozy/cozy-stack/pkg/couchdb" 13 "github.com/cozy/cozy-stack/pkg/limits" 14 "github.com/cozy/cozy-stack/web/middlewares" 15 "github.com/labstack/echo/v4" 16 ) 17 18 func registerClient(c echo.Context) error { 19 instance := middlewares.GetInstance(c) 20 err := config.GetRateLimiter().CheckRateLimit(instance, limits.OAuthClientType) 21 if limits.IsLimitReachedOrExceeded(err) { 22 return echo.NewHTTPError(http.StatusNotFound, "Not found") 23 } 24 client := new(oauth.Client) 25 if err := json.NewDecoder(c.Request().Body).Decode(client); err != nil { 26 return err 27 } 28 // We do not allow the creation of clients allowed to have an empty scope 29 // ("login" scope), except via the CLI. 30 if client.AllowLoginScope { 31 if _, ok := middlewares.GetCLIPermission(c); !ok { 32 return echo.NewHTTPError(http.StatusUnauthorized, 33 "Not authorized to create client with given parameters") 34 } 35 } 36 if err := client.Create(instance); err != nil { 37 return c.JSON(err.Code, err) 38 } 39 return c.JSON(http.StatusCreated, client) 40 } 41 42 func readClient(c echo.Context) error { 43 client := c.Get("client").(*oauth.Client) 44 client.TransformIDAndRev() 45 return c.JSON(http.StatusOK, client) 46 } 47 48 func updateClient(c echo.Context) error { 49 instance := middlewares.GetInstance(c) 50 err := config.GetRateLimiter().CheckRateLimit(instance, limits.OAuthClientType) 51 if limits.IsLimitReachedOrExceeded(err) { 52 return echo.NewHTTPError(http.StatusNotFound, "Not found") 53 } 54 55 clientID := c.Param("client-id") 56 defer LockOAuthClient(instance, clientID)() 57 58 oldClient, err := oauth.FindClient(instance, clientID) 59 if err != nil { 60 return c.JSON(http.StatusNotFound, echo.Map{ 61 "error": "Client not found", 62 }) 63 } 64 if err := checkClientToken(c, oldClient); err != nil { 65 return c.JSON(http.StatusUnauthorized, echo.Map{ 66 "error": err.Error(), 67 }) 68 } 69 70 client := new(oauth.Client) 71 if err := json.NewDecoder(c.Request().Body).Decode(client); err != nil { 72 return err 73 } 74 if err := client.Update(instance, oldClient); err != nil { 75 return c.JSON(err.Code, err) 76 } 77 return c.JSON(http.StatusOK, client) 78 } 79 80 func deleteClient(c echo.Context) error { 81 instance := middlewares.GetInstance(c) 82 clientID := c.Param("client-id") 83 defer LockOAuthClient(instance, clientID)() 84 85 client, err := oauth.FindClient(instance, clientID) 86 if err != nil { 87 if couchdb.IsNotFoundError(err) { 88 return c.NoContent(http.StatusNoContent) 89 } 90 return c.JSON(http.StatusInternalServerError, echo.Map{ 91 "error": err.Error(), 92 }) 93 } 94 if err := checkClientToken(c, client); err != nil { 95 return c.JSON(http.StatusUnauthorized, echo.Map{ 96 "error": err.Error(), 97 }) 98 } 99 if err := client.Delete(instance); err != nil { 100 return c.JSON(err.Code, err) 101 } 102 return c.NoContent(http.StatusNoContent) 103 } 104 105 func checkRegistrationToken(next echo.HandlerFunc) echo.HandlerFunc { 106 return func(c echo.Context) error { 107 instance := middlewares.GetInstance(c) 108 client, err := oauth.FindClient(instance, c.Param("client-id")) 109 if err != nil { 110 return c.JSON(http.StatusNotFound, echo.Map{ 111 "error": "Client not found", 112 }) 113 } 114 if err := checkClientToken(c, client); err != nil { 115 return c.JSON(http.StatusUnauthorized, echo.Map{ 116 "error": err.Error(), 117 }) 118 } 119 c.Set("client", client) 120 return next(c) 121 } 122 } 123 124 func checkClientToken(c echo.Context, client *oauth.Client) error { 125 header := c.Request().Header.Get(echo.HeaderAuthorization) 126 if !strings.HasPrefix(header, "Bearer ") { 127 return errors.New("invalid_token") 128 } 129 token := header[len("Bearer "):] 130 instance := middlewares.GetInstance(c) 131 _, ok := client.ValidToken(instance, consts.RegistrationTokenAudience, token) 132 if !ok { 133 return errors.New("invalid_token") 134 } 135 return nil 136 }