v0.9.33 修复表格会话数据BUG & 安全更新

This commit is contained in:
Max 2022-01-19 22:29:21 +08:00
parent d68accee47
commit 6d2cdaaf1b
13 changed files with 373 additions and 50 deletions

View file

@ -20,5 +20,5 @@ func check(t *testing.T) {
for key := range gou.Flows {
keys = append(keys, key)
}
assert.Equal(t, 18, len(keys))
assert.Equal(t, 19, len(keys))
}

View file

@ -118,15 +118,20 @@ func (api API) MergeDefaultQueryParam(param gou.QueryParam, i int, sid string) g
// GetQueryParam 解析参数
func GetQueryParam(v interface{}, sid string) gou.QueryParam {
fmt.Println("\n==== GetQueryParam ===== SID:", sid)
data := map[string]interface{}{}
if sid != "" {
var err error
ss := session.Global().ID(sid)
data, err = ss.Dump()
utils.Dump(data)
if err != nil {
xlog.Printf("读取会话信息出错 %s", err.Error())
}
}
fmt.Println("==== GetQueryParam========================")
fmt.Println("")
v = share.Bind(v, maps.Of(data).Dot())
param, ok := gou.AnyToQueryParam(v)
if !ok {

View file

@ -1,7 +1,7 @@
package share
// VERSION 版本号
const VERSION = "0.9.32"
const VERSION = "0.9.33"
// DOMAIN 许可域(废弃)
const DOMAIN = "*.iqka.com"

View file

@ -70,7 +70,7 @@ func apiDefaultWhere(model *gou.Model, withs map[string]gou.With, name string, p
}
// apiDefaultSetting 数据表格配置默认值
func apiDefaultSetting(table *Table) share.API {
func apiDefaultSetting() share.API {
return share.API{
Name: "setting",
Guard: "bearer-jwt",

View file

@ -1,11 +1,13 @@
package table
import (
"fmt"
"strings"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/kun/maps"
"github.com/yaoapp/kun/utils"
)
func init() {
@ -39,11 +41,17 @@ func ProcessSearch(process *gou.Process) interface{} {
}
// Before Hook
process.Args = table.Before(table.Hooks.BeforeSearch, process.Args)
process.Args = table.Before(table.Hooks.BeforeSearch, process.Args, process.Sid)
// 参数表
process.ValidateArgNums(4)
param := api.MergeDefaultQueryParam(process.ArgsQueryParams(1), 0, process.Sid)
fmt.Println("\n==== ProcessSearch ============= SID:", process.Sid)
utils.Dump(param)
fmt.Println("==== END ProcessSearch ===============================================")
fmt.Println("")
page := process.ArgsInt(2, api.DefaultInt(1))
pagesize := process.ArgsInt(3, api.DefaultInt(2))
@ -51,7 +59,7 @@ func ProcessSearch(process *gou.Process) interface{} {
response := gou.NewProcess(api.Process, param, page, pagesize).Run()
// After Hook
return table.After(table.Hooks.AfterSearch, response)
return table.After(table.Hooks.AfterSearch, response, process.Sid)
}
// ProcessFind xiang.table.Find
@ -67,7 +75,7 @@ func ProcessFind(process *gou.Process) interface{} {
}
// Before Hook
process.Args = table.Before(table.Hooks.BeforeFind, process.Args)
process.Args = table.Before(table.Hooks.BeforeFind, process.Args, process.Sid)
// 参数表
process.ValidateArgNums(2)
@ -78,7 +86,7 @@ func ProcessFind(process *gou.Process) interface{} {
response := gou.NewProcess(api.Process, id, param).Run()
// After Hook
return table.After(table.Hooks.AfterFind, response)
return table.After(table.Hooks.AfterFind, response, process.Sid)
}
// ProcessSave xiang.table.Save
@ -95,7 +103,7 @@ func ProcessSave(process *gou.Process) interface{} {
}
// Before Hook
process.Args = table.Before(table.Hooks.BeforeSave, process.Args)
process.Args = table.Before(table.Hooks.BeforeSave, process.Args, process.Sid)
// 参数处理
process.ValidateArgNums(2)
@ -104,7 +112,7 @@ func ProcessSave(process *gou.Process) interface{} {
response := gou.NewProcess(api.Process, process.Args[1]).Run()
// After Hook
return table.After(table.Hooks.AfterSave, response)
return table.After(table.Hooks.AfterSave, response, process.Sid)
}
// ProcessDelete xiang.table.Delete
@ -315,10 +323,10 @@ func ProcessSelect(process *gou.Process) interface{} {
}
// Before Hook
process.Args = table.Before(table.Hooks.BeforeSelect, process.Args)
process.Args = table.Before(table.Hooks.BeforeSelect, process.Args, process.Sid)
response := gou.NewProcess(api.Process, process.Args[1:]...).Run()
// After Hook
return table.After(table.Hooks.AfterSelect, response)
return table.After(table.Hooks.AfterSelect, response, process.Sid)
}

View file

@ -93,7 +93,7 @@ func (table *Table) loadAPIs() {
return
}
defaults := getDefaultAPIs(table.Bind)
defaults["setting"] = apiDefaultSetting(table)
defaults["setting"] = apiDefaultSetting()
for name := range table.APIs {
if _, has := defaults[name]; !has {
@ -150,7 +150,7 @@ func getDefaultAPIs(bind Bind) map[string]share.API {
}
// Before 运行 Before hook
func (table *Table) Before(process string, processArgs []interface{}) []interface{} {
func (table *Table) Before(process string, processArgs []interface{}, sid string) []interface{} {
if process == "" {
return processArgs
}
@ -161,7 +161,7 @@ func (table *Table) Before(process string, processArgs []interface{}) []interfac
res = append(res, processArgs[0])
}
response, err := gou.NewProcess(process, args...).Exec()
response, err := gou.NewProcess(process, args...).WithSID(sid).Exec()
if err != nil {
xlog.Println("Hook执行失败: ", err.Error(), maps.StrAny{"process": process, "args": args})
return processArgs
@ -177,11 +177,11 @@ func (table *Table) Before(process string, processArgs []interface{}) []interfac
}
// After 运行 After hook
func (table *Table) After(process string, data interface{}) interface{} {
func (table *Table) After(process string, data interface{}, sid string) interface{} {
if process == "" {
return data
}
response, err := gou.NewProcess(process, data).Exec()
response, err := gou.NewProcess(process, data).WithSID(sid).Exec()
if err != nil {
xlog.Println("Hook执行失败: ", err.Error(), maps.StrAny{"process": process, "data": data})
return data

View file

@ -48,5 +48,5 @@ func check(t *testing.T) {
for key := range Tables {
keys = append(keys, key)
}
assert.Equal(t, 8, len(keys))
assert.Equal(t, 9, len(keys))
}

View file

@ -2,6 +2,9 @@
"name": "象传应用",
"short": "象传",
"description": "象传应用后台",
"option": {
"index": "/table/service"
},
"storage": {
"default": "oss",
"oss": {
@ -14,6 +17,5 @@
},
"cos": {},
"s3": {}
},
"option": {}
}
}

View file

@ -0,0 +1,17 @@
{
"label": "会话测试",
"version": "1.0.0",
"description": "Before:Select",
"nodes": [
{
"name": "会话数据",
"process": "session.Dump"
},
{
"name": "打印数据",
"process": "xiang.helper.Print",
"args": ["会话数据:", "{{$res.会话数据}}"]
}
],
"output": "{{$in}}"
}

View file

@ -22,7 +22,6 @@
"select": { "guard": "-" },
"search": {
"process": "models.service.Paginate",
"guard": "-",
"default": [null, null, 15]
},
"find": {

305
tests/tables/session.json Normal file
View file

@ -0,0 +1,305 @@
{
"name": "会话调试",
"version": "1.0.0",
"decription": "会话调试",
"bind": {
"model": "service",
"withs": {
"manu": {
"query": {
"select": ["id", "name", "short_name", "status"]
}
},
"kind": {
"query": {
"select": ["id", "name"]
}
}
}
},
"hooks": {
"before:search": "flows.hooks.session",
"after:search": "flows.hooks.session"
},
"apis": {
"search": {
"default": [
{
"wheres": [{ "column": "status", "value": "{{user_id}}" }],
"withs": { "manu": {}, "kind": {} }
},
null,
10
]
}
},
"columns": {
"服务名称": {
"label": "服务名称",
"view": {
"type": "label",
"props": {
"value": ":name"
}
},
"edit": {
"type": "input",
"props": {
"value": ":name"
}
}
},
"所属厂商": {
"label": "所属厂商",
"view": {
"name": "label",
"props": {
"value": ":manu.short_name"
}
},
"edit": {
"type": "select",
"props": {
"value": ":manu.id",
"searchable": true,
"remote": {
"api": "/api/manu/get",
"query": {
"select": ["id", "name"],
"keyword": "where_name_like"
}
}
}
}
},
"服务类型": {
"label": "服务类型",
"view": {
"name": "label",
"props": {
"value": ":kind.name"
}
},
"edit": {
"type": "select",
"props": {
"value": ":kind.id",
"tree": true,
"searchable": true,
"remote": {
"api": "/api/kind/tree",
"query": {
"select": ["id", "name"],
"keyword": "where_name_like"
}
}
}
}
},
"服务领域": {
"label": "服务领域",
"view": {
"name": "tags",
"props": {
"value": ":fields"
}
},
"edit": {
"type": "select",
"props": {
"value": ":fields",
"searchable": true,
"inputable": true,
"multiple": true,
"options": [
{ "label": "视频会议", "value": "视频会议" },
{ "label": "即时通信", "value": "即时通信" },
{ "label": "客服管理", "value": "客服管理" },
{ "label": "财务管理", "value": "财务管理" },
{ "label": "客户关系管理", "value": "客户关系管理" },
{ "label": "营销管理", "value": "营销管理" },
{ "label": "办公自动化", "value": "办公自动化" },
{ "label": "ERP", "value": "ERP" },
{ "label": "人力", "value": "人力" },
{ "label": "采购", "value": "采购" },
{ "label": "供应链", "value": "供应链" },
{ "label": "企业网盘", "value": "企业网盘" },
{ "label": "邮件服务", "value": "邮件服务" },
{ "label": "电子合同", "value": "电子合同" },
{ "label": "安全工具", "value": "安全工具" },
{ "label": "舆情分析", "value": "舆情分析" },
{ "label": "行业应用", "value": "行业应用" },
{ "label": "其他", "value": "其他" }
]
}
}
},
"计费方式C": {
"label": "计费方式",
"view": {
"name": "xxxxx",
"components": { "label": {}, "image": {} }
}
},
"计费方式": {
"label": "计费方式",
"view": {
"name": "tags",
"props": {
"value": ":fields",
"format": [".jpg", "..."]
}
},
"edit": {
"type": "select",
"props": {
"value": ":price_options",
"multiple": true,
"options": [
{ "label": "按年订阅", "value": "按年订阅" },
{ "label": "按月订阅", "value": "按月订阅" },
{ "label": "按量计费", "value": "按量计费" },
{ "label": "私有化部署", "value": "私有化部署" },
{ "label": "其他", "value": "其他" }
]
}
}
},
"行业覆盖": {
"label": "行业覆盖",
"view": {
"name": "tags",
"props": {
"value": ":industries"
}
},
"edit": {
"type": "select",
"props": {
"value": ":industries",
"inputable": true,
"multiple": true,
"options": [
{ "label": "房地产", "value": "房地产" },
{ "label": "旅游", "value": "旅游" },
{ "label": "教育", "value": "教育" },
{ "label": "互联网", "value": "互联网" },
{ "label": "其他", "value": "其他" }
]
}
}
},
"状态": {
"label": "状态",
"view": {
"name": "tags",
"props": {
"value": ":status",
"options": [
{ "label": "开启", "value": "enabled", "color": "primary" },
{ "label": "关闭", "value": "disabled", "color": "danger" }
]
}
},
"edit": {
"type": "select",
"props": {
"value": ":status",
"options": [
{ "label": "开启", "value": "enabled" },
{ "label": "关闭", "value": "disabled" }
]
}
}
},
"创建时间": {
"label": "创建时间",
"view": {
"name": "label",
"props": {
"value": ":created_at",
"datetime-format": "YYYY年MM月DD日 @hh:mm:ss"
}
}
},
"更新时间": {
"label": "更新时间",
"view": {
"name": "label",
"props": {
"value": ":update_at",
"datetime-format": "YYYY年MM月DD日 @hh:mm:ss"
}
}
},
"查询排序": {
"label": "查询排序",
"view": {
"type": "label",
"props": {
"value": ":rank"
}
},
"edit": {
"type": "input",
"props": {
"value": ":rank",
"type": "number"
}
}
}
},
"filters": {},
"list": {
"primary": "id",
"layout": {
"columns": [
{ "name": "服务名称", "width": 6 },
{ "name": "所属厂商", "width": 6 },
{ "name": "服务类型", "width": 4 },
{ "name": "状态", "width": 4 },
{ "name": "服务领域", "width": 4 },
{ "name": "计费方式", "width": 4 },
{ "name": "创建时间", "width": 6 },
{ "name": "更新时间", "width": 6 }
],
"filters": []
},
"actions": {}
},
"edit": {
"primary": "id",
"layout": {
"fieldset": [
{
"title": "基础信息",
"description": "",
"columns": [
{ "name": "服务名称", "width": 6 },
{ "name": "所属厂商", "width": 6 },
{ "name": "服务类型", "width": 6 },
{ "name": "状态", "width": 6 },
{ "name": "rank", "width": 6 }
]
},
{
"title": "数据标签",
"description": "",
"columns": ["服务领域", "计费方式", "行业覆盖"]
}
]
},
"actions": {
"cancel": {},
"save": {
"type": "button",
"props": {
"label": "保存"
}
},
"delete": {}
}
},
"insert": {},
"view": {}
}

View file

@ -1,14 +1,15 @@
package user
import (
"fmt"
"time"
"github.com/golang-jwt/jwt"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/session"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/maps"
"github.com/yaoapp/xiang/config"
"github.com/yaoapp/xiang/helper"
"golang.org/x/crypto/bcrypt"
)
@ -52,6 +53,7 @@ func Auth(field string, value string, password string) maps.Map {
row := rows[0]
passwordHash := row.Get("password").(string)
row.Del("password")
err = bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password))
if err != nil {
@ -59,38 +61,23 @@ func Auth(field string, value string, password string) maps.Map {
}
expiresAt := time.Now().Unix() + 3600
token := MakeToken(row, expiresAt)
row.Del("password")
// token := MakeToken(row, expiresAt)
sid := session.ID()
id := any.Of(row.Get("id")).CInt()
token := helper.JwtMake(id, map[string]interface{}{}, map[string]interface{}{
"expires_at": expiresAt,
"sid": sid,
})
session.Global().Expire(time.Duration(token.ExpiresAt)*time.Second).ID(sid).Set("user_id", id)
session.Global().ID(sid).Set("user", row)
// 读取菜单
menus := gou.NewProcess("flows.xiang.menu").Run()
return maps.Map{
"expires_at": expiresAt,
"token": token,
"expires_at": token.ExpiresAt,
"token": token.Token,
"user": row,
"menus": menus,
}
}
// MakeToken 生成 JWT Token
func MakeToken(row maps.Map, ExpiresAt int64) string {
claims := &JwtClaims{
ID: int(row.Get("id").(int64)),
Type: row.Get("type").(string),
Name: row.Get("name").(string),
StandardClaims: jwt.StandardClaims{
Id: fmt.Sprintf("%s_%d", row.Get("type"), row.Get("id")),
Subject: fmt.Sprintf("%d", row.Get("id")),
ExpiresAt: ExpiresAt,
Issuer: fmt.Sprintf("%d", row.Get("id")),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(config.Conf.JWT.Secret))
if err != nil {
exception.New("生成登录口令失败 %s", 500, err).Throw()
}
return tokenString
}

View file

@ -3,7 +3,7 @@
"version": "1.0.0",
"description": "数据表格接口API",
"group": "xiang/table",
"guard": "in-process",
"guard": "bearer-jwt",
"paths": [
{
"path": "/:name/search",