Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
|
|
@ -0,0 +1,108 @@
|
|||
package tenant
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config/constants"
|
||||
tenantdto "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/dto/tenant"
|
||||
tenantservice "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/tenant"
|
||||
tenantusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/tenant"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httperror"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpresponse"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpvalidation"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
|
||||
)
|
||||
|
||||
// CreateHandler handles tenant creation HTTP requests
|
||||
type CreateHandler struct {
|
||||
service tenantservice.CreateTenantService
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// ProvideCreateHandler creates a new CreateHandler
|
||||
func ProvideCreateHandler(service tenantservice.CreateTenantService, logger *zap.Logger) *CreateHandler {
|
||||
return &CreateHandler{
|
||||
service: service,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle handles the HTTP request for creating a tenant
|
||||
// Note: This endpoint does NOT require tenant middleware since we're creating a tenant
|
||||
// Security: CWE-20, CWE-79, CWE-117 - Comprehensive input validation and sanitization
|
||||
func (h *CreateHandler) Handle(w http.ResponseWriter, r *http.Request) {
|
||||
// CWE-436: Enforce strict Content-Type validation
|
||||
if err := httpvalidation.ValidateJSONContentTypeStrict(r); err != nil {
|
||||
h.logger.Warn("invalid content type", zap.String("content_type", r.Header.Get("Content-Type")))
|
||||
httperror.ProblemBadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse request body
|
||||
var req tenantdto.CreateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
h.logger.Warn("invalid request body", zap.Error(err))
|
||||
httperror.ProblemBadRequest(w, "Invalid request body format. Please check your JSON syntax.")
|
||||
return
|
||||
}
|
||||
|
||||
// CWE-20: Comprehensive input validation
|
||||
if err := req.Validate(); err != nil {
|
||||
h.logger.Warn("tenant creation validation failed", zap.Error(err))
|
||||
httperror.ProblemBadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Extract user context for logging
|
||||
userID := "unknown"
|
||||
if uid := r.Context().Value(constants.SessionUserID); uid != nil {
|
||||
if userIDUint, ok := uid.(uint64); ok {
|
||||
userID = fmt.Sprintf("%d", userIDUint)
|
||||
}
|
||||
}
|
||||
|
||||
// CWE-532: Safe logging with hashed PII
|
||||
h.logger.Info("creating tenant",
|
||||
zap.String("user_id", userID),
|
||||
logger.TenantSlugHash(req.Slug))
|
||||
|
||||
// Map DTO to use case input
|
||||
input := &tenantusecase.CreateTenantInput{
|
||||
Name: req.Name,
|
||||
Slug: req.Slug,
|
||||
}
|
||||
|
||||
// Call service
|
||||
output, err := h.service.CreateTenant(r.Context(), input)
|
||||
if err != nil {
|
||||
// CWE-532: Log with safe identifiers
|
||||
h.logger.Error("failed to create tenant",
|
||||
zap.Error(err),
|
||||
zap.String("user_id", userID),
|
||||
logger.TenantSlugHash(req.Slug))
|
||||
httperror.ProblemInternalServerError(w, "Failed to create tenant. Please try again later.")
|
||||
return
|
||||
}
|
||||
|
||||
// CWE-532: Log successful creation
|
||||
h.logger.Info("tenant created successfully",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("tenant_id", output.ID),
|
||||
logger.TenantSlugHash(output.Slug))
|
||||
|
||||
// Map to response DTO
|
||||
response := tenantdto.CreateResponse{
|
||||
ID: output.ID,
|
||||
Name: output.Name,
|
||||
Slug: output.Slug,
|
||||
Status: output.Status,
|
||||
CreatedAt: output.CreatedAt,
|
||||
}
|
||||
|
||||
// Write response
|
||||
httpresponse.Created(w, response)
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package tenant
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
tenantdto "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/dto/tenant"
|
||||
tenantservice "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/tenant"
|
||||
tenantusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/tenant"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httperror"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpresponse"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/validation"
|
||||
)
|
||||
|
||||
// GetHandler handles getting a tenant by ID or slug
|
||||
type GetHandler struct {
|
||||
service tenantservice.GetTenantService
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// ProvideGetHandler creates a new GetHandler
|
||||
func ProvideGetHandler(service tenantservice.GetTenantService, logger *zap.Logger) *GetHandler {
|
||||
return &GetHandler{
|
||||
service: service,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleByID handles the HTTP request for getting a tenant by ID
|
||||
// Security: CWE-20 - Path parameter validation
|
||||
func (h *GetHandler) HandleByID(w http.ResponseWriter, r *http.Request) {
|
||||
// CWE-20: Validate UUID path parameter
|
||||
id, err := validation.ValidatePathUUID(r, "id")
|
||||
if err != nil {
|
||||
h.logger.Warn("invalid tenant ID", zap.Error(err))
|
||||
httperror.ProblemBadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Call service
|
||||
input := &tenantusecase.GetTenantInput{ID: id}
|
||||
output, err := h.service.GetTenant(r.Context(), input)
|
||||
if err != nil {
|
||||
// CWE-532: Don't log full error details to prevent information leakage
|
||||
h.logger.Debug("failed to get tenant",
|
||||
zap.String("tenant_id", id),
|
||||
zap.Error(err))
|
||||
httperror.ProblemNotFound(w, "The requested tenant could not be found.")
|
||||
return
|
||||
}
|
||||
|
||||
// CWE-532: Safe logging
|
||||
h.logger.Info("tenant retrieved",
|
||||
zap.String("tenant_id", output.ID),
|
||||
logger.TenantSlugHash(output.Slug))
|
||||
|
||||
// Map to response DTO
|
||||
response := tenantdto.GetResponse{
|
||||
ID: output.ID,
|
||||
Name: output.Name,
|
||||
Slug: output.Slug,
|
||||
Status: output.Status,
|
||||
CreatedAt: output.CreatedAt,
|
||||
UpdatedAt: output.UpdatedAt,
|
||||
}
|
||||
|
||||
// Write response
|
||||
httpresponse.OK(w, response)
|
||||
}
|
||||
|
||||
// HandleBySlug handles the HTTP request for getting a tenant by slug
|
||||
// Security: CWE-20 - Path parameter validation
|
||||
func (h *GetHandler) HandleBySlug(w http.ResponseWriter, r *http.Request) {
|
||||
// CWE-20: Validate slug path parameter
|
||||
slug, err := validation.ValidatePathSlug(r, "slug")
|
||||
if err != nil {
|
||||
h.logger.Warn("invalid tenant slug", zap.Error(err))
|
||||
httperror.ProblemBadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Call service
|
||||
input := &tenantusecase.GetTenantBySlugInput{Slug: slug}
|
||||
output, err := h.service.GetTenantBySlug(r.Context(), input)
|
||||
if err != nil {
|
||||
// CWE-532: Don't log full error details to prevent information leakage
|
||||
h.logger.Debug("failed to get tenant by slug",
|
||||
logger.TenantSlugHash(slug),
|
||||
zap.Error(err))
|
||||
httperror.ProblemNotFound(w, "The requested tenant could not be found.")
|
||||
return
|
||||
}
|
||||
|
||||
// CWE-532: Safe logging
|
||||
h.logger.Info("tenant retrieved by slug",
|
||||
zap.String("tenant_id", output.ID),
|
||||
logger.TenantSlugHash(output.Slug))
|
||||
|
||||
// Map to response DTO
|
||||
response := tenantdto.GetResponse{
|
||||
ID: output.ID,
|
||||
Name: output.Name,
|
||||
Slug: output.Slug,
|
||||
Status: output.Status,
|
||||
CreatedAt: output.CreatedAt,
|
||||
UpdatedAt: output.UpdatedAt,
|
||||
}
|
||||
|
||||
// Write response
|
||||
httpresponse.OK(w, response)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue