aboutsummaryrefslogtreecommitdiff
path: root/app/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/api_user.go181
-rw-r--r--app/controllers/api_user_list.go56
2 files changed, 237 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}
diff --git a/app/controllers/api_user_list.go b/app/controllers/api_user_list.go
new file mode 100644
index 0000000..ba6dff5
--- /dev/null
+++ b/app/controllers/api_user_list.go
@@ -0,0 +1,56 @@
1package controllers
2
3import (
4 "context"
5 "net/http"
6 "time"
7
8 "code.crute.us/mcrute/cloud-identity-broker/app/models"
9
10 glecho "code.crute.us/mcrute/golib/echo"
11 "code.crute.us/mcrute/golib/echo/controller"
12 "github.com/labstack/echo/v4"
13)
14
15type jsonListUser struct {
16 Username string `bson:"_id" json:"key_id"`
17 IsAdmin bool `json:"is_admin"`
18 IsService bool `json:"is_service"`
19 SelfLink string `json:"self"`
20 Deleted *time.Time `json:"deleted,omitempty"`
21}
22
23type APIUserListHandler struct {
24 store models.UserStore
25}
26
27func NewAPIUserListHandler(s models.UserStore) echo.HandlerFunc {
28 al := &APIUserListHandler{store: s}
29 h := &controller.ContentTypeNegotiatingHandler{
30 DefaultHandler: al.Handle,
31 Handlers: map[string]echo.HandlerFunc{
32 contentTypeV2: al.Handle,
33 },
34 }
35 return h.Handle
36}
37
38func (h *APIUserListHandler) Handle(c echo.Context) error {
39 users, err := h.store.List(context.Background())
40 if err != nil {
41 return echo.ErrInternalServerError
42 }
43
44 out := map[string]*jsonListUser{}
45 for _, v := range users {
46 out[v.Username] = &jsonListUser{
47 Username: v.Username,
48 IsAdmin: v.IsAdmin,
49 IsService: v.IsService,
50 SelfLink: glecho.URLFor(c, "/api/user", v.Username).String(),
51 Deleted: v.Deleted,
52 }
53 }
54
55 return c.JSON(http.StatusOK, out)
56}