package controllers import ( "context" "net/http" "code.crute.us/mcrute/cloud-identity-broker/app/models" glecho "code.crute.us/mcrute/golib/echo" "code.crute.us/mcrute/golib/echo/controller" "github.com/labstack/echo/v4" ) type APIUserHandler struct { Store models.UserStore } func (h *APIUserHandler) Register(prefix string, r glecho.URLRouter, mw ...echo.MiddlewareFunc) { // This resource did not exist in the V1 API and thus has no V1 // representation. We use the default handlers for V1 because otherwise // requests with V1 Accept headers would result in 406 Unacceptable errors. gh := &controller.ContentTypeNegotiatingHandler{ DefaultHandler: h.HandleGet, Handlers: map[string]echo.HandlerFunc{ contentTypeV1: h.HandleGet, contentTypeV2: h.HandleGet, }, } r.GET(prefix, gh.Handle, mw...) ph := &controller.ContentTypeNegotiatingHandler{ DefaultHandler: h.HandlePut, Handlers: map[string]echo.HandlerFunc{ contentTypeV1: h.HandlePut, contentTypeV2: h.HandlePut, }, } r.PUT(prefix, ph.Handle, mw...) poh := &controller.ContentTypeNegotiatingHandler{ DefaultHandler: h.HandlePost, Handlers: map[string]echo.HandlerFunc{ contentTypeV1: h.HandlePost, contentTypeV2: h.HandlePost, }, } r.POST(prefix, poh.Handle, mw...) r.DELETE(prefix, h.HandleDelete, mw...) } func (h *APIUserHandler) HandleGet(c echo.Context) error { u, err := h.Store.Get(context.Background(), c.Param("user")) if err != nil { return echo.ErrInternalServerError } return c.JSON(http.StatusOK, u) } func validateKeysAndTokens(in *models.User) error { for k, v := range in.Keys { if k != v.KeyId { return &echo.HTTPError{ Code: http.StatusBadRequest, Message: "Key ID must match hash key.", } } if v.PrivateKey == nil && v.PublicKey == nil { return &echo.HTTPError{ Code: http.StatusBadRequest, Message: "One of public_key or private_key must be set", } } if v.PrivateKey != nil && v.PublicKey != nil { return &echo.HTTPError{ Code: http.StatusBadRequest, Message: "Only one of public_key or private_key may be set", } } } for k, v := range in.AuthTokens { if k != v.Kind { return &echo.HTTPError{ Code: http.StatusBadRequest, Message: "Token kind must match hash key.", } } } return nil } func (h *APIUserHandler) HandlePut(c echo.Context) error { var in models.User if err := c.Echo().JSONSerializer.Deserialize(c, &in); err != nil { return echo.ErrBadRequest } u, err := h.Store.Get(context.Background(), c.Param("user")) if err != nil { return echo.ErrInternalServerError } if in.Username != u.Username { return &echo.HTTPError{ Code: http.StatusBadRequest, Message: "Username can not be changed. Create a new user.", } } if in.Deleted != nil && u.Deleted == nil { return &echo.HTTPError{ Code: http.StatusBadRequest, Message: "Use the DELETE method to delete a record", } } if err := validateKeysAndTokens(&in); err != nil { return err } err = h.Store.Put(context.Background(), &in) if err != nil { return echo.ErrInternalServerError } return c.String(http.StatusNoContent, "") } func (h *APIUserHandler) HandlePost(c echo.Context) error { var in models.User if err := c.Echo().JSONSerializer.Deserialize(c, &in); err != nil { return echo.ErrBadRequest } _, err := h.Store.Get(context.Background(), c.Param("user")) if err == nil { return &echo.HTTPError{ Code: http.StatusConflict, Message: "User with username already exists.", } } if in.Deleted != nil { return &echo.HTTPError{ Code: http.StatusBadRequest, Message: "Can not create deleted user, set Deleted to null", } } if err := validateKeysAndTokens(&in); err != nil { return err } err = h.Store.Put(context.Background(), &in) if err != nil { return echo.ErrInternalServerError } c.Response().Header().Add("Location", glecho.URLFor(c, "/api/user", in.Username).String()) return c.String(http.StatusCreated, "") } func (h *APIUserHandler) HandleDelete(c echo.Context) error { u, err := h.Store.Get(context.Background(), c.Param("user")) if err != nil { return echo.ErrInternalServerError } err = h.Store.Delete(context.Background(), u) if err != nil { return echo.ErrInternalServerError } return c.String(http.StatusNoContent, "") }