Skip to content

Commit c355046

Browse files
authored
Plugin permissoin service (#125)
1 parent 9c31f7f commit c355046

17 files changed

+228
-79
lines changed

cmd/autoupdate/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"path"
1414
"syscall"
1515

16+
"github.com/OpenSlides/openslides-permission-service/pkg/permission"
1617
"github.com/openslides/openslides-autoupdate-service/internal/auth"
1718
"github.com/openslides/openslides-autoupdate-service/internal/autoupdate"
1819
"github.com/openslides/openslides-autoupdate-service/internal/datastore"
@@ -93,14 +94,13 @@ func run() error {
9394
}
9495

9596
// Perm Service.
96-
perms := &test.MockPermission{}
97-
perms.Default = true
97+
perms := permission.New(datastoreService)
9898

9999
// Restricter Service.
100100
restricter := restrict.New(perms, restrict.RelationChecker(restrict.RelationLists, perms))
101101

102102
// Autoupdate Service.
103-
service := autoupdate.New(datastoreService, restricter, closed)
103+
service := autoupdate.New(datastoreService, restricter, perms, closed)
104104

105105
// Auth Service.
106106
authService, err := buildAuth(env, r, closed, errHandler)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.15
44

55
require (
66
github.com/OpenSlides/openslides-models-to-go v0.1.1-0.20201023163752-f3a92dde2a27
7+
github.com/OpenSlides/openslides-permission-service v0.0.0-20201106150223-db52cf71b584
78
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1
89
github.com/gomodule/redigo v1.8.3
910
github.com/ostcar/topic v0.3.4-0.20200613094955-61bb28837a98

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/OpenSlides/openslides-models-to-go v0.1.1-0.20201023163752-f3a92dde2a27 h1:hKpGuicCgWOrbDHU6+1vsPJgzbzX1Y1el5XaeIUZMFY=
22
github.com/OpenSlides/openslides-models-to-go v0.1.1-0.20201023163752-f3a92dde2a27/go.mod h1:CriCefW5smTixhFfVLiuA8NgyMX4PAU5e3YpJHnaZx8=
3+
github.com/OpenSlides/openslides-permission-service v0.0.0-20201106150223-db52cf71b584 h1:5Fv6W0+eyuD4TkL+I4WjOjM2TSkX+m24vIQq9utPGO0=
4+
github.com/OpenSlides/openslides-permission-service v0.0.0-20201106150223-db52cf71b584/go.mod h1:VPzKimi8Jz5Qdu5oeAWYRFLThXRP6pnueH/9K8wILDE=
35
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
46
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
57
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=

internal/autoupdate/autoupdate.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ import (
2121
// value means, that more memory is used.
2222
const pruneTime = 10 * time.Minute
2323

24+
// Format of keys in the topic that shows, that a full update is necessary. It
25+
// is in the same namespace then model names. So make sure, there is no model
26+
// with this name.
27+
const fullUpdateFormat = "fullupdate/%d"
28+
2429
// Autoupdate holds the state of the autoupdate service. It has to be initialized
2530
// with autoupdate.New().
2631
type Autoupdate struct {
@@ -30,7 +35,7 @@ type Autoupdate struct {
3035
}
3136

3237
// New creates a new autoupdate service.
33-
func New(datastore Datastore, restricter Restricter, closed <-chan struct{}) *Autoupdate {
38+
func New(datastore Datastore, restricter Restricter, userUdater UserUpdater, closed <-chan struct{}) *Autoupdate {
3439
a := &Autoupdate{
3540
datastore: datastore,
3641
restricter: restricter,
@@ -43,6 +48,16 @@ func New(datastore Datastore, restricter Restricter, closed <-chan struct{}) *Au
4348
for k := range data {
4449
keys = append(keys, k)
4550
}
51+
52+
uids, err := userUdater.AdditionalUpdate(context.TODO(), data)
53+
if err != nil {
54+
return fmt.Errorf("getting addition user ids: %w", err)
55+
}
56+
57+
for _, uid := range uids {
58+
keys = append(keys, fmt.Sprintf(fullUpdateFormat, uid))
59+
}
60+
4661
a.topic.Publish(keys...)
4762
return nil
4863
})
@@ -125,7 +140,7 @@ func (a *Autoupdate) RestrictedData(ctx context.Context, uid int, keys ...string
125140
data[key] = values[i]
126141
}
127142

128-
if err := a.restricter.Restrict(uid, data); err != nil {
143+
if err := a.restricter.Restrict(ctx, uid, data); err != nil {
129144
return nil, fmt.Errorf("restrict data: %w", err)
130145
}
131146
return data, nil

internal/autoupdate/autoupdate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func TestLive(t *testing.T) {
1212
datastore := new(test.MockDatastore)
1313
closed := make(chan struct{})
1414
defer close(closed)
15-
s := autoupdate.New(datastore, new(test.MockRestricter), closed)
15+
s := autoupdate.New(datastore, new(test.MockRestricter), mockUserUpdater{}, closed)
1616
kb := test.KeysBuilder{K: []string{"foo", "bar"}}
1717

1818
ctx, cancel := context.WithCancel(context.Background())

internal/autoupdate/connection.go

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,7 @@ type Connection struct {
2222
// this case, nil is returned.
2323
func (c *Connection) Next(ctx context.Context) (map[string]json.RawMessage, error) {
2424
if c.filter == nil {
25-
// First time called
26-
c.filter = new(filter)
27-
if c.tid == 0 {
28-
c.tid = c.autoupdate.topic.LastID()
29-
}
30-
31-
if err := c.kb.Update(ctx); err != nil {
32-
return nil, fmt.Errorf("create keys for keysbuilder: %w", err)
33-
}
34-
35-
data, err := c.autoupdate.RestrictedData(ctx, c.uid, c.kb.Keys()...)
36-
if err != nil {
37-
return nil, fmt.Errorf("get first time restricted data: %w", err)
38-
}
39-
40-
// Delete empty values in first responce.
41-
for k, v := range data {
42-
if len(v) == 0 {
43-
delete(data, k)
44-
}
45-
}
46-
47-
if err := c.filter.filter(data); err != nil {
48-
return nil, fmt.Errorf("filter data for the first time: %w", err)
49-
}
50-
51-
return data, nil
25+
return c.allData(ctx)
5226
}
5327

5428
var err error
@@ -60,6 +34,21 @@ func (c *Connection) Next(ctx context.Context) (map[string]json.RawMessage, erro
6034
return nil, fmt.Errorf("get updated keys: %w", err)
6135
}
6236

37+
changedSlice := make(map[string]bool, len(changedKeys))
38+
for _, key := range changedKeys {
39+
var uid int
40+
if _, err := fmt.Sscanf(key, fullUpdateFormat, &uid); err == nil {
41+
// The key is a fullUpdate key. Do not use it, excpect of a full
42+
// update.
43+
if uid == c.uid {
44+
return c.allData(ctx)
45+
}
46+
continue
47+
}
48+
49+
changedSlice[key] = true
50+
}
51+
6352
oldKeys := c.kb.Keys()
6453

6554
// Update keysbuilder get new list of keys
@@ -70,11 +59,6 @@ func (c *Connection) Next(ctx context.Context) (map[string]json.RawMessage, erro
7059
// Start with keys hat are new for the user
7160
keys := keysDiff(oldKeys, c.kb.Keys())
7261

73-
changedSlice := make(map[string]bool, len(changedKeys))
74-
for _, key := range changedKeys {
75-
changedSlice[key] = true
76-
}
77-
7862
// Append keys that are old but have been changed.
7963
for _, key := range oldKeys {
8064
if !changedSlice[key] {
@@ -107,6 +91,36 @@ func (c *Connection) Next(ctx context.Context) (map[string]json.RawMessage, erro
10791
return data, nil
10892
}
10993

94+
func (c *Connection) allData(ctx context.Context) (map[string]json.RawMessage, error) {
95+
// First time called
96+
c.filter = new(filter)
97+
if c.tid == 0 {
98+
c.tid = c.autoupdate.topic.LastID()
99+
}
100+
101+
if err := c.kb.Update(ctx); err != nil {
102+
return nil, fmt.Errorf("create keys for keysbuilder: %w", err)
103+
}
104+
105+
data, err := c.autoupdate.RestrictedData(ctx, c.uid, c.kb.Keys()...)
106+
if err != nil {
107+
return nil, fmt.Errorf("get first time restricted data: %w", err)
108+
}
109+
110+
// Delete empty values in first responce.
111+
for k, v := range data {
112+
if len(v) == 0 {
113+
delete(data, k)
114+
}
115+
}
116+
117+
if err := c.filter.filter(data); err != nil {
118+
return nil, fmt.Errorf("filter data for the first time: %w", err)
119+
}
120+
121+
return data, nil
122+
}
123+
110124
func keysDiff(old []string, new []string) []string {
111125
keySet := make(map[string]bool, len(old))
112126
for _, key := range old {

internal/autoupdate/connection_test.go

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"testing"
9+
"time"
910

1011
"github.com/openslides/openslides-autoupdate-service/internal/autoupdate"
1112
"github.com/openslides/openslides-autoupdate-service/internal/test"
@@ -86,7 +87,7 @@ func TestConnectionEmptyData(t *testing.T) {
8687
closed := make(chan struct{})
8788
defer close(closed)
8889

89-
s := autoupdate.New(datastore, new(test.MockRestricter), closed)
90+
s := autoupdate.New(datastore, new(test.MockRestricter), mockUserUpdater{}, closed)
9091

9192
kb := test.KeysBuilder{K: test.Str(doesExistKey, doesNotExistKey)}
9293

@@ -189,7 +190,7 @@ func TestConnectionFilterData(t *testing.T) {
189190

190191
closed := make(chan struct{})
191192
defer close(closed)
192-
s := autoupdate.New(datastore, new(test.MockRestricter), closed)
193+
s := autoupdate.New(datastore, new(test.MockRestricter), mockUserUpdater{}, closed)
193194
kb := test.KeysBuilder{K: test.Str("user/1/name")}
194195
c := s.Connect(1, kb)
195196
if _, err := c.Next(context.Background()); err != nil {
@@ -214,7 +215,7 @@ func TestConntectionFilterOnlyOneKey(t *testing.T) {
214215
datastore := new(test.MockDatastore)
215216
closed := make(chan struct{})
216217
close(closed)
217-
s := autoupdate.New(datastore, new(test.MockRestricter), closed)
218+
s := autoupdate.New(datastore, new(test.MockRestricter), mockUserUpdater{}, closed)
218219
kb := test.KeysBuilder{K: test.Str("user/1/name")}
219220
c := s.Connect(1, kb)
220221
if _, err := c.Next(context.Background()); err != nil {
@@ -239,12 +240,102 @@ func TestConntectionFilterOnlyOneKey(t *testing.T) {
239240
}
240241
}
241242

243+
func TestFullUpdate(t *testing.T) {
244+
datastore := new(test.MockDatastore)
245+
closed := make(chan struct{})
246+
defer close(closed)
247+
userUpdater := new(mockUserUpdater)
248+
s := autoupdate.New(datastore, new(test.MockRestricter), userUpdater, closed)
249+
kb := test.KeysBuilder{K: test.Str("user/1/name")}
250+
251+
t.Run("other user", func(t *testing.T) {
252+
c := s.Connect(1, kb)
253+
if _, err := c.Next(context.Background()); err != nil {
254+
t.Errorf("c.Next() returned an error: %v", err)
255+
}
256+
257+
// send fulldata for other user
258+
userUpdater.userIDs = []int{2}
259+
datastore.Send(test.Str("some/5/data"))
260+
261+
ctx, cancel := context.WithCancel(context.Background())
262+
defer cancel()
263+
264+
var data map[string]json.RawMessage
265+
var err error
266+
isBlocking := blocking(func() {
267+
data, err = c.Next(ctx)
268+
})
269+
270+
if !isBlocking {
271+
t.Fatalf("fulldataupdate did not block")
272+
}
273+
274+
if err != nil {
275+
t.Errorf("Got unexpected error: %v", err)
276+
}
277+
278+
if len(data) != 0 {
279+
t.Errorf("Got %v, expected no key update", data)
280+
}
281+
})
282+
283+
t.Run("same user", func(t *testing.T) {
284+
c := s.Connect(1, kb)
285+
if _, err := c.Next(context.Background()); err != nil {
286+
t.Errorf("c.Next() returned an error: %v", err)
287+
}
288+
289+
// Send fulldata for same user.
290+
userUpdater.userIDs = []int{1}
291+
datastore.Send(test.Str("some/5/data"))
292+
293+
ctx, cancel := context.WithCancel(context.Background())
294+
defer cancel()
295+
296+
var data map[string]json.RawMessage
297+
var err error
298+
isBlocking := blocking(func() {
299+
data, err = c.Next(ctx)
300+
})
301+
302+
if isBlocking {
303+
t.Fatalf("fulldataupdate did block")
304+
}
305+
306+
if err != nil {
307+
t.Errorf("Got unexpected error: %v", err)
308+
}
309+
310+
if len(data) != 1 || string(data["user/1/name"]) != `"Hello World"` {
311+
t.Errorf("Got %v, expected [user/1/name: Hello World]", data)
312+
}
313+
})
314+
}
315+
316+
func blocking(f func()) bool {
317+
done := make(chan struct{})
318+
go func() {
319+
f()
320+
close(done)
321+
}()
322+
323+
timer := time.NewTimer(time.Millisecond)
324+
defer timer.Stop()
325+
select {
326+
case <-done:
327+
return false
328+
case <-timer.C:
329+
return true
330+
}
331+
}
332+
242333
func BenchmarkFilterChanging(b *testing.B) {
243334
const keyCount = 100
244335
datastore := new(test.MockDatastore)
245336
closed := make(chan struct{})
246337
defer close(closed)
247-
s := autoupdate.New(datastore, new(test.MockRestricter), closed)
338+
s := autoupdate.New(datastore, new(test.MockRestricter), mockUserUpdater{}, closed)
248339

249340
keys := make([]string, 0, keyCount)
250341
for i := 0; i < keyCount; i++ {
@@ -271,7 +362,7 @@ func BenchmarkFilterNotChanging(b *testing.B) {
271362
datastore := new(test.MockDatastore)
272363
closed := make(chan struct{})
273364
defer close(closed)
274-
s := autoupdate.New(datastore, new(test.MockRestricter), closed)
365+
s := autoupdate.New(datastore, new(test.MockRestricter), mockUserUpdater{}, closed)
275366

276367
keys := make([]string, 0, keyCount)
277368
for i := 0; i < keyCount; i++ {

internal/autoupdate/feature_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestFeatures(t *testing.T) {
6969
datastore.OnlyData = true
7070
closed := make(chan struct{})
7171
defer close(closed)
72-
s := autoupdate.New(datastore, new(test.MockRestricter), closed)
72+
s := autoupdate.New(datastore, new(test.MockRestricter), mockUserUpdater{}, closed)
7373

7474
for _, tt := range []struct {
7575
name string

internal/autoupdate/interfaces.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ type Datastore interface {
1414
// Restricter restricts keys.
1515
type Restricter interface {
1616
// Restrict manipulates the values for the user with the given id.
17-
Restrict(uid int, data map[string]json.RawMessage) error
17+
Restrict(ctx context.Context, uid int, data map[string]json.RawMessage) error
1818
}
1919

2020
// KeysBuilder holds the keys that are requested by a user.
2121
type KeysBuilder interface {
2222
Update(ctx context.Context) error
2323
Keys() []string
2424
}
25+
26+
// UserUpdater has a function to get user_ids, that should get a full update.
27+
type UserUpdater interface {
28+
AdditionalUpdate(ctx context.Context, updated map[string]json.RawMessage) ([]int, error)
29+
}

0 commit comments

Comments
 (0)