Skip to content

Commit 2f58ac6

Browse files
committed
调整权限判断、非开放请求流程、错误提示
1 parent 65d9bae commit 2f58ac6

File tree

16 files changed

+716
-477
lines changed

16 files changed

+716
-477
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
- [x] 基于角色控制
2424

2525
- [ ] 远程函数
26-
26+
- [ ] 错误提示
27+
- [ ] 查询节点 自定义查询数据
28+
29+
2730
## 查询实现
2831
1. 根据json构造节点树, 并检查节点结构(不符合直接返回)
2932
2. parse 节点树内容, 并分析关联关系(不要求json的key顺序, 因为go的原生map不支持顺序遍历)
@@ -34,7 +37,7 @@
3437
# 列表查询限制
3538

3639
[//]: # (1. page,count 最大值)
37-
- []下只能有一个主查询表 (不依赖于列表中其他表)
40+
- `[]`下只能有一个主查询表 (不依赖于列表中其他表)
3841

3942
# 权限控制方案
4043
##

action/action.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package action
2+
3+
import (
4+
"context"
5+
"github.com/glennliao/apijson-go/db"
6+
"github.com/gogf/gf/v2/database/gdb"
7+
"github.com/gogf/gf/v2/errors/gerror"
8+
"github.com/gogf/gf/v2/frame/g"
9+
"github.com/gogf/gf/v2/util/gconv"
10+
"strings"
11+
)
12+
13+
// Structure https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/Operation.java
14+
type Structure struct {
15+
Must []string `json:"MUST,omitempty"`
16+
Refuse []string `json:"REFUSE,omitempty"`
17+
18+
Unique []string `json:"UNIQUE,omitempty"`
19+
20+
// 不存在时添加
21+
Insert g.Map `json:"INSERT,omitempty"`
22+
// 不存在时就添加,存在时就修改
23+
Update g.Map `json:"UPDATE,omitempty"`
24+
// 存在时替换
25+
Replace g.Map `json:"REPLACE,omitempty"`
26+
// 存在时移除
27+
Remove []string `json:"REMOVE,omitempty"`
28+
}
29+
30+
// Action 非get查询的request表中的请求
31+
type Action struct {
32+
ctx context.Context
33+
tagRequest *db.Request
34+
method string
35+
36+
req g.Map
37+
38+
err error
39+
40+
children map[string]Node
41+
}
42+
43+
func New(ctx context.Context, method string, req g.Map) *Action {
44+
45+
request, err := checkTag(req, method)
46+
if err != nil {
47+
panic(err)
48+
}
49+
50+
delete(req, "tag")
51+
52+
a := &Action{
53+
ctx: ctx,
54+
tagRequest: request,
55+
method: method,
56+
req: req,
57+
children: map[string]Node{},
58+
}
59+
return a
60+
}
61+
62+
func (a *Action) parse() error {
63+
64+
structures := a.tagRequest.Structure
65+
66+
for key, v := range a.req {
67+
68+
structureMap, ok := structures[key]
69+
if !ok {
70+
return gerror.New("structure错误: 400, 缺少" + key)
71+
}
72+
73+
structure := Structure{}
74+
err := gconv.Scan(structureMap, &structure)
75+
if err != nil {
76+
return err
77+
}
78+
79+
structure.Must = strings.Split(structure.Must[0], ",")
80+
structure.Refuse = strings.Split(structure.Refuse[0], ",")
81+
82+
node := newNode(key, v.(g.Map), structure)
83+
err = node.parse(a.ctx, a.method)
84+
if err != nil {
85+
return err
86+
}
87+
88+
a.children[key] = node
89+
}
90+
91+
return nil
92+
}
93+
94+
func (a *Action) Result() (g.Map, error) {
95+
96+
err := a.parse()
97+
if err != nil {
98+
return nil, err
99+
}
100+
101+
ret := g.Map{}
102+
103+
err = g.DB().Transaction(a.ctx, func(ctx context.Context, tx *gdb.TX) error {
104+
for k, node := range a.children {
105+
ret[k], err = node.execute(ctx, a.method)
106+
if err != nil {
107+
return err
108+
}
109+
}
110+
return nil
111+
})
112+
113+
return ret, err
114+
}

action/node.go

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
package action
2+
3+
import (
4+
"context"
5+
"github.com/glennliao/apijson-go/config"
6+
"github.com/glennliao/apijson-go/consts"
7+
"github.com/glennliao/apijson-go/db"
8+
"github.com/gogf/gf/v2/errors/gerror"
9+
"github.com/gogf/gf/v2/frame/g"
10+
"github.com/gogf/gf/v2/util/gconv"
11+
"github.com/samber/lo"
12+
)
13+
14+
type Node struct {
15+
req g.Map
16+
key string
17+
tableName string
18+
role string
19+
20+
data g.Map // 需写入数据库的数据
21+
where g.Map // 条件
22+
rowKey string // 主键
23+
24+
structure Structure
25+
}
26+
27+
func newNode(key string, req g.Map, structure Structure) Node {
28+
return Node{
29+
key: key, req: req, structure: structure,
30+
}
31+
}
32+
33+
func (n *Node) parseReq(method string) {
34+
n.data = g.Map{}
35+
n.where = g.Map{}
36+
for key, val := range n.req {
37+
if key == "@role" {
38+
n.role = gconv.String(val)
39+
} else {
40+
if method == consts.MethodDelete {
41+
n.where[key] = val
42+
} else {
43+
if key == n.rowKey {
44+
if method == consts.MethodPut {
45+
n.where[key] = val
46+
}
47+
// Post 暂原则上不让传递这个值
48+
} else {
49+
n.data[key] = val
50+
}
51+
}
52+
}
53+
}
54+
}
55+
56+
func (n *Node) parse(ctx context.Context, method string) error {
57+
58+
access, err := db.GetAccess(n.key, true)
59+
60+
if err != nil {
61+
return err
62+
}
63+
64+
n.tableName = access.Name
65+
n.rowKey = "id" // 暂固定
66+
67+
n.parseReq(method)
68+
69+
// 0. 角色替换
70+
71+
err = n.roleUpdate()
72+
if err != nil {
73+
return err
74+
}
75+
76+
// 1. 检查权限, 无权限就不用做参数检查了
77+
var accessRoles []string
78+
79+
switch method {
80+
case consts.MethodPost:
81+
accessRoles = access.Post
82+
case consts.MethodPut:
83+
accessRoles = access.Put
84+
case consts.MethodDelete:
85+
accessRoles = access.Delete
86+
}
87+
88+
err = n.checkAccess(ctx, method, accessRoles)
89+
if err != nil {
90+
return err
91+
}
92+
93+
// 2. 检查参数
94+
err = n.checkReq()
95+
if err != nil {
96+
return err
97+
}
98+
99+
return nil
100+
}
101+
102+
func (n *Node) roleUpdate() error {
103+
104+
if val, exists := n.structure.Insert["@role"]; exists {
105+
if n.role == "" {
106+
n.role = gconv.String(val)
107+
}
108+
}
109+
110+
if val, exists := n.structure.Update["@role"]; exists {
111+
n.role = gconv.String(val)
112+
}
113+
114+
return nil
115+
}
116+
117+
func (n *Node) checkAccess(ctx context.Context, method string, accessRoles []string) error {
118+
119+
role, err := config.DefaultRoleFunc(ctx, config.RoleReq{
120+
Table: n.tableName,
121+
Method: method,
122+
NodeRole: n.role,
123+
})
124+
125+
if err != nil {
126+
return err
127+
}
128+
129+
if role == consts.DENY {
130+
return gerror.Newf("deny node: %s with %s", n.key, n.role)
131+
}
132+
133+
n.role = role
134+
135+
if !lo.Contains(accessRoles, role) {
136+
return gerror.Newf("node not access: %s with %s", n.key, n.role)
137+
}
138+
139+
where, err := config.AccessConditionFunc(ctx, config.AccessConditionReq{
140+
Table: n.tableName,
141+
TableAccessRoleList: accessRoles,
142+
Method: method,
143+
NodeRole: n.role,
144+
NodeReq: n.req,
145+
})
146+
147+
if err != nil {
148+
return err
149+
}
150+
151+
if method == consts.MethodPost {
152+
for k, v := range where {
153+
n.data[k] = v
154+
}
155+
} else {
156+
for k, v := range where {
157+
n.where[k] = v
158+
}
159+
}
160+
161+
return nil
162+
}
163+
164+
func (n *Node) checkReq() error {
165+
166+
// must
167+
for _, key := range n.structure.Must {
168+
if _, exists := n.req[key]; !exists {
169+
return gerror.New("structure错误: 400, 缺少" + n.key + "." + key)
170+
}
171+
}
172+
173+
// refuse
174+
if n.structure.Refuse[0] == "!" {
175+
if len(n.structure.Must) == 0 {
176+
return gerror.New("structure错误: 400, REFUSE为!时必须指定MUST" + n.key)
177+
}
178+
179+
for key, _ := range n.req {
180+
if !lo.Contains(n.structure.Must, key) {
181+
return gerror.New("structure错误: 400, 不能包含" + n.key + "." + key)
182+
}
183+
}
184+
185+
} else {
186+
for _, key := range n.structure.Refuse {
187+
if _, exists := n.req[key]; exists {
188+
return gerror.New("structure错误: 400, 不能包含" + n.key + "." + key)
189+
}
190+
}
191+
}
192+
193+
return nil
194+
}
195+
196+
func (n *Node) reqUpdate() error {
197+
198+
for key, updateVal := range n.structure.Update {
199+
n.data[key] = updateVal
200+
}
201+
202+
for key, updateVal := range n.structure.Insert {
203+
if _, exists := n.data[key]; !exists {
204+
n.data[key] = updateVal
205+
}
206+
}
207+
208+
return nil
209+
}
210+
211+
func (n *Node) do(ctx context.Context, method string) (g.Map, error) {
212+
213+
switch method {
214+
case consts.MethodPost:
215+
id, count, err := db.Insert(ctx, n.tableName, n.data)
216+
if err != nil {
217+
return nil, err
218+
}
219+
220+
return g.Map{
221+
"code": 200,
222+
"id": id,
223+
"count": count,
224+
}, nil
225+
case consts.MethodPut:
226+
count, err := db.Update(ctx, n.tableName, n.data, n.where)
227+
if err != nil {
228+
return nil, err
229+
}
230+
231+
return g.Map{
232+
"code": 200,
233+
"count": count,
234+
}, nil
235+
case consts.MethodDelete:
236+
count, err := db.Delete(ctx, n.tableName, n.where)
237+
if err != nil {
238+
return nil, err
239+
}
240+
241+
return g.Map{
242+
"code": 200,
243+
"count": count,
244+
}, nil
245+
}
246+
247+
return nil, gerror.New("undefined method:" + method)
248+
}
249+
250+
func (n *Node) execute(ctx context.Context, method string) (g.Map, error) {
251+
252+
// 参数替换
253+
err := n.reqUpdate()
254+
if err != nil {
255+
return nil, err
256+
}
257+
258+
// 执行操作
259+
return n.do(ctx, method)
260+
}

0 commit comments

Comments
 (0)