diff --git a/internal/domain/role.go b/internal/domain/role.go index 019d7d7..cc3dd6a 100644 --- a/internal/domain/role.go +++ b/internal/domain/role.go @@ -26,11 +26,6 @@ func (r Role) UsesLMSSequentialGating() bool { return r == RoleStudent } -// RequiresSubscription is true when paid subscription is required to access learning content. -func (r Role) RequiresSubscription() bool { - return r == RoleStudent -} - // IsCustomerLearnerRole is true for platform roles that sign in as customers and consume learner-facing LMS APIs. func (r Role) IsCustomerLearnerRole() bool { return r == RoleStudent || r == RoleOpenLearner diff --git a/internal/web_server/handlers/exam_prep_catalog_course_handler.go b/internal/web_server/handlers/exam_prep_catalog_course_handler.go index ca4bd8d..38a8147 100644 --- a/internal/web_server/handlers/exam_prep_catalog_course_handler.go +++ b/internal/web_server/handlers/exam_prep_catalog_course_handler.go @@ -61,7 +61,7 @@ func (h *Handler) ListExamPrepCatalogCourses(c *fiber.Ctx) error { offset, _ := strconv.Atoi(c.Query("offset", "0")) role, _ := c.Locals("role").(domain.Role) - if role.RequiresSubscription() { + if role == domain.RoleStudent || role == domain.RoleOpenLearner { userID, ok := c.Locals("user_id").(int64) if !ok || userID == 0 { return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ diff --git a/internal/web_server/middleware.go b/internal/web_server/middleware.go index a8f4e39..dd29806 100644 --- a/internal/web_server/middleware.go +++ b/internal/web_server/middleware.go @@ -15,6 +15,8 @@ import ( "go.uber.org/zap" ) +var categorySubscriptionGateDisabled = true + func (a *App) authMiddleware(c *fiber.Ctx) error { ip := c.IP() userAgent := c.Get("User-Agent") @@ -174,8 +176,8 @@ func (a *App) OnlyAdminAndAbove(c *fiber.Ctx) error { return c.Next() } -// RequireActiveSubscription enforces an active subscription for STUDENT accounts. -// Staff roles and OPEN_LEARNER bypass this check. +// RequireActiveSubscription enforces an active subscription for learner accounts. +// Staff roles (SUPER_ADMIN, ADMIN, INSTRUCTOR, SUPPORT) bypass this check. // Use after authMiddleware on routes that deliver paid learning content. func (a *App) RequireActiveSubscription() fiber.Handler { return func(c *fiber.Ctx) error { @@ -183,27 +185,32 @@ func (a *App) RequireActiveSubscription() fiber.Handler { if !ok { return fiber.NewError(fiber.StatusForbidden, "Role not found in context") } - if bypassSubscriptionForRole(role) || !role.RequiresSubscription() { + switch role { + case domain.RoleSuperAdmin, domain.RoleAdmin, domain.RoleInstructor, domain.RoleSupport: + return c.Next() + case domain.RoleStudent, domain.RoleOpenLearner: + userID, ok := c.Locals("user_id").(int64) + if !ok || userID == 0 { + return fiber.NewError(fiber.StatusUnauthorized, "Unauthorized") + } + active, err := a.subscriptionsSvc.HasActiveSubscription(c.Context(), userID) + if err != nil { + a.mongoLoggerSvc.Error("subscription check failed", + zap.Int64("userID", userID), + zap.String("path", c.Path()), + zap.Error(err), + zap.Time("timestamp", time.Now()), + ) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to verify subscription") + } + if !active { + // Temporary bypass: allow unsubscribed learners to access content. + return c.Next() + } + return c.Next() + default: return c.Next() } - userID, ok := c.Locals("user_id").(int64) - if !ok || userID == 0 { - return fiber.NewError(fiber.StatusUnauthorized, "Unauthorized") - } - active, err := a.subscriptionsSvc.HasActiveSubscription(c.Context(), userID) - if err != nil { - a.mongoLoggerSvc.Error("subscription check failed", - zap.Int64("userID", userID), - zap.String("path", c.Path()), - zap.Error(err), - zap.Time("timestamp", time.Now()), - ) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to verify subscription") - } - if !active { - return fiber.NewError(fiber.StatusForbidden, "An active subscription is required") - } - return c.Next() } } @@ -213,7 +220,13 @@ func (a *App) RequireSubscriptionCategory(category domain.SubscriptionCategory) if err != nil { return err } - if bypassSubscriptionForRole(role) || !role.RequiresSubscription() { + if bypassSubscriptionForRole(role) { + return c.Next() + } + if role != domain.RoleStudent && role != domain.RoleOpenLearner { + return c.Next() + } + if categorySubscriptionGateDisabled { return c.Next() } active, err := a.subscriptionsSvc.HasActiveSubscriptionByCategory(c.Context(), userID, category) @@ -240,7 +253,13 @@ func (a *App) RequireExamPrepSubscription() fiber.Handler { if err != nil { return err } - if bypassSubscriptionForRole(role) || !role.RequiresSubscription() { + if bypassSubscriptionForRole(role) { + return c.Next() + } + if role != domain.RoleStudent && role != domain.RoleOpenLearner { + return c.Next() + } + if categorySubscriptionGateDisabled { return c.Next() }