2022-05-11 22:47:31 +00:00
|
|
|
package handler
|
|
|
|
|
|
|
|
import (
|
2023-01-09 13:31:47 +00:00
|
|
|
"database/sql"
|
2022-05-11 22:47:31 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
c "npm/internal/api/context"
|
|
|
|
h "npm/internal/api/http"
|
|
|
|
"npm/internal/api/middleware"
|
|
|
|
"npm/internal/api/schema"
|
|
|
|
"npm/internal/entity/certificate"
|
2023-01-09 13:31:47 +00:00
|
|
|
"npm/internal/entity/host"
|
2022-07-15 04:26:12 +00:00
|
|
|
"npm/internal/jobqueue"
|
|
|
|
"npm/internal/logger"
|
2022-05-11 22:47:31 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// GetCertificates will return a list of Certificates
|
|
|
|
// Route: GET /certificates
|
|
|
|
func GetCertificates() func(http.ResponseWriter, *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
pageInfo, err := getPageInfoFromRequest(r)
|
|
|
|
if err != nil {
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-07-25 01:59:02 +00:00
|
|
|
certificates, err := certificate.List(pageInfo, middleware.GetFiltersFromContext(r), middleware.GetExpandFromContext(r))
|
2022-05-11 22:47:31 +00:00
|
|
|
if err != nil {
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
|
|
} else {
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, certificates)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCertificate will return a single Certificate
|
|
|
|
// Route: GET /certificates/{certificateID}
|
|
|
|
func GetCertificate() func(http.ResponseWriter, *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2023-03-07 06:42:26 +00:00
|
|
|
if item := getCertificateFromRequest(w, r); item != nil {
|
2023-01-13 23:45:08 +00:00
|
|
|
// nolint: errcheck,gosec
|
2023-07-25 01:59:02 +00:00
|
|
|
item.Expand(middleware.GetExpandFromContext(r))
|
2023-01-13 23:45:08 +00:00
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
2022-05-11 22:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateCertificate will create a Certificate
|
|
|
|
// Route: POST /certificates
|
|
|
|
func CreateCertificate() func(http.ResponseWriter, *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2023-03-07 06:42:26 +00:00
|
|
|
var item certificate.Model
|
|
|
|
if fillObjectFromBody(w, r, "", &item) {
|
|
|
|
// Get userID from token
|
2023-05-26 01:04:43 +00:00
|
|
|
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
2023-03-07 06:42:26 +00:00
|
|
|
item.UserID = userID
|
|
|
|
|
|
|
|
if err := item.Save(); err != nil {
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Certificate: %s", err.Error()), nil)
|
|
|
|
return
|
|
|
|
}
|
2022-05-11 22:47:31 +00:00
|
|
|
|
2023-03-07 06:42:26 +00:00
|
|
|
configureCertificate(item)
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
2022-05-11 22:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateCertificate updates a cert
|
|
|
|
// Route: PUT /certificates/{certificateID}
|
|
|
|
func UpdateCertificate() func(http.ResponseWriter, *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2023-03-07 06:42:26 +00:00
|
|
|
if item := getCertificateFromRequest(w, r); item != nil {
|
2022-05-11 22:47:31 +00:00
|
|
|
// This is a special endpoint, as it needs to verify the schema payload
|
|
|
|
// based on the certificate type, without being given a type in the payload.
|
|
|
|
// The middleware would normally handle this.
|
2023-03-07 06:42:26 +00:00
|
|
|
if fillObjectFromBody(w, r, schema.UpdateCertificate(item.Type), item) {
|
|
|
|
if err := item.Save(); err != nil {
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// configureCertificate(*item, item.Request)
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
2022-05-11 22:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteCertificate deletes a cert
|
|
|
|
// Route: DELETE /certificates/{certificateID}
|
|
|
|
func DeleteCertificate() func(http.ResponseWriter, *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2023-03-07 06:42:26 +00:00
|
|
|
if item := getCertificateFromRequest(w, r); item != nil {
|
|
|
|
cnt := host.GetCertificateUseCount(item.ID)
|
2023-01-09 13:31:47 +00:00
|
|
|
if cnt > 0 {
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, "Cannot delete certificate that is in use by at least 1 host", nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
|
2022-05-11 22:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-15 04:26:12 +00:00
|
|
|
|
2023-03-07 06:42:26 +00:00
|
|
|
// RenewCertificate is self explanatory
|
|
|
|
// Route: PUT /certificates/{certificateID}/renew
|
|
|
|
func RenewCertificate() func(http.ResponseWriter, *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if item := getCertificateFromRequest(w, r); item != nil {
|
|
|
|
configureCertificate(*item)
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// DownloadCertificate is self explanatory
|
|
|
|
// Route: PUT /certificates/{certificateID}/download
|
|
|
|
func DownloadCertificate() func(http.ResponseWriter, *http.Request) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if item := getCertificateFromRequest(w, r); item != nil {
|
|
|
|
// todo
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, "ok")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// getCertificateFromRequest has some reusable code for all endpoints that
|
|
|
|
// have a certificate id in the url. it will write errors to the output.
|
|
|
|
func getCertificateFromRequest(w http.ResponseWriter, r *http.Request) *certificate.Model {
|
|
|
|
var err error
|
2023-05-26 01:04:43 +00:00
|
|
|
var certificateID uint
|
2023-03-07 06:42:26 +00:00
|
|
|
if certificateID, err = getURLParamInt(r, "certificateID"); err != nil {
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
certificateObject, err := certificate.GetByID(certificateID)
|
|
|
|
switch err {
|
|
|
|
case sql.ErrNoRows:
|
|
|
|
h.NotFound(w, r)
|
|
|
|
case nil:
|
|
|
|
return &certificateObject
|
|
|
|
default:
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-05-30 12:26:44 +00:00
|
|
|
// fillObjectFromBody has some reusable code for all endpoints that
|
2023-03-07 06:42:26 +00:00
|
|
|
// have a certificate id in the url. it will write errors to the output.
|
|
|
|
func fillObjectFromBody(w http.ResponseWriter, r *http.Request, validationSchema string, o interface{}) bool {
|
|
|
|
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
|
|
|
|
|
|
|
if validationSchema != "" {
|
|
|
|
schemaErrors, jsonErr := middleware.CheckRequestSchema(r.Context(), validationSchema, bodyBytes)
|
|
|
|
if jsonErr != nil {
|
|
|
|
h.ResultErrorJSON(w, r, http.StatusInternalServerError, fmt.Sprintf("Schema Fatal: %v", jsonErr), nil)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if len(schemaErrors) > 0 {
|
|
|
|
h.ResultSchemaErrorJSON(w, r, schemaErrors)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := json.Unmarshal(bodyBytes, o)
|
|
|
|
if err != nil {
|
2023-05-30 12:26:44 +00:00
|
|
|
logger.Debug("Unmarshal Error: %+v", err)
|
2023-03-07 06:42:26 +00:00
|
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-07-15 04:26:12 +00:00
|
|
|
func configureCertificate(c certificate.Model) {
|
|
|
|
err := jobqueue.AddJob(jobqueue.Job{
|
|
|
|
Name: "RequestCertificate",
|
|
|
|
Action: c.Request,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
logger.Error("ConfigureCertificateError", err)
|
|
|
|
}
|
|
|
|
}
|