access.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package access
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "os"
  6. "path/filepath"
  7. "sync"
  8. "imuslab.com/zoraxy/mod/database"
  9. "imuslab.com/zoraxy/mod/geodb"
  10. "imuslab.com/zoraxy/mod/info/logger"
  11. "imuslab.com/zoraxy/mod/utils"
  12. )
  13. /*
  14. Access.go
  15. This module is the new version of access control system
  16. where now the blacklist / whitelist are seperated from
  17. geodb module
  18. */
  19. type Options struct {
  20. Logger logger.Logger
  21. ConfigFolder string //Path for storing config files
  22. GeoDB *geodb.Store //For resolving country code
  23. Database *database.Database //System key-value database
  24. }
  25. type AccessRule struct {
  26. ID string
  27. Name string
  28. Desc string
  29. BlacklistEnabled bool
  30. WhitelistEnabled bool
  31. /* Whitelist Blacklist Table, value is comment if supported */
  32. WhiteListCountryCode *map[string]string
  33. WhiteListIP *map[string]string
  34. BlackListContryCode *map[string]string
  35. BlackListIP *map[string]string
  36. parent *Controller
  37. }
  38. type Controller struct {
  39. GlobalAccessRule *AccessRule
  40. ProxyAccessRule *sync.Map
  41. Options *Options
  42. }
  43. // Create a new access controller to handle blacklist / whitelist
  44. func NewAccessController(options *Options) (*Controller, error) {
  45. sysdb := options.Database
  46. if sysdb == nil {
  47. return nil, errors.New("missing database access")
  48. }
  49. //Create the config folder if not exists
  50. confFolder := options.ConfigFolder
  51. if !utils.FileExists(confFolder) {
  52. err := os.MkdirAll(confFolder, 0775)
  53. if err != nil {
  54. return nil, err
  55. }
  56. }
  57. // Create the global access rule if not exists
  58. globalAccessRule := AccessRule{
  59. ID: "default",
  60. Name: "Default",
  61. Desc: "Default access rule for all HTTP proxy hosts",
  62. BlacklistEnabled: false,
  63. WhitelistEnabled: false,
  64. }
  65. defaultRuleSettingFile := filepath.Join(confFolder, "default.json")
  66. if utils.FileExists(defaultRuleSettingFile) {
  67. //Load from file
  68. defaultRuleBytes, err := os.ReadFile(defaultRuleSettingFile)
  69. if err == nil {
  70. err = json.Unmarshal(defaultRuleBytes, &globalAccessRule)
  71. if err != nil {
  72. options.Logger.PrintAndLog("Access", "Unable to parse default routing rule config file. Using default", err)
  73. }
  74. }
  75. } else {
  76. //Create one
  77. js, _ := json.MarshalIndent(globalAccessRule, "", " ")
  78. os.WriteFile(defaultRuleSettingFile, js, 0775)
  79. }
  80. //Generate a controller object
  81. thisController := Controller{
  82. GlobalAccessRule: &globalAccessRule,
  83. Options: options,
  84. }
  85. //Load all acccess rules from file
  86. configFiles, err := filepath.Glob(options.ConfigFolder + "/*.json")
  87. if err != nil {
  88. return nil, err
  89. }
  90. ProxyAccessRules := sync.Map{}
  91. for _, configFile := range configFiles {
  92. if filepath.Base(configFile) == "default.json" {
  93. //Skip this, as this was already loaded as default
  94. continue
  95. }
  96. configContent, err := os.ReadFile(configFile)
  97. if err != nil {
  98. options.Logger.PrintAndLog("Access", "Unable to load config "+filepath.Base(configFile), err)
  99. continue
  100. }
  101. //Parse the config file into AccessRule
  102. thisAccessRule := AccessRule{}
  103. err = json.Unmarshal(configContent, &thisAccessRule)
  104. if err != nil {
  105. options.Logger.PrintAndLog("Access", "Unable to parse config "+filepath.Base(configFile), err)
  106. continue
  107. }
  108. thisAccessRule.parent = &thisController
  109. ProxyAccessRules.Store(thisAccessRule.ID, &thisAccessRule)
  110. }
  111. thisController.ProxyAccessRule = &ProxyAccessRules
  112. return &thisController, nil
  113. }
  114. // Get the global access rule
  115. func (c *Controller) GetGlobalAccessRule() (*AccessRule, error) {
  116. if c.GlobalAccessRule == nil {
  117. return nil, errors.New("global access rule is not set")
  118. }
  119. return c.GlobalAccessRule, nil
  120. }
  121. // Load access rules to runtime, require rule ID
  122. func (c *Controller) GetAccessRuleByID(accessRuleID string) (*AccessRule, error) {
  123. //Load from sync.Map, should be O(1)
  124. targetRule, ok := c.ProxyAccessRule.Load(accessRuleID)
  125. if !ok {
  126. return nil, errors.New("target access rule not exists")
  127. }
  128. ar, ok := targetRule.(*AccessRule)
  129. if !ok {
  130. return nil, errors.New("assertion of access rule failed, version too old?")
  131. }
  132. return ar, nil
  133. }