527 lines
14 KiB
Go
527 lines
14 KiB
Go
package helpers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/configs"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
|
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
|
|
teaconst "github.com/TeaOSLab/EdgeUser/internal/const"
|
|
"github.com/TeaOSLab/EdgeUser/internal/remotelogs"
|
|
"github.com/TeaOSLab/EdgeUser/internal/rpc"
|
|
"github.com/TeaOSLab/EdgeUser/internal/utils/portalutils"
|
|
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/login/loginutils"
|
|
"github.com/iwind/TeaGo/actions"
|
|
"github.com/iwind/TeaGo/lists"
|
|
"github.com/iwind/TeaGo/maps"
|
|
)
|
|
|
|
// 认证拦截
|
|
type userMustAuth struct {
|
|
UserId int64
|
|
module string
|
|
}
|
|
|
|
func NewUserMustAuth(module string) *userMustAuth {
|
|
return &userMustAuth{module: module}
|
|
}
|
|
|
|
func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramName string) (goNext bool) {
|
|
var action = actionPtr.Object()
|
|
|
|
// 检测注入
|
|
if !safeFilterRequest(action.Request) {
|
|
action.ResponseWriter.WriteHeader(http.StatusForbidden)
|
|
_, _ = action.ResponseWriter.Write([]byte("Denied By WAF"))
|
|
return false
|
|
}
|
|
|
|
// 安全相关
|
|
action.AddHeader("X-Frame-Options", "SAMEORIGIN")
|
|
action.AddHeader("Content-Security-Policy", "default-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:;")
|
|
|
|
var session = action.Session()
|
|
var userId = session.GetInt64(teaconst.SessionUserId)
|
|
|
|
if userId <= 0 {
|
|
var errString = session.GetString("@error")
|
|
if len(errString) > 0 {
|
|
// 这里为了安全起见不打印具体错误信息
|
|
action.WriteString("read session failed, please contact the system administrator.")
|
|
return false
|
|
}
|
|
this.login(action)
|
|
return false
|
|
}
|
|
|
|
// 检查指纹
|
|
// 将来用户可以自行设置
|
|
/**var clientFingerprint = session.GetString("@fingerprint")
|
|
if len(clientFingerprint) > 0 && clientFingerprint != loginutils.CalculateClientFingerprint(action) {
|
|
loginutils.UnsetCookie(action)
|
|
session.Delete()
|
|
|
|
this.login(action)
|
|
return false
|
|
}**/
|
|
|
|
registerConfig, err := configloaders.LoadRegisterConfig()
|
|
if err != nil {
|
|
remotelogs.Error("LOAD_REGISTER_CONFIG", err.Error())
|
|
return false
|
|
}
|
|
|
|
if registerConfig == nil {
|
|
action.WriteString("invalid 'registerConfig'")
|
|
return false
|
|
}
|
|
|
|
if registerConfig.CheckClientRegion {
|
|
var oldClientIP = session.GetString("@ip")
|
|
var currentClientIP = loginutils.RemoteIP(action)
|
|
if len(oldClientIP) > 0 && len(currentClientIP) > 0 && oldClientIP != currentClientIP {
|
|
var oldRegion = loginutils.LookupIPRegion(oldClientIP)
|
|
var newRegion = loginutils.LookupIPRegion(currentClientIP)
|
|
if newRegion != oldRegion {
|
|
loginutils.UnsetCookie(action)
|
|
session.Delete()
|
|
|
|
this.login(action)
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
this.UserId = userId
|
|
action.Context.Set("userId", this.UserId)
|
|
|
|
userIsOn, fullname, isVerified, isIndividualIdentified, isEnterpriseIdentified, mobileIsVerified, userLang, err := this.findUserInfo(userId)
|
|
if err != nil {
|
|
if rpc.IsConnError(err) {
|
|
remotelogs.Debug("USER_INFO", err.Error())
|
|
} else {
|
|
remotelogs.Error("USER_INFO", err.Error())
|
|
}
|
|
action.RedirectURL("/logout")
|
|
return false
|
|
}
|
|
if !userIsOn {
|
|
action.ResponseWriter.WriteHeader(http.StatusForbidden)
|
|
action.WriteString("You account has been disabled by administrator, please contact with your administrator.")
|
|
return false
|
|
}
|
|
|
|
// 强制绑定手机号
|
|
if !mobileIsVerified && registerConfig.MobileVerification.Force {
|
|
var skipPackages = []string{"settings", "docs", "logout", "files", "csrf", "api", "email", "ui", "tickets", "index", "messages"}
|
|
var skip bool
|
|
for _, packageName := range skipPackages {
|
|
if strings.Contains(action.Spec.PkgPath, "/actions/default/"+packageName) {
|
|
skip = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !skip {
|
|
action.RedirectURL("/settings/mobile-verify")
|
|
return false
|
|
}
|
|
}
|
|
|
|
var isIdentified = isIndividualIdentified || isEnterpriseIdentified
|
|
|
|
action.Context.Set("isIndividualIdentified", isIndividualIdentified)
|
|
action.Context.Set("isEnterpriseIdentified", isEnterpriseIdentified)
|
|
action.Context.Set("isIdentified", isIdentified)
|
|
action.Context.Set("isVerified", isVerified)
|
|
|
|
if len(userLang) == 0 {
|
|
userLang = langs.ParseLangFromAction(action)
|
|
}
|
|
action.Data["teaLang"] = userLang
|
|
|
|
// 以下是Get专有
|
|
if action.Request.Method != http.MethodGet {
|
|
return true
|
|
}
|
|
|
|
config, err := configloaders.LoadUIConfig()
|
|
if err != nil {
|
|
action.WriteString(err.Error())
|
|
return false
|
|
}
|
|
|
|
// 初始化内置方法
|
|
action.ViewFunc("teaTitle", func() string {
|
|
return action.Data["teaTitle"].(string)
|
|
})
|
|
|
|
// 注册 jsonEncode 函数
|
|
action.ViewFunc("jsonEncode", func(v interface{}) string {
|
|
if v == nil {
|
|
return "null"
|
|
}
|
|
data, err := json.Marshal(v)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
})
|
|
|
|
action.Data["teaShowVersion"] = config.ShowVersion
|
|
action.Data["teaTitle"] = config.UserSystemName
|
|
action.Data["teaName"] = config.ProductName
|
|
action.Data["teaFaviconFileId"] = config.FaviconFileId
|
|
action.Data["teaLogoFileId"] = config.LogoFileId
|
|
|
|
action.Data["teaUsername"] = fullname
|
|
action.Data["teaUserAvatar"] = ""
|
|
|
|
if !action.Data.Has("teaMenu") {
|
|
action.Data["teaMenu"] = ""
|
|
}
|
|
action.Data["teaModules"] = this.modules(userId, isVerified, isIdentified)
|
|
action.Data["teaSubMenus"] = []map[string]interface{}{}
|
|
action.Data["teaTabbar"] = []map[string]interface{}{}
|
|
|
|
// 注入品牌配置
|
|
brandConfig := configs.GetBrandConfig()
|
|
action.Data["brandConfig"] = brandConfig.ToMap()
|
|
if len(config.Version) == 0 {
|
|
action.Data["teaVersion"] = teaconst.Version
|
|
} else {
|
|
action.Data["teaVersion"] = config.Version
|
|
}
|
|
action.Data["teaShowPageFooter"] = config.ShowPageFooter
|
|
action.Data["teaPageFooterHTML"] = config.PageFooterHTML
|
|
action.Data["teaThemeBackgroundColor"] = config.Theme.BackgroundColor
|
|
action.Data["teaShowIndexPage"] = portalutils.HasPortalIndex()
|
|
action.Data["teaIsSuper"] = false
|
|
action.Data["teaDemoEnabled"] = false
|
|
if !action.Data.Has("teaSubMenu") {
|
|
action.Data["teaSubMenu"] = ""
|
|
}
|
|
|
|
// 菜单
|
|
action.Data["firstMenuItem"] = ""
|
|
|
|
// 未读消息数
|
|
action.Data["teaBadge"] = 0
|
|
|
|
// 调用Init
|
|
initMethod := reflect.ValueOf(actionPtr).MethodByName("Init")
|
|
if initMethod.IsValid() {
|
|
initMethod.Call([]reflect.Value{})
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// 菜单配置
|
|
func (this *userMustAuth) modules(userId int64, isVerified bool, isIdentified bool) []maps.Map {
|
|
// 开通的功能
|
|
var featureCodes = []string{}
|
|
rpcClient, err := rpc.SharedRPC()
|
|
if err == nil {
|
|
userFeatureResp, err := rpcClient.UserRPC().FindUserFeatures(rpcClient.Context(userId), &pb.FindUserFeaturesRequest{UserId: userId})
|
|
if err == nil {
|
|
for _, feature := range userFeatureResp.Features {
|
|
featureCodes = append(featureCodes, feature.Code)
|
|
}
|
|
}
|
|
}
|
|
|
|
registerConfig, err := configloaders.LoadRegisterConfig()
|
|
if err != nil {
|
|
remotelogs.Error("LOAD_REGISTER_CONFIG", err.Error())
|
|
return []maps.Map{}
|
|
}
|
|
var requireVerification = false
|
|
var requireIdentity = false
|
|
if registerConfig != nil {
|
|
requireVerification = registerConfig.RequireVerification
|
|
requireIdentity = registerConfig.RequireIdentity
|
|
}
|
|
|
|
if (requireVerification && !isVerified) || (requireIdentity && !isIdentified) {
|
|
featureCodes = []string{}
|
|
}
|
|
|
|
config, _ := configloaders.LoadUIConfig()
|
|
|
|
var allMaps = []maps.Map{
|
|
{
|
|
"code": "dashboard",
|
|
"name": "概览",
|
|
"icon": "dashboard",
|
|
"isOn": true,
|
|
},
|
|
}
|
|
|
|
var dashboardSubItems = []maps.Map{}
|
|
|
|
priceConfig, _ := configloaders.LoadCacheableUserPriceConfig()
|
|
|
|
if (!requireVerification || isVerified) && (!requireIdentity || isIdentified) {
|
|
allMaps = []maps.Map{
|
|
{
|
|
"code": "dashboard",
|
|
"name": "概览",
|
|
"icon": "dashboard",
|
|
"isOn": true,
|
|
"subItems": dashboardSubItems,
|
|
},
|
|
{
|
|
"code": "servers",
|
|
"name": "CDN加速",
|
|
"icon": "clone outline",
|
|
"isOn": registerConfig != nil && registerConfig.CDNIsOn,
|
|
|
|
"subItems": []maps.Map{
|
|
{
|
|
"name": "刷新预热",
|
|
"code": "cache",
|
|
"url": "/servers/cache",
|
|
},
|
|
{
|
|
"name": "证书管理",
|
|
"code": "certs",
|
|
"url": "/servers/certs",
|
|
},
|
|
{
|
|
"name": "证书申请",
|
|
"code": "acme",
|
|
"url": "/servers/certs/acme",
|
|
"isOn": lists.ContainsString(featureCodes, userconfigs.UserFeatureCodeServerACME),
|
|
},
|
|
{
|
|
"name": "用量统计",
|
|
"url": "/servers/traffic-stats",
|
|
"code": "trafficStat",
|
|
"isOn": config != nil && config.ShowBandwidthCharts,
|
|
},
|
|
{
|
|
"name": "计费方式",
|
|
"code": "fee",
|
|
"url": "/servers/fee",
|
|
"isOn": priceConfig != nil && priceConfig.IsOn && !priceConfig.EnablePlans && priceConfig.UserUI.ShowPrices,
|
|
},
|
|
{
|
|
"name": "流量包",
|
|
"code": "trafficPackage",
|
|
"url": "/servers/packages",
|
|
"isOn": priceConfig != nil && priceConfig.IsOn && !priceConfig.EnablePlans && priceConfig.EnableTrafficPackages && priceConfig.ShowTrafficPackages,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"code": "dns",
|
|
"name": "域名解析",
|
|
"icon": "globe",
|
|
"isOn": false,
|
|
//"subItems": []maps.Map{
|
|
// {
|
|
// "name": "域名管理",
|
|
// "code": "dns",
|
|
// "url": "/dns/providers",
|
|
// },
|
|
//},
|
|
},
|
|
{
|
|
"code": "lb",
|
|
"name": "负载均衡",
|
|
"icon": "paper plane",
|
|
"isOn": registerConfig != nil && registerConfig.CDNIsOn && (lists.ContainsString(featureCodes, "server.tcp") || lists.ContainsString(featureCodes, "server.udp")),
|
|
},
|
|
{
|
|
"code": "waf",
|
|
"name": "WAF安全",
|
|
"icon": "magnet",
|
|
"isOn": registerConfig != nil && registerConfig.CDNIsOn && lists.ContainsString(featureCodes, "server.waf"),
|
|
"subItems": []maps.Map{
|
|
{
|
|
"name": "拦截日志",
|
|
"code": "wafLogs",
|
|
"url": "/waf/logs",
|
|
},
|
|
{
|
|
"name": "拦截IP",
|
|
"code": "iplist",
|
|
"url": "/waf/iplists",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"code": "plans",
|
|
"name": "套餐管理",
|
|
"icon": "puzzle piece",
|
|
"isOn": registerConfig != nil &&
|
|
registerConfig.CDNIsOn &&
|
|
lists.ContainsString(featureCodes, "plan") &&
|
|
priceConfig != nil &&
|
|
priceConfig.IsOn &&
|
|
priceConfig.EnablePlans &&
|
|
priceConfig.ShowPlansInUserSystem,
|
|
},
|
|
{
|
|
"code": "anti-ddos",
|
|
"name": "DDoS高防",
|
|
"icon": "shield",
|
|
"isOn": registerConfig != nil && registerConfig.ADIsOn,
|
|
"subItems": []maps.Map{
|
|
{
|
|
"name": "实例列表",
|
|
"code": "instance",
|
|
"url": "/anti-ddos/instances",
|
|
},
|
|
{
|
|
"name": "购买实例",
|
|
"code": "package",
|
|
"url": "/anti-ddos/packages",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"code": "ns",
|
|
"name": "智能DNS",
|
|
"icon": "globe",
|
|
"isOn": registerConfig != nil && registerConfig.NSIsOn,
|
|
"subItems": []maps.Map{
|
|
{
|
|
"name": "我的域名",
|
|
"code": "domain",
|
|
"url": "/ns/domains",
|
|
},
|
|
{
|
|
"name": "域名分组",
|
|
"code": "domainGroup",
|
|
"url": "/ns/domains/groups",
|
|
},
|
|
{
|
|
"name": "批量操作",
|
|
"code": "domainBatch",
|
|
"url": "/ns/domains/batch",
|
|
},
|
|
{
|
|
"name": "线路管理",
|
|
"code": "route",
|
|
"url": "/ns/routes",
|
|
},
|
|
{
|
|
"name": "套餐",
|
|
"code": "plan",
|
|
"url": "/ns/plans",
|
|
},
|
|
/**{
|
|
"name": "访问日志",
|
|
"code": "accessLog",
|
|
"url": "/ns/clusters/accessLogs",
|
|
},**/
|
|
},
|
|
},
|
|
{
|
|
"code": "finance",
|
|
"name": "财务管理",
|
|
"icon": "yen sign",
|
|
"isOn": true,
|
|
"subItems": []maps.Map{
|
|
{
|
|
"name": "费用账单",
|
|
"code": "bills",
|
|
"url": "/finance/bills",
|
|
},
|
|
{
|
|
"name": "收支明细",
|
|
"code": "logs",
|
|
"url": "/finance/logs",
|
|
},
|
|
{
|
|
"name": "充值",
|
|
"code": "charge",
|
|
"url": "/finance/charge",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"code": "tickets",
|
|
"name": "工单系统",
|
|
"icon": "ticket",
|
|
"isOn": true,
|
|
},
|
|
{
|
|
"code": "acl",
|
|
"name": "访问控制",
|
|
"icon": "address book",
|
|
"isOn": true,
|
|
},
|
|
/**{
|
|
"code": "tickets",
|
|
"name": "工单",
|
|
"icon": "question circle outline",
|
|
},**/
|
|
}
|
|
}
|
|
|
|
var result = []maps.Map{}
|
|
for _, m := range allMaps {
|
|
// 财务管理
|
|
if m.GetString("code") == "finance" {
|
|
if config != nil && !config.ShowFinance {
|
|
continue
|
|
}
|
|
}
|
|
|
|
// 域名解析
|
|
if !m.GetBool("isOn") {
|
|
continue
|
|
}
|
|
|
|
result = append(result, m)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// 跳转到登录页
|
|
func (this *userMustAuth) login(action *actions.ActionObject) {
|
|
action.RedirectURL("/login?from=" + url.QueryEscape(action.Request.RequestURI))
|
|
}
|
|
|
|
// 查找用户名称
|
|
func (this *userMustAuth) findUserInfo(userId int64) (isOn bool, fullname string, isVerified bool, isIndividualIdentified bool, isEnterpriseIdentified bool, mobileIsVerified bool, lang string, err error) {
|
|
rpcClient, err := rpc.SharedRPC()
|
|
if err != nil {
|
|
return false, "", false, false, false, false, "", err
|
|
}
|
|
resp, err := rpcClient.UserRPC().FindEnabledUser(rpcClient.Context(userId), &pb.FindEnabledUserRequest{UserId: userId})
|
|
if err != nil {
|
|
return false, "", false, false, false, false, "", err
|
|
}
|
|
var user = resp.User
|
|
if user == nil {
|
|
return false, "", false, false, false, false, "", errors.New("can not find user")
|
|
}
|
|
|
|
if !user.IsOn {
|
|
return false, "", false, false, false, false, "", nil
|
|
}
|
|
|
|
fullname = user.Fullname
|
|
if !user.IsVerified {
|
|
fullname += "(未审核)"
|
|
} else {
|
|
if user.IsRejected {
|
|
fullname += "(已被拒绝)"
|
|
}
|
|
}
|
|
|
|
return true, fullname, user.IsVerified, user.IsIndividualIdentified, user.IsEnterpriseIdentified, len(user.VerifiedMobile) > 0, user.Lang, nil
|
|
}
|