aboutsummaryrefslogtreecommitdiff
path: root/app/controllers/api_user.go
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers/api_user.go')
-rw-r--r--app/controllers/api_user.go181
1 files changed, 181 insertions, 0 deletions
diff --git a/app/controllers/api_user.go b/app/controllers/api_user.go
new file mode 100644
index 0000000..df667db
--- /dev/null
+++ b/app/controllers/api_user.go
@@ -0,0 +1,181 @@
1package controllers
2
3import (
4 "context"
5 "net/http"
6
7 "code.crute.us/mcrute/cloud-identity-broker/app/models"
8
9 glecho "code.crute.us/mcrute/golib/echo"
10 "code.crute.us/mcrute/golib/echo/controller"
11 "github.com/labstack/echo/v4"
12)
13
14type APIUserHandler struct {
15 Store models.UserStore
16}
17
18func (h *APIUserHandler) Register(prefix string, r glecho.URLRouter, mw ...echo.MiddlewareFunc) {
19 // This resource did not exist in the V1 API and thus has no V1
20 // representation. We use the default handlers for V1 because otherwise
21 // requests with V1 Accept headers would result in 406 Unacceptable errors.
22 gh := &controller.ContentTypeNegotiatingHandler{
23 DefaultHandler: h.HandleGet,
24 Handlers: map[string]echo.HandlerFunc{
25 contentTypeV1: h.HandleGet,
26 contentTypeV2: h.HandleGet,
27 },
28 }
29 r.GET(prefix, gh.Handle, mw...)
30
31 ph := &controller.ContentTypeNegotiatingHandler{
32 DefaultHandler: h.HandlePut,
33 Handlers: map[string]echo.HandlerFunc{
34 contentTypeV1: h.HandlePut,
35 contentTypeV2: h.HandlePut,
36 },
37 }
38 r.PUT(prefix, ph.Handle, mw...)
39
40 poh := &controller.ContentTypeNegotiatingHandler{
41 DefaultHandler: h.HandlePost,
42 Handlers: map[string]echo.HandlerFunc{
43 contentTypeV1: h.HandlePost,
44 contentTypeV2: h.HandlePost,
45 },
46 }
47 r.POST(prefix, poh.Handle, mw...)
48
49 r.DELETE(prefix, h.HandleDelete, mw...)
50}
51
52func (h *APIUserHandler) HandleGet(c echo.Context) error {
53 u, err := h.Store.Get(context.Background(), c.Param("user"))
54 if err != nil {
55 return echo.ErrInternalServerError
56 }
57
58 return c.JSON(http.StatusOK, u)
59}
60
61func validateKeysAndTokens(in *models.User) error {
62 for k, v := range in.Keys {
63 if k != v.KeyId {
64 return &echo.HTTPError{
65 Code: http.StatusBadRequest,
66 Message: "Key ID must match hash key.",
67 }
68 }
69
70 if v.PrivateKey == nil && v.PublicKey == nil {
71 return &echo.HTTPError{
72 Code: http.StatusBadRequest,
73 Message: "One of public_key or private_key must be set",
74 }
75 }
76
77 if v.PrivateKey != nil && v.PublicKey != nil {
78 return &echo.HTTPError{
79 Code: http.StatusBadRequest,
80 Message: "Only one of public_key or private_key may be set",
81 }
82 }
83 }
84
85 for k, v := range in.AuthTokens {
86 if k != v.Kind {
87 return &echo.HTTPError{
88 Code: http.StatusBadRequest,
89 Message: "Token kind must match hash key.",
90 }
91 }
92 }
93
94 return nil
95}
96
97func (h *APIUserHandler) HandlePut(c echo.Context) error {
98 var in models.User
99 if err := c.Echo().JSONSerializer.Deserialize(c, &in); err != nil {
100 return echo.ErrBadRequest
101 }
102
103 u, err := h.Store.Get(context.Background(), c.Param("user"))
104 if err != nil {
105 return echo.ErrInternalServerError
106 }
107
108 if in.Username != u.Username {
109 return &echo.HTTPError{
110 Code: http.StatusBadRequest,
111 Message: "Username can not be changed. Create a new user.",
112 }
113 }
114
115 if in.Deleted != nil && u.Deleted == nil {
116 return &echo.HTTPError{
117 Code: http.StatusBadRequest,
118 Message: "Use the DELETE method to delete a record",
119 }
120 }
121
122 if err := validateKeysAndTokens(&in); err != nil {
123 return err
124 }
125
126 err = h.Store.Put(context.Background(), &in)
127 if err != nil {
128 return echo.ErrInternalServerError
129 }
130
131 return c.String(http.StatusNoContent, "")
132}
133
134func (h *APIUserHandler) HandlePost(c echo.Context) error {
135 var in models.User
136 if err := c.Echo().JSONSerializer.Deserialize(c, &in); err != nil {
137 return echo.ErrBadRequest
138 }
139
140 _, err := h.Store.Get(context.Background(), c.Param("user"))
141 if err == nil {
142 return &echo.HTTPError{
143 Code: http.StatusConflict,
144 Message: "User with username already exists.",
145 }
146 }
147
148 if in.Deleted != nil {
149 return &echo.HTTPError{
150 Code: http.StatusBadRequest,
151 Message: "Can not create deleted user, set Deleted to null",
152 }
153 }
154
155 if err := validateKeysAndTokens(&in); err != nil {
156 return err
157 }
158
159 err = h.Store.Put(context.Background(), &in)
160 if err != nil {
161 return echo.ErrInternalServerError
162 }
163
164 c.Response().Header().Add("Location", glecho.URLFor(c, "/api/user", in.Username).String())
165
166 return c.String(http.StatusCreated, "")
167}
168
169func (h *APIUserHandler) HandleDelete(c echo.Context) error {
170 u, err := h.Store.Get(context.Background(), c.Param("user"))
171 if err != nil {
172 return echo.ErrInternalServerError
173 }
174
175 err = h.Store.Delete(context.Background(), u)
176 if err != nil {
177 return echo.ErrInternalServerError
178 }
179
180 return c.String(http.StatusNoContent, "")
181}