Pulse/internal/api/licensing_bridge.go

375 lines
15 KiB
Go

package api
import (
"crypto/ed25519"
"net/http"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
agentshost "github.com/rcourtman/pulse-go-rewrite/pkg/agents/host"
agentsk8s "github.com/rcourtman/pulse-go-rewrite/pkg/agents/kubernetes"
pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing"
"github.com/rcourtman/pulse-go-rewrite/pkg/licensing/metering"
)
// Sentinel errors for user-friendly activation error mapping.
var (
errMalformedLicenseSentinel = pkglicensing.ErrMalformedLicense
errInvalidLicenseSentinel = pkglicensing.ErrInvalidLicense
errSignatureInvalidSentinel = pkglicensing.ErrSignatureInvalid
errExpiredLicenseSentinel = pkglicensing.ErrExpiredLicense
errNoPublicKeySentinel = pkglicensing.ErrNoPublicKey
)
type licenseServerErrorModel = pkglicensing.LicenseServerError
type licenseService = pkglicensing.Service
type licenseModel = pkglicensing.License
type licenseStatus = pkglicensing.LicenseStatus
type licensePersistence = pkglicensing.Persistence
type licenseEvaluator = pkglicensing.Evaluator
type licenseFeatureServiceResolver = pkglicensing.FeatureServiceResolver
type licenseFeatureChecker = pkglicensing.FeatureChecker
type licenseFeaturesResponse = pkglicensing.LicenseFeaturesResponse
type activateLicenseRequestModel = pkglicensing.ActivateLicenseRequest
type activateLicenseResponseModel = pkglicensing.ActivateLicenseResponse
type activationStateModel = pkglicensing.ActivationState
type trialStartDecisionModel = pkglicensing.TrialStartDecision
type trialStartDenialReasonModel = pkglicensing.TrialStartDenialReason
type billingStoreModel = pkglicensing.BillingStore
type billingState = pkglicensing.BillingState
type subscriptionState = pkglicensing.SubscriptionState
type entitlementPayloadModel = pkglicensing.EntitlementPayload
type commercialPosturePayloadModel = pkglicensing.CommercialPosturePayload
type runtimeCapabilitiesPayloadModel = pkglicensing.RuntimeCapabilitiesPayload
type limitStatusModel = pkglicensing.LimitStatus
type upgradeReasonModel = pkglicensing.UpgradeReason
type entitlementUsageSnapshotModel = pkglicensing.EntitlementUsageSnapshot
type legacyConnectionCountsModel = pkglicensing.LegacyConnectionCounts
type commercialMigrationStatusModel = pkglicensing.CommercialMigrationStatus
type conversionRecorder = pkglicensing.Recorder
type conversionPipelineHealth = pkglicensing.PipelineHealth
type conversionCollectionConfig = pkglicensing.CollectionConfig
type conversionStore = pkglicensing.ConversionStore
type conversionEvent = pkglicensing.ConversionEvent
type conversionHealthStatus = pkglicensing.HealthStatus
type conversionCollectionConfigSnapshot = pkglicensing.CollectionConfigSnapshot
type trialActivationClaimsModel = pkglicensing.TrialActivationClaims
type purchaseReturnClaimsModel = pkglicensing.PurchaseReturnClaims
type entitlementLeaseClaimsModel = pkglicensing.EntitlementLeaseClaims
type licenseTier = pkglicensing.Tier
type checkoutPortalHandoffRequestModel = pkglicensing.CheckoutPortalHandoffRequest
type checkoutPortalHandoffResponseModel = pkglicensing.CheckoutPortalHandoffResponse
const (
featureMultiTenantKey = pkglicensing.FeatureMultiTenant
featureAgentProfilesValue = pkglicensing.FeatureAgentProfiles
featureAIPatrolValue = pkglicensing.FeatureAIPatrol
featureAIAutoFixValue = pkglicensing.FeatureAIAutoFix
featureAuditLoggingValue = pkglicensing.FeatureAuditLogging
featureRBACValue = pkglicensing.FeatureRBAC
featureAdvancedReportingValue = pkglicensing.FeatureAdvancedReporting
featureLongTermMetricsValue = pkglicensing.FeatureLongTermMetrics
featureDemoFixturesValue = pkglicensing.FeatureDemoFixtures
maxMonitoredSystemsLicenseGateKey = pkglicensing.MaxMonitoredSystemsLicenseGateKey
maxUsersLicenseGateKey = pkglicensing.MaxUsersLicenseGateKey
subscriptionStateActiveValue = pkglicensing.SubStateActive
subscriptionStateExpiredValue = pkglicensing.SubStateExpired
subscriptionStateGraceValue = pkglicensing.SubStateGrace
subscriptionStateCanceledValue = pkglicensing.SubStateCanceled
subscriptionStateTrialValue = pkglicensing.SubStateTrial
activationKeyPrefixValue = pkglicensing.ActivationKeyPrefix
// Conversion event type constants for backend-emitted events.
conversionEventTrialStarted = pkglicensing.EventTrialStarted
conversionEventLicenseActivated = pkglicensing.EventLicenseActivated
conversionEventLicenseActivationFailed = pkglicensing.EventLicenseActivationFailed
conversionEventCheckoutStarted = pkglicensing.EventCheckoutStarted
conversionEventCheckoutCompleted = pkglicensing.EventCheckoutCompleted
conversionEventLimitBlocked = pkglicensing.EventLimitBlocked
)
func newLicenseService() *licenseService {
return pkglicensing.NewService()
}
func hasMultiTenantLicense(service *licenseService) bool {
return service != nil && service.HasFeature(featureMultiTenantKey)
}
func upgradeURLForFeatureFromLicensing(feature string) string {
return pkglicensing.UpgradeURLForFeature(feature)
}
func proTrialSignupURLFromLicensing(override string) string {
return pkglicensing.ResolveProTrialSignupURL(override)
}
func pulseAccountPortalURLFromLicensing(override string) string {
return pkglicensing.ResolvePulseAccountPortalURL(override)
}
func defaultBillingStateFromLicensing() *billingState {
return pkglicensing.DefaultBillingState()
}
func normalizeBillingStateFromLicensing(state *billingState) *billingState {
return pkglicensing.NormalizeBillingState(state)
}
func cloneCommercialMigrationStatusFromLicensing(state *commercialMigrationStatusModel) *commercialMigrationStatusModel {
return pkglicensing.CloneCommercialMigrationStatus(state)
}
func classifyLegacyExchangeErrorFromLicensing(err error) *commercialMigrationStatusModel {
return pkglicensing.ClassifyLegacyExchangeError(err)
}
func isValidBillingSubscriptionStateFromLicensing(state subscriptionState) bool {
return pkglicensing.IsValidBillingSubscriptionState(state)
}
func cloudCapabilitiesFromLicensing() []string {
return pkglicensing.DeriveCapabilitiesFromTier(pkglicensing.TierCloud, nil)
}
func defaultTrialDurationFromLicensing() time.Duration {
return pkglicensing.DefaultTrialDuration
}
func buildTrialBillingStateWithPlanFromLicensing(now time.Time, capabilities []string, planVersion string, duration time.Duration) *billingState {
return pkglicensing.BuildTrialBillingStateWithPlan(now, capabilities, planVersion, duration)
}
func evaluateTrialStartEligibilityFromLicensing(hasActiveLicense bool, existing *billingState) trialStartDecisionModel {
return pkglicensing.EvaluateTrialStartEligibility(hasActiveLicense, existing)
}
func trialStartErrorFromLicensing(reason trialStartDenialReasonModel) (code, message string, includeOrgID bool) {
return pkglicensing.TrialStartError(reason)
}
func newLicensePersistenceFromLicensing(configDir string) (*licensePersistence, error) {
return pkglicensing.NewPersistence(configDir)
}
func newLicenseServerClientFromLicensing(baseURL string) *pkglicensing.LicenseServerClient {
return pkglicensing.NewLicenseServerClient(baseURL)
}
func isLicenseValidationDevModeFromLicensing() bool {
return pkglicensing.IsLicenseValidationDevMode()
}
func newLicenseEvaluatorForBillingStoreFromLicensing(store billingStoreModel, orgID string, cacheTTL time.Duration, expectedInstanceHost string) *licenseEvaluator {
return pkglicensing.NewEvaluator(
pkglicensing.NewDatabaseSource(store, orgID, cacheTTL).WithExpectedInstanceHost(expectedInstanceHost),
)
}
func maxUsersLimitFromLicensing(lic *licenseModel) int {
return pkglicensing.MaxUsersLimitFromLicense(lic)
}
func exceedsUserLimitFromLicensing(current, additions, limit int) bool {
return pkglicensing.ExceedsUserLimit(current, additions, limit)
}
func userLimitExceededMessageFromLicensing(current, limit int) string {
return pkglicensing.UserLimitExceededMessage(current, limit)
}
func monitoredSystemLimitExceededMessageFromLicensing(current, limit int) string {
return pkglicensing.MonitoredSystemLimitExceededMessage(current, limit)
}
func hostReportTargetsExistingHostFromLicensing(snapshot models.StateSnapshot, report agentshost.Report, tokenID string) bool {
return pkglicensing.HostReportTargetsExistingHost(snapshot, report, tokenID)
}
func dockerReportTargetsExistingHostFromLicensing(snapshot models.StateSnapshot, report agentsdocker.Report, tokenID string) bool {
return pkglicensing.DockerReportTargetsExistingHost(snapshot, report, tokenID)
}
func kubernetesReportTargetsExistingClusterFromLicensing(snapshot models.StateSnapshot, report agentsk8s.Report, tokenID string) bool {
return pkglicensing.KubernetesReportTargetsExistingCluster(snapshot, report, tokenID)
}
func kubernetesReportIdentifierFromLicensing(report agentsk8s.Report) string {
return pkglicensing.KubernetesReportIdentifier(report)
}
func hostReportTargetsExistingHostsFromLicensing(hosts []models.Host, report agentshost.Report, tokenID string) bool {
return pkglicensing.HostReportTargetsExistingHosts(hosts, report, tokenID)
}
func mapStripeSubscriptionStatusToStateFromLicensing(status string) subscriptionState {
return pkglicensing.MapStripeSubscriptionStatusToState(status)
}
func shouldGrantPaidCapabilitiesFromLicensing(state subscriptionState) bool {
return pkglicensing.ShouldGrantPaidCapabilities(state)
}
func deriveStripePlanVersionFromLicensing(metadata map[string]string, priceID string) string {
return pkglicensing.DeriveStripePlanVersion(metadata, priceID)
}
func limitsForCloudPlanFromLicensing(planVersion string) (map[string]int64, bool) {
return pkglicensing.LimitsForCloudPlan(planVersion)
}
func buildEntitlementPayloadFromLicensing(status *licenseStatus, subscriptionState string) entitlementPayloadModel {
return pkglicensing.BuildEntitlementPayload(status, subscriptionState)
}
func buildRuntimeCapabilitiesPayloadFromLicensing(
status *licenseStatus,
subscriptionState string,
) runtimeCapabilitiesPayloadModel {
return pkglicensing.BuildRuntimeCapabilitiesPayload(status, subscriptionState)
}
func buildCommercialPosturePayloadFromLicensing(
status *licenseStatus,
subscriptionState string,
) commercialPosturePayloadModel {
return pkglicensing.BuildCommercialPosturePayload(status, subscriptionState)
}
func buildFeatureMapFromLicensing(service *licenseService) map[string]bool {
return pkglicensing.BuildFeatureMap(service, nil)
}
func buildEntitlementPayloadWithUsageFromLicensing(
status *licenseStatus,
subscriptionState string,
usage entitlementUsageSnapshotModel,
trialEndsAtUnix *int64,
) entitlementPayloadModel {
return pkglicensing.BuildEntitlementPayloadWithUsage(status, subscriptionState, usage, trialEndsAtUnix)
}
func buildRuntimeCapabilitiesPayloadWithUsageFromLicensing(
status *licenseStatus,
subscriptionState string,
usage entitlementUsageSnapshotModel,
) runtimeCapabilitiesPayloadModel {
return pkglicensing.BuildRuntimeCapabilitiesPayloadWithUsage(status, subscriptionState, usage)
}
func buildCommercialPosturePayloadWithUsageFromLicensing(
status *licenseStatus,
subscriptionState string,
usage entitlementUsageSnapshotModel,
trialEndsAtUnix *int64,
) commercialPosturePayloadModel {
return pkglicensing.BuildCommercialPosturePayloadWithUsage(
status,
subscriptionState,
usage,
trialEndsAtUnix,
)
}
func commercialPosturePayloadFromEntitlementPayloadFromLicensing(
payload entitlementPayloadModel,
) commercialPosturePayloadModel {
return pkglicensing.CommercialPosturePayloadFromEntitlementPayload(payload)
}
func limitStateFromLicensing(current, limit int64) string {
return pkglicensing.LimitState(current, limit)
}
func newCollectionConfigFromLicensing() *conversionCollectionConfig {
return pkglicensing.NewCollectionConfig()
}
func newConversionRecorderFromLicensing(store *conversionStore) *conversionRecorder {
return pkglicensing.NewRecorderFromWindowedAggregator(metering.NewWindowedAggregator(), store)
}
func newConversionPipelineHealthFromLicensing() *conversionPipelineHealth {
return pkglicensing.NewPipelineHealth()
}
func conversionValidationReasonFromLicensing(err error) string {
return pkglicensing.ConversionValidationReason(err)
}
func parseOptionalTimeParamFromLicensing(raw string, defaultValue time.Time) (time.Time, error) {
return pkglicensing.ParseOptionalTimeParam(raw, defaultValue)
}
func trialActivationPublicKeyFromLicensing() (ed25519.PublicKey, error) {
return pkglicensing.TrialActivationPublicKey()
}
func verifyTrialActivationTokenFromLicensing(token string, key ed25519.PublicKey, expectedInstanceHost string, now time.Time) (*trialActivationClaimsModel, error) {
return pkglicensing.VerifyTrialActivationToken(token, key, expectedInstanceHost, now)
}
func signPurchaseReturnTokenFromLicensing(signingKey []byte, claims purchaseReturnClaimsModel) (string, error) {
return pkglicensing.SignPurchaseReturnToken(signingKey, claims)
}
func verifyPurchaseReturnTokenFromLicensing(token string, signingKey []byte, expectedInstanceHost string, now time.Time) (*purchaseReturnClaimsModel, error) {
return pkglicensing.VerifyPurchaseReturnToken(token, signingKey, expectedInstanceHost, now)
}
func verifyEntitlementLeaseTokenFromLicensing(token string, key ed25519.PublicKey, expectedInstanceHost string, now time.Time) (*entitlementLeaseClaimsModel, error) {
return pkglicensing.VerifyEntitlementLeaseToken(token, key, expectedInstanceHost, now)
}
func parseEntitlementLeaseTokenFromLicensing(token string, key ed25519.PublicKey, expectedInstanceHost string) (*entitlementLeaseClaimsModel, error) {
return pkglicensing.ParseEntitlementLeaseToken(token, key, expectedInstanceHost)
}
func writePaymentRequiredFromLicensing(w http.ResponseWriter, payload map[string]interface{}) {
pkglicensing.WritePaymentRequired(w, payload)
}
func writeLicenseRequiredFromLicensing(w http.ResponseWriter, feature, message string) {
pkglicensing.WriteLicenseRequired(w, feature, message, pkglicensing.UpgradeURLForFeature)
}
func recordConversionInvalidMetric(reason string) {
pkglicensing.GetConversionMetrics().RecordInvalid(reason)
}
func recordConversionSkippedMetric(reason string) {
pkglicensing.GetConversionMetrics().RecordSkipped(reason)
}
func recordConversionEventMetric(eventType, surface string) {
pkglicensing.GetConversionMetrics().RecordEvent(eventType, surface)
}
// licenseTierFreeValue is the canonical free-tier constant for use outside the bridge.
const licenseTierFreeValue = pkglicensing.TierFree
// quickstartCreditsTotalFromLicensing returns the total quickstart credits granted per workspace.
const quickstartCreditsTotalFromLicensing = pkglicensing.QuickstartCreditsTotal
// overflowBonusFromLicensing returns the number of bonus host slots granted by the onboarding overflow.
func overflowBonusFromLicensing(tier licenseTier, overflowGrantedAt *int64, now time.Time) int {
return pkglicensing.OverflowBonus(tier, overflowGrantedAt, now)
}
// overflowDaysRemainingFromLicensing returns the number of days remaining in the overflow window.
func overflowDaysRemainingFromLicensing(tier licenseTier, overflowGrantedAt *int64, now time.Time) int {
return pkglicensing.OverflowDaysRemaining(tier, overflowGrantedAt, now)
}
// freeHistoryDaysDefault is the fallback history days when no license service is available.
var freeHistoryDaysDefault = pkglicensing.TierHistoryDays[pkglicensing.TierFree]
func tierHistoryDaysFromLicensing(tier pkglicensing.Tier) int {
days := pkglicensing.TierHistoryDays[tier]
if days == 0 {
days = freeHistoryDaysDefault
}
return days
}