backend_mode_guard.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package middleware
  2. import (
  3. "strings"
  4. "github.com/Wei-Shaw/sub2api/internal/pkg/response"
  5. "github.com/Wei-Shaw/sub2api/internal/service"
  6. "github.com/gin-gonic/gin"
  7. )
  8. // BackendModeUserGuard blocks non-admin users from accessing user routes when backend mode is enabled.
  9. // Must be placed AFTER JWT auth middleware so that the user role is available in context.
  10. func BackendModeUserGuard(settingService *service.SettingService) gin.HandlerFunc {
  11. return func(c *gin.Context) {
  12. if settingService == nil || !settingService.IsBackendModeEnabled(c.Request.Context()) {
  13. c.Next()
  14. return
  15. }
  16. role, _ := GetUserRoleFromContext(c)
  17. if service.IsAdminRole(role) {
  18. c.Next()
  19. return
  20. }
  21. response.Forbidden(c, "Backend mode is active. User self-service is disabled.")
  22. c.Abort()
  23. }
  24. }
  25. func backendModeAllowsAuthPath(path string) bool {
  26. path = strings.ToLower(strings.TrimSpace(path))
  27. for _, suffix := range []string{
  28. "/auth/login",
  29. "/auth/login/2fa",
  30. "/auth/passkey/login/begin",
  31. "/auth/passkey/login/finish",
  32. "/auth/logout",
  33. "/auth/refresh",
  34. } {
  35. if strings.HasSuffix(path, suffix) {
  36. return true
  37. }
  38. }
  39. for _, suffix := range []string{
  40. "/auth/oauth/linuxdo/callback",
  41. "/auth/oauth/wechat/callback",
  42. "/auth/oauth/wechat/payment/callback",
  43. "/auth/oauth/oidc/callback",
  44. "/auth/oauth/github/callback",
  45. "/auth/oauth/google/callback",
  46. "/auth/oauth/dingtalk/callback",
  47. "/auth/oauth/github/complete-registration",
  48. "/auth/oauth/google/complete-registration",
  49. "/auth/oauth/linuxdo/complete-registration",
  50. "/auth/oauth/wechat/complete-registration",
  51. "/auth/oauth/oidc/complete-registration",
  52. "/auth/oauth/dingtalk/complete-registration",
  53. "/auth/oauth/linuxdo/create-account",
  54. "/auth/oauth/wechat/create-account",
  55. "/auth/oauth/oidc/create-account",
  56. "/auth/oauth/dingtalk/create-account",
  57. "/auth/oauth/linuxdo/bind-login",
  58. "/auth/oauth/wechat/bind-login",
  59. "/auth/oauth/oidc/bind-login",
  60. "/auth/oauth/dingtalk/bind-login",
  61. } {
  62. if strings.HasSuffix(path, suffix) {
  63. return true
  64. }
  65. }
  66. return strings.Contains(path, "/auth/oauth/pending/")
  67. }
  68. // BackendModeAuthGuard selectively blocks auth endpoints when backend mode is enabled.
  69. // Allows the minimal auth surface admins still need in backend mode, including
  70. // OAuth callbacks and pending continuations. Handler-level backend mode checks
  71. // still enforce admin-only login and forbid self-service registration.
  72. func BackendModeAuthGuard(settingService *service.SettingService) gin.HandlerFunc {
  73. return func(c *gin.Context) {
  74. if settingService == nil || !settingService.IsBackendModeEnabled(c.Request.Context()) {
  75. c.Next()
  76. return
  77. }
  78. if backendModeAllowsAuthPath(c.Request.URL.Path) {
  79. c.Next()
  80. return
  81. }
  82. response.Forbidden(c, "Backend mode is active. Registration and self-service auth flows are disabled.")
  83. c.Abort()
  84. }
  85. }