-
Notifications
You must be signed in to change notification settings - Fork 0
/
permission_table.go
172 lines (142 loc) · 3.73 KB
/
permission_table.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package quill
import (
"fmt"
"strings"
"sync"
)
type permissionLayer struct {
// negative values means writing, 0 means unoccupied, positive values is
// the number of commands reading.
permissions map[string]int
children map[string]*permissionLayer
}
func newPermissionLayer() permissionLayer {
return permissionLayer{
permissions: make(map[string]int),
children: make(map[string]*permissionLayer),
}
}
func (pl *permissionLayer) Conflict(keys []string, newPerm PermissionType) bool {
if len(keys) == 0 {
panic("conflict should never be passed 0 keys")
}
rootKey := keys[0]
if curPerm, ok := pl.permissions[rootKey]; ok {
if newPerm == WritePermissionType || curPerm < 0 {
return true
}
}
if len(keys) > 1 {
if curPerm, ok := pl.children[rootKey]; ok {
return curPerm.Conflict(keys[1:], newPerm)
}
}
return false
}
func (pl *permissionLayer) Add(keys []string, newPerm PermissionType) {
if len(keys) == 0 {
panic("add should never be passed 0 keys")
}
rootKey := keys[0]
if len(keys) == 1 {
switch newPerm {
case WritePermissionType:
pl.permissions[rootKey] = -1
case ReadPermissionType:
if oldVal, ok := pl.permissions[rootKey]; ok {
pl.permissions[rootKey] = oldVal + 1
} else {
pl.permissions[rootKey] = 1
}
}
return
}
if _, ok := pl.children[rootKey]; !ok {
layer := newPermissionLayer()
pl.children[rootKey] = &layer
}
pl.children[rootKey].Add(keys[1:], newPerm)
}
func (pl *permissionLayer) Clear(keys []string) {
if len(keys) == 0 {
panic("add should never be passed 0 keys")
}
rootKey := keys[0]
if len(keys) == 1 {
if perm, ok := pl.permissions[rootKey]; ok {
if perm < 0 {
pl.permissions[rootKey] = 0
} else if perm > 0 {
pl.permissions[rootKey] = perm - 1
} else {
panic(fmt.Errorf("trying to clear permission %s that's already clear", rootKey))
}
} else {
panic(fmt.Errorf("trying to clear permission %s that's never been set", rootKey))
}
return
}
if layer, ok := pl.children[rootKey]; ok {
layer.Clear(keys[1:])
} else {
panic(fmt.Errorf("trying to clear permission %s that's never been set", keys))
}
}
// Thread safe collection of permissions
type PermissionTable struct {
permissions permissionLayer
changes int
lock sync.RWMutex
}
func NewPermissionTable() *PermissionTable {
return &PermissionTable{
permissions: newPermissionLayer(),
}
}
// Assumes something else is utilizing the mutex to guarantee synchronization
func (pt *PermissionTable) unsafeConflict(newPermission map[string]PermissionType) bool {
for key, newVal := range newPermission {
path := strings.Split(key, ".")
if pt.permissions.Conflict(path, newVal) {
return true
}
}
return false
}
// Atomic operation
func (pt *PermissionTable) Conflicts(newPermissions map[string]PermissionType) bool {
pt.lock.RLock()
defer pt.lock.RUnlock()
return pt.unsafeConflict(newPermissions)
}
// Atomic operation
func (pt *PermissionTable) Version() int {
pt.lock.RLock()
defer pt.lock.RUnlock()
return pt.changes
}
// Atomic operation
func (pt *PermissionTable) TryAdd(newPermissions map[string]PermissionType) bool {
pt.lock.Lock()
defer pt.lock.Unlock()
if pt.unsafeConflict(newPermissions) {
return false
}
pt.changes++
for key, permission := range newPermissions {
keys := strings.Split(key, ".")
if pt.permissions.Conflict(keys, permission) {
panic(fmt.Errorf("%s permission conflicts with the rest of the block attempting to be added", key))
}
pt.permissions.Add(keys, permission)
}
return true
}
func (pt *PermissionTable) Clear(permissionsToClear map[string]PermissionType) {
pt.lock.Lock()
defer pt.lock.Unlock()
pt.changes++
for key := range permissionsToClear {
pt.permissions.Clear(strings.Split(key, "."))
}
}