Adding healthcheck endpoint for sms
[aaf/sms.git] / sms-service / src / sms / handler / handler.go
1 /*
2  * Copyright 2018 Intel Corporation, Inc
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package handler
18
19 import (
20         "encoding/json"
21         "github.com/gorilla/mux"
22         "net/http"
23
24         uuid "github.com/hashicorp/go-uuid"
25         smsbackend "sms/backend"
26         smslogger "sms/log"
27 )
28
29 // handler stores two interface implementations that implement
30 // the backend functionality
31 type handler struct {
32         secretBackend smsbackend.SecretBackend
33         loginBackend  smsbackend.LoginBackend
34 }
35
36 // createSecretDomainHandler creates a secret domain with a name provided
37 func (h handler) createSecretDomainHandler(w http.ResponseWriter, r *http.Request) {
38         var d smsbackend.SecretDomain
39
40         err := json.NewDecoder(r.Body).Decode(&d)
41         if smslogger.CheckError(err, "CreateSecretDomainHandler") != nil {
42                 http.Error(w, err.Error(), http.StatusBadRequest)
43                 return
44         }
45
46         dom, err := h.secretBackend.CreateSecretDomain(d.Name)
47         if smslogger.CheckError(err, "CreateSecretDomainHandler") != nil {
48                 http.Error(w, err.Error(), http.StatusInternalServerError)
49                 return
50         }
51
52         w.Header().Set("Content-Type", "application/json")
53         w.WriteHeader(http.StatusCreated)
54         err = json.NewEncoder(w).Encode(dom)
55         if smslogger.CheckError(err, "CreateSecretDomainHandler") != nil {
56                 http.Error(w, err.Error(), http.StatusInternalServerError)
57                 return
58         }
59 }
60
61 // deleteSecretDomainHandler deletes a secret domain with the name provided
62 func (h handler) deleteSecretDomainHandler(w http.ResponseWriter, r *http.Request) {
63         vars := mux.Vars(r)
64         domName := vars["domName"]
65
66         err := h.secretBackend.DeleteSecretDomain(domName)
67         if smslogger.CheckError(err, "DeleteSecretDomainHandler") != nil {
68                 http.Error(w, err.Error(), http.StatusInternalServerError)
69                 return
70         }
71
72         w.WriteHeader(http.StatusNoContent)
73 }
74
75 // createSecretHandler handles creation of secrets on a given domain name
76 func (h handler) createSecretHandler(w http.ResponseWriter, r *http.Request) {
77         // Get domain name from URL
78         vars := mux.Vars(r)
79         domName := vars["domName"]
80
81         // Get secrets to be stored from body
82         var b smsbackend.Secret
83         err := json.NewDecoder(r.Body).Decode(&b)
84         if smslogger.CheckError(err, "CreateSecretHandler") != nil {
85                 http.Error(w, err.Error(), http.StatusBadRequest)
86                 return
87         }
88
89         err = h.secretBackend.CreateSecret(domName, b)
90         if smslogger.CheckError(err, "CreateSecretHandler") != nil {
91                 http.Error(w, err.Error(), http.StatusInternalServerError)
92                 return
93         }
94
95         w.WriteHeader(http.StatusCreated)
96 }
97
98 // getSecretHandler handles reading a secret by given domain name and secret name
99 func (h handler) getSecretHandler(w http.ResponseWriter, r *http.Request) {
100         vars := mux.Vars(r)
101         domName := vars["domName"]
102         secName := vars["secretName"]
103
104         sec, err := h.secretBackend.GetSecret(domName, secName)
105         if smslogger.CheckError(err, "GetSecretHandler") != nil {
106                 http.Error(w, err.Error(), http.StatusInternalServerError)
107                 return
108         }
109
110         w.Header().Set("Content-Type", "application/json")
111         err = json.NewEncoder(w).Encode(sec)
112         if smslogger.CheckError(err, "GetSecretHandler") != nil {
113                 http.Error(w, err.Error(), http.StatusInternalServerError)
114                 return
115         }
116 }
117
118 // listSecretHandler handles listing all secrets under a particular domain name
119 func (h handler) listSecretHandler(w http.ResponseWriter, r *http.Request) {
120         vars := mux.Vars(r)
121         domName := vars["domName"]
122
123         secList, err := h.secretBackend.ListSecret(domName)
124         if smslogger.CheckError(err, "ListSecretHandler") != nil {
125                 http.Error(w, err.Error(), http.StatusInternalServerError)
126                 return
127         }
128
129         // Creating an anonymous struct to store the returned list of data
130         var retStruct = struct {
131                 SecretNames []string `json:"secretnames"`
132         }{
133                 secList,
134         }
135
136         w.Header().Set("Content-Type", "application/json")
137         err = json.NewEncoder(w).Encode(retStruct)
138         if smslogger.CheckError(err, "ListSecretHandler") != nil {
139                 http.Error(w, err.Error(), http.StatusInternalServerError)
140                 return
141         }
142 }
143
144 // deleteSecretHandler handles deleting a secret by given domain name and secret name
145 func (h handler) deleteSecretHandler(w http.ResponseWriter, r *http.Request) {
146         vars := mux.Vars(r)
147         domName := vars["domName"]
148         secName := vars["secretName"]
149
150         err := h.secretBackend.DeleteSecret(domName, secName)
151         if smslogger.CheckError(err, "DeleteSecretHandler") != nil {
152                 http.Error(w, err.Error(), http.StatusInternalServerError)
153                 return
154         }
155
156         w.WriteHeader(http.StatusNoContent)
157 }
158
159 // statusHandler returns information related to SMS and SMS backend services
160 func (h handler) statusHandler(w http.ResponseWriter, r *http.Request) {
161         s, err := h.secretBackend.GetStatus()
162         if smslogger.CheckError(err, "StatusHandler") != nil {
163                 http.Error(w, err.Error(), http.StatusInternalServerError)
164                 return
165         }
166
167         status := struct {
168                 Seal bool `json:"sealstatus"`
169         }{
170                 s,
171         }
172
173         w.Header().Set("Content-Type", "application/json")
174         err = json.NewEncoder(w).Encode(status)
175         if smslogger.CheckError(err, "StatusHandler") != nil {
176                 http.Error(w, err.Error(), http.StatusInternalServerError)
177                 return
178         }
179 }
180
181 // loginHandler handles login via password and username
182 func (h handler) loginHandler(w http.ResponseWriter, r *http.Request) {
183
184 }
185
186 // unsealHandler is a pass through that sends requests from quorum client
187 // to the backend.
188 func (h handler) unsealHandler(w http.ResponseWriter, r *http.Request) {
189         // Get shards to be used for unseal
190         type unsealStruct struct {
191                 UnsealShard string `json:"unsealshard"`
192         }
193
194         var inp unsealStruct
195         decoder := json.NewDecoder(r.Body)
196         decoder.DisallowUnknownFields()
197         err := decoder.Decode(&inp)
198         if smslogger.CheckError(err, "UnsealHandler") != nil {
199                 http.Error(w, "Bad input JSON", http.StatusBadRequest)
200                 return
201         }
202
203         err = h.secretBackend.Unseal(inp.UnsealShard)
204         if smslogger.CheckError(err, "UnsealHandler") != nil {
205                 http.Error(w, err.Error(), http.StatusInternalServerError)
206                 return
207         }
208 }
209
210 // registerHandler allows the quorum clients to register with SMS
211 // with their PGP public keys that are then used by sms for backend
212 // initialization
213 func (h handler) registerHandler(w http.ResponseWriter, r *http.Request) {
214
215         // Get shards to be used for unseal
216         type registerStruct struct {
217                 PGPKey   string `json:"pgpkey"`
218                 QuorumID string `json:"quorumid"`
219         }
220
221         var inp registerStruct
222         decoder := json.NewDecoder(r.Body)
223         decoder.DisallowUnknownFields()
224         err := decoder.Decode(&inp)
225         if smslogger.CheckError(err, "RegisterHandler") != nil {
226                 http.Error(w, "Bad input JSON", http.StatusBadRequest)
227                 return
228         }
229
230         sh, err := h.secretBackend.RegisterQuorum(inp.PGPKey)
231         if smslogger.CheckError(err, "RegisterHandler") != nil {
232                 http.Error(w, err.Error(), http.StatusInternalServerError)
233                 return
234         }
235
236         // Creating a struct for return data
237         shStruct := struct {
238                 Shard string `json:"shard"`
239         }{
240                 sh,
241         }
242
243         w.Header().Set("Content-Type", "application/json")
244         err = json.NewEncoder(w).Encode(shStruct)
245         if smslogger.CheckError(err, "RegisterHandler") != nil {
246                 http.Error(w, err.Error(), http.StatusInternalServerError)
247                 return
248         }
249 }
250
251 // healthCheckHandler runs a few commands on the backend and returns
252 // OK or not depending on the status of the backend
253 func (h handler) healthCheckHandler(w http.ResponseWriter, r *http.Request) {
254
255         sealed, err := h.secretBackend.GetStatus()
256         if smslogger.CheckError(err, "HealthCheck") != nil {
257                 http.Error(w, err.Error(), http.StatusInternalServerError)
258                 return
259         }
260
261         // backend is sealed
262         if sealed == true {
263                 http.Error(w, "Secret Backend is not ready for operations", http.StatusInternalServerError)
264                 return
265         }
266
267         // backend is not sealed
268         dname, _ := uuid.GenerateUUID()
269         _, err = h.secretBackend.CreateSecretDomain(dname)
270         if smslogger.CheckError(err, "HealthCheck Create Domain") != nil {
271                 http.Error(w, err.Error(), http.StatusInternalServerError)
272                 return
273         }
274
275         err = h.secretBackend.DeleteSecretDomain(dname)
276         if smslogger.CheckError(err, "HealthCheck Delete Domain") != nil {
277                 http.Error(w, err.Error(), http.StatusInternalServerError)
278                 return
279         }
280
281         w.WriteHeader(http.StatusOK)
282 }
283
284 // CreateRouter returns an http.Handler for the registered URLs
285 // Takes an interface implementation as input
286 func CreateRouter(b smsbackend.SecretBackend) http.Handler {
287         h := handler{secretBackend: b}
288
289         // Create a new mux to handle URL endpoints
290         router := mux.NewRouter()
291
292         router.HandleFunc("/v1/sms/login", h.loginHandler).Methods("POST")
293
294         // Initialization APIs which will be used by quorum client
295         // to unseal and to provide root token to sms service
296         router.HandleFunc("/v1/sms/quorum/status", h.statusHandler).Methods("GET")
297         router.HandleFunc("/v1/sms/quorum/unseal", h.unsealHandler).Methods("POST")
298         router.HandleFunc("/v1/sms/quorum/register", h.registerHandler).Methods("POST")
299
300         router.HandleFunc("/v1/sms/healthcheck", h.healthCheckHandler).Methods("GET")
301         router.HandleFunc("/v1/sms/domain", h.createSecretDomainHandler).Methods("POST")
302         router.HandleFunc("/v1/sms/domain/{domName}", h.deleteSecretDomainHandler).Methods("DELETE")
303
304         router.HandleFunc("/v1/sms/domain/{domName}/secret", h.createSecretHandler).Methods("POST")
305         router.HandleFunc("/v1/sms/domain/{domName}/secret", h.listSecretHandler).Methods("GET")
306         router.HandleFunc("/v1/sms/domain/{domName}/secret/{secretName}", h.getSecretHandler).Methods("GET")
307         router.HandleFunc("/v1/sms/domain/{domName}/secret/{secretName}", h.deleteSecretHandler).Methods("DELETE")
308
309         return router
310 }