290 lines
7.9 KiB
Go
290 lines
7.9 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
|
|
"git.apinb.com/ops/license/internal/issuance"
|
|
"git.apinb.com/ops/license/internal/subject"
|
|
"git.apinb.com/ops/license/sdk/licence"
|
|
)
|
|
|
|
const timeLayout = time.RFC3339Nano
|
|
|
|
type issuanceHandler struct {
|
|
service *issuance.Service
|
|
subjectService *subject.Service
|
|
}
|
|
|
|
type issueRequest struct {
|
|
ValidFrom string `json:"valid_from"`
|
|
ExpiresOn string `json:"expires_on"`
|
|
Quotas quotaRequest `json:"quotas"`
|
|
}
|
|
|
|
type quotaRequest struct {
|
|
MaxDatabase *int64 `json:"max_database"`
|
|
MaxMiddleware *int64 `json:"max_middleware"`
|
|
MaxNetworkDevice *int64 `json:"max_network_device"`
|
|
MaxSecurity *int64 `json:"max_security"`
|
|
MaxStorage *int64 `json:"max_storage"`
|
|
MaxPC *int64 `json:"max_pc"`
|
|
MaxServer *int64 `json:"max_server"`
|
|
MaxUser *int64 `json:"max_user"`
|
|
MaxRole *int64 `json:"max_role"`
|
|
MaxPermission *int64 `json:"max_permission"`
|
|
MaxMenu *int64 `json:"max_menu"`
|
|
}
|
|
|
|
type issuanceSummaryView struct {
|
|
ID uuid.UUID `json:"id"`
|
|
SubjectID uuid.UUID `json:"subject_id"`
|
|
PlatformName string `json:"platform_name"`
|
|
Workspace string `json:"workspace"`
|
|
IssuanceType string `json:"issuance_type"`
|
|
PreviousLicenceID *uuid.UUID `json:"previous_licence_id"`
|
|
ValidFrom string `json:"valid_from"`
|
|
ExpiresOn string `json:"expires_on"`
|
|
IssuedAt string `json:"issued_at"`
|
|
}
|
|
|
|
type issuanceDetailView struct {
|
|
issuanceSummaryView
|
|
Quotas licence.Quotas `json:"quotas"`
|
|
SigningKeyID uuid.UUID `json:"signing_key_id"`
|
|
OperatorName string `json:"operator_name"`
|
|
}
|
|
|
|
func (h issuanceHandler) issue(c *gin.Context) {
|
|
subjectID, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
input, ok := readIssueInput(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
record, err := h.service.Issue(c.Request.Context(), subjectID, input, "system")
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeLicence(c, record.FileContent, record.ID)
|
|
}
|
|
|
|
func (h issuanceHandler) renew(c *gin.Context) {
|
|
previousID, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
input, ok := readIssueInput(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
record, err := h.service.Renew(c.Request.Context(), previousID, input, "system")
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeLicence(c, record.FileContent, record.ID)
|
|
}
|
|
|
|
func (h issuanceHandler) list(c *gin.Context) {
|
|
page, pageSize, ok := parsePagination(c)
|
|
if !ok {
|
|
writeBadRequest(c)
|
|
return
|
|
}
|
|
filter, ok := parseIssuanceFilter(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var items []issuance.RecordSummary
|
|
var total int64
|
|
var err error
|
|
if rawSubjectID, exists := c.GetQuery("subject_id"); exists {
|
|
if rawSubjectID == "" || hasGeneralIssuanceFilter(c) {
|
|
writeBadRequest(c)
|
|
return
|
|
}
|
|
subjectID, parseErr := uuid.Parse(rawSubjectID)
|
|
if parseErr != nil || subjectID == uuid.Nil {
|
|
writeBadRequest(c)
|
|
return
|
|
}
|
|
items, total, err = h.service.ListBySubject(c.Request.Context(), subjectID, page, pageSize)
|
|
} else {
|
|
items, total, err = h.service.List(c.Request.Context(), filter, page, pageSize)
|
|
}
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
views := make([]issuanceSummaryView, len(items))
|
|
for index := range items {
|
|
views[index] = newIssuanceSummaryView(items[index])
|
|
}
|
|
writePage(c, views, page, pageSize, total)
|
|
}
|
|
|
|
func (h issuanceHandler) get(c *gin.Context) {
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
record, err := h.service.Get(c.Request.Context(), id)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
item, err := h.subjectService.Get(c.Request.Context(), record.SubjectID)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
summary := issuance.RecordSummary{
|
|
ID: record.ID,
|
|
SubjectID: record.SubjectID,
|
|
PlatformName: item.PlatformName,
|
|
Workspace: item.Workspace,
|
|
IssuanceType: record.IssuanceType,
|
|
PreviousLicenceID: record.PreviousLicenceID,
|
|
ValidFrom: record.ValidFrom,
|
|
ExpiresOn: record.ExpiresOn,
|
|
IssuedAt: record.IssuedAt,
|
|
}
|
|
writeData(c, http.StatusOK, issuanceDetailView{
|
|
issuanceSummaryView: newIssuanceSummaryView(summary),
|
|
Quotas: record.Quotas,
|
|
SigningKeyID: record.SigningKeyID,
|
|
OperatorName: "system",
|
|
})
|
|
}
|
|
|
|
func (h issuanceHandler) download(c *gin.Context) {
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
content, metadata, err := h.service.Download(c.Request.Context(), id)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeLicence(c, content, metadata.ID)
|
|
}
|
|
|
|
func readIssueInput(c *gin.Context) (issuance.IssueInput, bool) {
|
|
var request issueRequest
|
|
if err := decodeJSON(c, &request); err != nil {
|
|
writeBadRequest(c)
|
|
return issuance.IssueInput{}, false
|
|
}
|
|
validFrom, err := licence.ParseDate(request.ValidFrom)
|
|
if err != nil {
|
|
writeError(c, issuance.ErrInvalidDates)
|
|
return issuance.IssueInput{}, false
|
|
}
|
|
expiresOn, err := licence.ParseDate(request.ExpiresOn)
|
|
if err != nil {
|
|
writeError(c, issuance.ErrInvalidDates)
|
|
return issuance.IssueInput{}, false
|
|
}
|
|
validFromTime, _ := validFrom.Time()
|
|
expiresOnTime, _ := expiresOn.Time()
|
|
quotas, ok := request.Quotas.value()
|
|
if !ok {
|
|
writeError(c, issuance.ErrInvalidInput)
|
|
return issuance.IssueInput{}, false
|
|
}
|
|
return issuance.IssueInput{ValidFrom: validFromTime, ExpiresOn: expiresOnTime, Quotas: quotas}, true
|
|
}
|
|
|
|
func (q quotaRequest) value() (licence.Quotas, bool) {
|
|
if q.MaxDatabase == nil || q.MaxMiddleware == nil || q.MaxNetworkDevice == nil || q.MaxSecurity == nil ||
|
|
q.MaxStorage == nil || q.MaxPC == nil || q.MaxServer == nil || q.MaxUser == nil || q.MaxRole == nil ||
|
|
q.MaxPermission == nil || q.MaxMenu == nil {
|
|
return licence.Quotas{}, false
|
|
}
|
|
return licence.Quotas{
|
|
MaxDatabase: *q.MaxDatabase,
|
|
MaxMiddleware: *q.MaxMiddleware,
|
|
MaxNetworkDevice: *q.MaxNetworkDevice,
|
|
MaxSecurity: *q.MaxSecurity,
|
|
MaxStorage: *q.MaxStorage,
|
|
MaxPC: *q.MaxPC,
|
|
MaxServer: *q.MaxServer,
|
|
MaxUser: *q.MaxUser,
|
|
MaxRole: *q.MaxRole,
|
|
MaxPermission: *q.MaxPermission,
|
|
MaxMenu: *q.MaxMenu,
|
|
}, true
|
|
}
|
|
|
|
func parseIssuanceFilter(c *gin.Context) (issuance.Filter, bool) {
|
|
filter := issuance.Filter{
|
|
PlatformName: c.Query("platform_name"),
|
|
Workspace: c.Query("workspace"),
|
|
}
|
|
var ok bool
|
|
if filter.ValidFrom, ok = parseOptionalDate(c, "valid_from"); !ok {
|
|
return issuance.Filter{}, false
|
|
}
|
|
if filter.ExpiresOn, ok = parseOptionalDate(c, "expires_on"); !ok {
|
|
return issuance.Filter{}, false
|
|
}
|
|
if filter.IssuedOn, ok = parseOptionalDate(c, "issued_on"); !ok {
|
|
return issuance.Filter{}, false
|
|
}
|
|
return filter, true
|
|
}
|
|
|
|
func parseOptionalDate(c *gin.Context, name string) (*time.Time, bool) {
|
|
raw, exists := c.GetQuery(name)
|
|
if !exists {
|
|
return nil, true
|
|
}
|
|
date, err := licence.ParseDate(raw)
|
|
if err != nil {
|
|
writeBadRequest(c)
|
|
return nil, false
|
|
}
|
|
value, _ := date.Time()
|
|
return &value, true
|
|
}
|
|
|
|
func hasGeneralIssuanceFilter(c *gin.Context) bool {
|
|
for _, name := range []string{"platform_name", "workspace", "valid_from", "expires_on", "issued_on"} {
|
|
if _, exists := c.GetQuery(name); exists {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func newIssuanceSummaryView(item issuance.RecordSummary) issuanceSummaryView {
|
|
return issuanceSummaryView{
|
|
ID: item.ID,
|
|
SubjectID: item.SubjectID,
|
|
PlatformName: item.PlatformName,
|
|
Workspace: item.Workspace,
|
|
IssuanceType: item.IssuanceType,
|
|
PreviousLicenceID: item.PreviousLicenceID,
|
|
ValidFrom: item.ValidFrom.Format("2006-01-02"),
|
|
ExpiresOn: item.ExpiresOn.Format("2006-01-02"),
|
|
IssuedAt: item.IssuedAt.UTC().Format(timeLayout),
|
|
}
|
|
}
|
|
|
|
func writeLicence(c *gin.Context, content []byte, id uuid.UUID) {
|
|
filename := fmt.Sprintf("%s.licence.key", id)
|
|
c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
|
|
c.Header("X-Licence-ID", id.String())
|
|
c.Data(http.StatusOK, "application/json", content)
|
|
}
|