package adminctrl import ( "encoding/base32" "encoding/json" "fmt" "strings" "91porn-server/common" "91porn-server/common/constant" "91porn-server/common/googauth" "91porn-server/common/log" "91porn-server/common/stderr" "91porn-server/models/commod" "91porn-server/models/l/operatorlgmod" "91porn-server/models/v/adminmod" "91porn-server/models/v/authoritymod" "91porn-server/web/middleware/authweb" "91porn-server/web/service/adminser" "91porn-server/web/service/authorser" "github.com/gin-gonic/gin" "github.com/skip2/go-qrcode" "go.mongodb.org/mongo-driver/bson/primitive" "golang.org/x/crypto/bcrypt" ) // Login doc // @Summary 登陆 - 管理员登陆 // @Description 管理员登陆 // @Tags web-登陆 // @Accept mpfd,json // @Produce json,html // @Param name formData string true "账号" // @Param password formData string true "密码" // @Success 200 {string} json "{"msg": "操作成功"}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/login [post] func Login(c *gin.Context) { var arg struct { Name string `form:"name" json:"name" binding:"required"` Password string `form:"password" json:"password" binding:"required"` } if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, "admin Login arg error: "+err.Error()) return } adm, err := adminmod.FindOneByName(arg.Name) if err != nil { common.ServeJSON(c, stderr.ErrDbQueryError, "admin Login FindOne error: "+err.Error()) return } if adm.Name == "" { common.ServeJSON(c, stderr.AdminIsNotExist, "Login admin is not exist") return } //校验密码 if err = bcrypt.CompareHashAndPassword([]byte(adm.Password), []byte(arg.Password)); err != nil { common.ServeJSON(c, stderr.AdminIsNotExist, "Login admin password error") return } if adm.HasLocked { common.ServeJSON(c, stderr.AdminIsHasLockedErr, "Login admin haslocked") return } var qrCodePNG []byte if !adm.HasBind { url := googauth.New(adm.Secret, adm.Name+"@91porn") qrCodePNG, err = qrcode.Encode(url, qrcode.Medium, 256) if err != nil { common.ServeJSON(c, stderr.AdminIsNotExist, "Login GetQRCodePNG error: "+err.Error()) return } } common.ServeJSON(c, stderr.Success, gin.H{ "qrCodePNG": qrCodePNG, }) } // SLogin doc // @Summary 管理员sso登陆 // @Description 管理员sso登陆 // @Tags web-单点登陆 // @Accept mpfd,json // @Produce json,html // @Param name formData string true "账号" // @Param password formData string true "密码" // @Success 200 {string} json "{"msg": "管理员信息""}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/slogin [post] func SLogin(c *gin.Context) { var arg struct { Name string `form:"name" json:"name" binding:"required"` Password string `form:"password" json:"password" binding:"required"` } if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, "admin SLogin arg error: "+err.Error()) return } adm, err := adminmod.FindOneByName(arg.Name) if err != nil { common.ServeJSON(c, stderr.ErrDbQueryError, "admin SLogin FindOne error: "+err.Error()) return } if adm.Name == "" { common.ServeJSON(c, stderr.AdminIsNotExist, "SLogin admin is not exist") return } //校验密码 if err = bcrypt.CompareHashAndPassword([]byte(adm.Password), []byte(arg.Password)); err != nil { common.ServeJSON(c, stderr.AdminIsNotExist, "Login admin password error") return } if adm.HasLocked { common.ServeJSON(c, stderr.AdminIsHasLockedErr, "SLogin admin haslocked") return } // SLogin 仅允许角色名包含"客服"的账号登录,其余角色一律拒绝 if !strings.Contains(adm.Role, "客服") { common.ServeJSON(c, stderr.ErrAccessForbid, "SLogin 仅限客服角色登录") return } var cid string token, err := authweb.GenAndSaveToken(&authweb.Claims{Type: authweb.Admin, Act: adm.Name, Role: adm.Role, CID: cid}) if err != nil { common.ServeJSON(c, stderr.Failure, "SLogin admin GenerateToken error: "+err.Error()) return } _ = adminmod.Bind(arg.Name) authJSON, _ := authoritymod.FineOneByRole(adm.Role) common.ServeJSON(c, stderr.Success, gin.H{ "token": token, "authJson": authJSON.AuthJson, "actJson": authJSON.ActJson, "admin": adminmod.AdminDoc{ ID: &adm.ID, Name: &adm.Name, Role: &adm.Role, Nickname: &adm.Nickname, }, }) } // Verify doc // @Summary 管理员二步验证 // @Description 管理员二步验证 // @Tags web-登陆 // @Accept mpfd,json // @Produce json,html // @Param name formData string true "账号" // @Param otp formData string true "动态口令" // @Success 200 {string} json "{"msg": "管理员信息""}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/verify [post] func Verify(c *gin.Context) { var arg struct { Name string `form:"name" json:"name" binding:"required"` Otp string `form:"otp" json:"otp" binding:"required"` } if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, "admin Verification arg error: "+err.Error()) return } adm, err := adminmod.FindOneByName(arg.Name) if err != nil { common.ServeJSON(c, stderr.ErrDbQueryError, "admin Verification FindOne error: "+err.Error()) return } if adm.Name == "" { log.Error(fmt.Sprintf("用户[%v]不存在", arg.Name)) common.ServeJSON(c, stderr.ErrAccessForbid, "admin is not exist") return } //测试模式不使用两步验证 if gin.Mode() != gin.DebugMode { ok, err := googauth.Verify(adm.Secret, arg.Otp) if err != nil { common.ServeJSON(c, stderr.GoogleAuthFail, "admin Verification 2-step error: "+err.Error()) return } if !ok { common.ServeJSON(c, stderr.GoogleAuthFail, "admin Verification 2-step verification faild") return } } var cid string if adm.Role == "裸聊商家" { common.ServeJSON(c, stderr.GoogleAuthFail, "role faild") return } token, err := authweb.GenAndSaveToken(&authweb.Claims{Type: authweb.Admin, Act: adm.Name, Role: adm.Role, CID: cid}) if err != nil { common.ServeJSON(c, stderr.Failure, "admin GenerateToken error: "+err.Error()) return } _ = adminmod.Bind(arg.Name) authJSON, _ := authoritymod.FineOneByRole(adm.Role) common.ServeJSON(c, stderr.Success, gin.H{ "token": token, "authJson": authJSON.AuthJson, "actJson": authJSON.ActJson, "admin": adminmod.AdminDoc{ ID: &adm.ID, Name: &adm.Name, Role: &adm.Role, Nickname: &adm.Nickname, }, }) } // Logout doc // @Summary 注销 - 管理员注销 // @Description 管理员登陆 // @Tags web-登陆 // @Accept mpfd,json // @Produce json,html // @Param name formData string true "账号" // @Success 200 {string} json "{"msg": "操作成功"}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/logout [post] func Logout(c *gin.Context) { if name, err := common.GetAdminAct(c); err == nil { authweb.RevokeAdminToken(name) } else { log.Error("admin logout error", log.E(err)) } common.ServeJSON(c, stderr.Success, "") } // Logout doc // @Summary 刷新token // @Description 刷新token // @Tags web-登陆 // @Accept json // @Produce json,html // @Success 200 {string} json "{"token": "token1"}" // @Router /web/admin/refresh [post] func Refresh(c *gin.Context) { name, err := common.GetAdminAct(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err) return } cid, err := common.GetJuShangID(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err) return } role, err := common.GetAdminRole(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err) return } token, err := authweb.GenAndSaveToken(&authweb.Claims{Type: authweb.Admin, Act: name, Role: role, CID: cid}) if err != nil { common.ServeJSON(c, stderr.AdminRefreshTokenErr, err) return } common.ServeJSON(c, stderr.Success, gin.H{"token": token}) } // Add doc // @Summary 登陆 - 添加管理员 // @Description 添加管理员 // @Tags web-admin // @Accept mpfd,json // @Produce json,html // @Param name formData string true "账号" // @Param password formData string true "密码" // @Param nickname formData string false "昵称" // @Param email formData string false "邮箱" // @Param privilegeID formData string false "权限ID" // @Success 200 {string} json "{"msg": "操作成功"}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/add [post] func Add(c *gin.Context) { manager, err := common.GetAdminAct(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err.Error()) return } var arg struct { Name string `form:"name" json:"name" binding:"required"` Password string `form:"password" json:"password" binding:"required"` Nickname *string `form:"nickname" json:"nickname" binding:""` //昵称 Email *string `form:"email" json:"email" binding:"omitempty,email"` //邮箱 PrivilegeID *primitive.ObjectID `form:"privilegeID" json:"privilegeID" binding:""` } if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, fmt.Errorf("auth Add arg error: %+v", err)) return } existed, err := adminmod.ExistsByName(arg.Name) if err != nil { common.ServeJSON(c, stderr.ErrParamError, fmt.Errorf("auth Add Exists error: %+v", err)) return } if existed { common.ServeJSON(c, stderr.AdminIsExisted, fmt.Errorf("auth Add admin is existed")) return } var role *string if arg.PrivilegeID != nil { auth, err := authoritymod.FindOne(authoritymod.AuthorityDoc{ID: *arg.PrivilegeID}) if err != nil { common.ServeJSON(c, stderr.Failure, fmt.Errorf("auth FindOne error: %+v", err)) return } if auth.ID.IsZero() { common.ServeJSON(c, stderr.AuthIsNotExist, gin.H{"privilegeID": arg.PrivilegeID}) return } role = &auth.Role } //生成密码 hashPwd, _ := bcrypt.GenerateFromPassword([]byte(arg.Password), bcrypt.DefaultCost) hashPwdStr := string(hashPwd) secret := base32.StdEncoding.EncodeToString([]byte(fmt.Sprintf("admin%s", common.RandStr(10)))) hasBind := false if err = adminmod.Insert(nil, adminmod.AdminDoc{ Name: &arg.Name, Password: &hashPwdStr, Secret: &secret, HasBind: &hasBind, Nickname: arg.Nickname, Email: arg.Email, Role: role, }); err != nil { common.ServeJSON(c, stderr.ErrDbInsertError, fmt.Errorf("auth Add Admin.Insert error: %+v", err)) return } log, _ := json.Marshal(arg) _ = operatorlgmod.RecordOperation(manager, constant.Administrator, constant.Add, string(log), c.Request.URL.RequestURI()) common.ServeJSON(c, stderr.Success, "") } // List doc // @Summary 获取管理员列表 // @Description 获取管理员列表 // @Tags web-admin // @Accept mpfd,json // @Produce json,html // @Param pageNumber query int true "当前页" // @Param pageSize query int true "每页条数" // @Param name query string false "过滤条件-管理员账号" // @Success 200 {string} json "{"msg": "操作成功", "date": { "total":10, "list":[] }}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/list [get] func List(c *gin.Context) { type Query struct { Name *string `form:"name" json:"name" bson:",omitempty"` //用户名 } var arg struct { commod.Page Query } if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, "admin List arg error "+err.Error()) return } skip := int64((arg.PageNumber - 1) * arg.PageSize) limit := int64(arg.PageSize) page, err := adminser.AdminPages(skip, limit, arg.Name) if err != nil { common.ServeJSON(c, stderr.Failure, "admin List.AdminPages error: "+err.Error()) return } common.ServeJSON(c, stderr.Success, page) } // Delete doc // @Summary 登陆 - 删除管理员 // @Description 删除管理员 // @Tags web-admin // @Accept mpfd,json // @Produce json,html // @Param adminID formData string true "目标管理员ID" // @Success 200 {string} json "{"msg": "操作成功"}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/del [delete] func Delete(c *gin.Context) { manager, err := common.GetAdminAct(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err.Error()) return } var arg struct { AdminID string `form:"adminID" json:"adminID,omitempty" binding:"required"` } if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, "author Delete arg error "+err.Error()) return } adminID, err := primitive.ObjectIDFromHex(arg.AdminID) if err != nil { common.ServeJSON(c, stderr.ErrParamError, err) return } // 删除前查询账号名,用于清除token adm, _ := adminmod.FindOneByID(adminID) if err := adminmod.Delete(adminmod.AdminDoc{ID: &adminID}); err != nil { common.ServeJSON(c, stderr.AdminIsNotExist, "author Delete error "+err.Error()) return } // 清除已删除账号的token if adm.Name != "" { authweb.RevokeAdminToken(adm.Name) } log, _ := json.Marshal(arg) _ = operatorlgmod.RecordOperation(manager, constant.Administrator, constant.Delete, string(log), c.Request.URL.RequestURI()) common.ServeJSON(c, stderr.Success, "") } // Update doc // @Summary 登陆 - 修改管理员 // @Description 修改管理员 // @Tags web-admin // @Accept mpfd,json // @Produce json,html // @Param adminID formData string true "目标管理员ID" // @Param nickname formData string false "昵称" // @Param email formData string false "邮箱" // @Param privilegeID formData string false "权限ID, "": 删除权限" // @Param password formData string false "密码" // @Success 200 {string} json "{"msg": "操作成功"}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/admin/update [post] func Update(c *gin.Context) { manager, err := common.GetAdminAct(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err.Error()) return } var arg struct { AdminID string `form:"adminID" json:"adminID" binding:"required"` Nickname *string `form:"nickname" json:"nickname" binding:""` //昵称 Email *string `form:"email" json:"email" binding:"omitempty,email"` //邮箱 PrivilegeID *primitive.ObjectID `form:"privilegeID" json:"privilegeID" binding:""` Password *string `form:"password" json:"password" binding:""` HasLocked *bool `json:"hasLocked,omitempty" form:"hasLocked"` //已禁止登陆 LockReason *string `json:"lockReason,omitempty" form:"lockReason"` //禁止登陆的原因 } if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, "author Update arg error "+err.Error()) return } adminID, err := primitive.ObjectIDFromHex(arg.AdminID) if err != nil { common.ServeJSON(c, stderr.ErrParamError, err) return } if arg.PrivilegeID != nil { if err := authorser.SetAuthority(adminID, *arg.PrivilegeID); err != nil { common.ServeJSON(c, stderr.Failure, "auth SetAuthority error: "+err.Error()) return } } doc := adminmod.AdminDoc{ Nickname: arg.Nickname, Email: arg.Email, HasLocked: arg.HasLocked, LockReason: arg.LockReason, } //更新密码 if arg.Password != nil { hashPwd, _ := bcrypt.GenerateFromPassword([]byte(*arg.Password), bcrypt.DefaultCost) hashPwdStr := string(hashPwd) doc.Password = &hashPwdStr } if err := adminmod.UpdateByID(adminID, doc); err != nil { common.ServeJSON(c, stderr.ErrDbUpdateError, "author UpdateByID error "+err.Error()) return } // 禁用账号时清除token,使其立即失效 if arg.HasLocked != nil && *arg.HasLocked { adm, _ := adminmod.FindOneByID(adminID) if adm.Name != "" { authweb.RevokeAdminToken(adm.Name) } } log, _ := json.Marshal(arg) _ = operatorlgmod.RecordOperation(manager, constant.Administrator, constant.Modify, string(log), c.Request.URL.RequestURI()) common.ServeJSON(c, stderr.Success, "") } // RefreshToken doc // @Summary token刷新 // @Description token刷新 // @Tags web-登陆 // @Accept mpfd,json // @Produce json,html // @Param name formData string true "账号" // @Param password formData string true "密码" // @Param otp formData string true "动态口令" // @Success 200 {string} json "{"msg": "操作成功"}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /api/web/admin/channel/user/refresh [get] func RefreshToken(c *gin.Context) { account, err := common.GetAdminAct(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err) return } role, err := common.GetAdminRole(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err) return } cid, err := common.GetJuShangID(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err) return } token, err := authweb.GenAndSaveToken(&authweb.Claims{Type: authweb.Admin, Act: account, Role: role, CID: cid}) if err != nil { common.ServeJSON(c, stderr.ErrUnrecognized, "generator token error!") return } c.Writer.Header().Set("Refresh-Authorization", "false") type RefreshTokenResp struct { Token string `json:"token"` } common.ServeJSON(c, stderr.Success, RefreshTokenResp{Token: token}) }