nginx-proxy-manager/backend/internal/api/handler/certificates.go

186 lines
5.7 KiB
Go
Raw Normal View History

package handler
import (
"database/sql"
"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"
"npm/internal/entity/host"
2022-07-15 04:26:12 +00:00
"npm/internal/jobqueue"
"npm/internal/logger"
)
// 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
}
certificates, err := certificate.List(pageInfo, middleware.GetFiltersFromContext(r), getExpandFromContext(r))
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) {
if item := getCertificateFromRequest(w, r); item != nil {
// nolint: errcheck,gosec
item.Expand(getExpandFromContext(r))
h.ResultResponseJSON(w, r, http.StatusOK, item)
}
}
}
// CreateCertificate will create a Certificate
// Route: POST /certificates
func CreateCertificate() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var item certificate.Model
if fillObjectFromBody(w, r, "", &item) {
// Get userID from token
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
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
}
configureCertificate(item)
h.ResultResponseJSON(w, r, http.StatusOK, item)
}
}
}
// UpdateCertificate updates a cert
// Route: PUT /certificates/{certificateID}
func UpdateCertificate() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if item := getCertificateFromRequest(w, r); item != nil {
// 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.
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)
}
}
}
}
// DeleteCertificate deletes a cert
// Route: DELETE /certificates/{certificateID}
func DeleteCertificate() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if item := getCertificateFromRequest(w, r); item != nil {
cnt := host.GetCertificateUseCount(item.ID)
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-07-15 04:26:12 +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
var certificateID uint
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
}
// getCertificateFromRequest has some reusable code for all endpoints that
// 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 {
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)
}
}