diff --git a/CHANGES b/CHANGES index 9780705..edc0d00 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,8 @@ Galene 0.9 (unreleased) * Implemented a contextual menu that triggers on a double click on a chat entry. * Added a new command "/stopshare". + * Added a new permission "message" and new commands "shutup" and + "unshutup". 14 April 2024: Galene 0.8.2 diff --git a/README b/README index b985342..59836f4 100644 --- a/README +++ b/README @@ -162,7 +162,7 @@ nobody will be able to join the group. The following fields are allowed: - `users`: is a dictionary that maps user names to dictionaries with entries `password` and `permissions`; `permissions` should be one of - `op`, `present` or `passive`; + `op`, `present`, `message` or `observe`. - `wildcard-user` is a dictionaries with entries `password` and `permissions` that will be used for usernames with no matching entry in the `users` dictionary; diff --git a/group/description.go b/group/description.go index 0ecb20e..91b0a97 100644 --- a/group/description.go +++ b/group/description.go @@ -24,8 +24,9 @@ type Permissions struct { } var permissionsMap = map[string][]string{ - "op": []string{"op", "present", "token"}, - "present": []string{"present"}, + "op": []string{"op", "present", "message", "token"}, + "present": []string{"present", "message"}, + "message": []string{"message"}, "observe": []string{}, "admin": []string{"admin"}, } @@ -528,7 +529,7 @@ func upgradeDescription(desc *Description) error { desc.Presenter = nil } if desc.Other != nil { - upgradeUsers(desc.Other, "observe") + upgradeUsers(desc.Other, "message") desc.Other = nil } diff --git a/group/description_test.go b/group/description_test.go index 4f7e0f6..00bd09c 100644 --- a/group/description_test.go +++ b/group/description_test.go @@ -63,11 +63,11 @@ var descJSON = ` "users": { "jch": {"password": "topsecret", "permissions": "op"}, "john": {"password": "secret", "permissions": "present"}, - "james": {"password": "secret2", "permissions": "observe"}, + "james": {"password": "secret2", "permissions": "message"}, "peter": {"password": "secret4"} }, "wildcard-user": - {"permissions": "observe", "password": {"type":"wildcard"}} + {"permissions": "message", "password": {"type":"wildcard"}} }` func TestDescriptionJSON(t *testing.T) { @@ -139,6 +139,10 @@ func TestUpgradeDescription(t *testing.T) { } for k, v1 := range d1.Users { + if k == "peter" { + // not representable in the old format + continue + } v2 := d2.Users[k] if !reflect.DeepEqual(v1.Password, v2.Password) || !permissionsEqual( diff --git a/group/group_test.go b/group/group_test.go index 5c5afc6..977fa19 100644 --- a/group/group_test.go +++ b/group/group_test.go @@ -114,19 +114,19 @@ type credPerm struct { var goodClients = []credPerm{ { ClientCredentials{Username: &jch, Password: "topsecret"}, - []string{"op", "present", "token"}, + []string{"op", "present", "message", "token"}, }, { ClientCredentials{Username: &john, Password: "secret"}, - []string{"present"}, + []string{"present", "message"}, }, { ClientCredentials{Username: &james, Password: "secret2"}, - []string{}, + []string{"message"}, }, { ClientCredentials{Username: &paul, Password: "secret3"}, - []string{}, + []string{"message"}, }, { ClientCredentials{Username: &peter, Password: "secret4"}, @@ -189,29 +189,29 @@ func TestExtraPermissions(t *testing.T) { } } - doit("jch", []string{"op", "token", "present"}) - doit("john", []string{"present"}) + doit("jch", []string{"op", "token", "present", "message"}) + doit("john", []string{"present", "message"}) doit("james", []string{}) d.AllowRecording = true d.UnrestrictedTokens = false - doit("jch", []string{"op", "record", "token", "present"}) - doit("john", []string{"present"}) + doit("jch", []string{"op", "record", "token", "present", "message"}) + doit("john", []string{"present", "message"}) doit("james", []string{}) d.AllowRecording = false d.UnrestrictedTokens = true - doit("jch", []string{"op", "token", "present"}) - doit("john", []string{"token", "present"}) + doit("jch", []string{"op", "token", "present", "message"}) + doit("john", []string{"token", "present", "message"}) doit("james", []string{}) d.AllowRecording = true d.UnrestrictedTokens = true - doit("jch", []string{"op", "record", "token", "present"}) - doit("john", []string{"token", "present"}) + doit("jch", []string{"op", "record", "token", "present", "message"}) + doit("john", []string{"token", "present", "message"}) doit("james", []string{}) } diff --git a/rtpconn/webclient.go b/rtpconn/webclient.go index d0cbf43..12fa330 100644 --- a/rtpconn/webclient.go +++ b/rtpconn/webclient.go @@ -1349,6 +1349,10 @@ func setPermissions(g *group.Group, id string, perm string) error { c.permissions = addnew("present", c.permissions) case "unpresent": c.permissions = remove("present", c.permissions) + case "shutup": + c.permissions = remove("message", c.permissions) + case "unshutup": + c.permissions = addnew("message", c.permissions) default: return group.UserError("unknown permission") } @@ -1570,6 +1574,10 @@ func handleClientMessage(c *webClient, m clientMessage) error { return c.error(group.UserError("join a group first")) } + if !member("message", c.permissions) { + return c.error(group.UserError("not authorised")) + } + now := time.Now() if m.Type == "chat" { @@ -1855,7 +1863,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { return c.error(group.UserError("join a group first")) } switch m.Kind { - case "op", "unop", "present", "unpresent": + case "op", "unop", "present", "unpresent", "shutup", "unshutup": if !member("op", c.permissions) { return c.error(group.UserError("not authorised")) } diff --git a/static/galene.js b/static/galene.js index a46625c..4230819 100644 --- a/static/galene.js +++ b/static/galene.js @@ -3288,10 +3288,13 @@ function makeToken(template) { v["not-before"] = template["not-before"]; if('permissions' in template) v.permissions = template.permissions; - else if(serverConnection.permissions.indexOf('present') >= 0) - v.permissions = ['present']; - else + else { v.permissions = []; + if(serverConnection.permissions.indexOf('present') >= 0) + v.permissions.push('present'); + if(serverConnection.permissions.indexOf('message') >= 0) + v.permissions.push('message'); + } serverConnection.groupAction('maketoken', v); } @@ -3520,6 +3523,20 @@ commands.unpresent = { f: userCommand, }; +commands.shutup = { + parameters: 'user', + description: 'revoke the right to send chat messages', + predicate: operatorPredicate, + f: userCommand, +}; + +commands.unshutup = { + parameters: 'user', + description: 'give the right to send chat messages', + predicate: operatorPredicate, + f: userCommand, +}; + commands.mute = { parameters: 'user', description: 'mute a remote user', diff --git a/token/stateful.go b/token/stateful.go index 102e535..70339d4 100644 --- a/token/stateful.go +++ b/token/stateful.go @@ -106,6 +106,15 @@ func (token *Stateful) Check(host, group string, username *string) (string, []st return user, token.Permissions, nil } +func member(v string, l []string) bool { + for _, w := range l { + if v == w { + return true + } + } + return false +} + // load updates the state from the corresponding file. // called locked func (state *state) load() (string, error) { @@ -155,6 +164,12 @@ func (state *state) load() (string, error) { state.fileSize = 0 return "", err } + // the "message" permission was introduced in Galene 0.9, + // so add it to tokens read from disk. We can remove this + // hack in late 2024. + if !member("message", t.Permissions) { + t.Permissions = append(t.Permissions, "message") + } ts[t.Token] = &t } state.tokens = ts diff --git a/token/stateful_test.go b/token/stateful_test.go index cce829e..d74407a 100644 --- a/token/stateful_test.go +++ b/token/stateful_test.go @@ -48,27 +48,27 @@ func TestStatefulCheck(t *testing.T) { Token: "token", Group: "group", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &future, } token2 := &Stateful{ Token: "token", Group: "group", - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &future, } token3 := &Stateful{ Token: "token", Group: "group", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &past, } token4 := &Stateful{ Token: "token", Group: "group", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &future, NotBefore: &nearFuture, } @@ -85,27 +85,27 @@ func TestStatefulCheck(t *testing.T) { group: "group", username: &user, expUsername: user, - expPermissions: []string{"present"}, + expPermissions: []string{"present", "message"}, }, { token: token1, group: "group", username: &user2, expUsername: user, - expPermissions: []string{"present"}, + expPermissions: []string{"present", "message"}, }, { token: token1, group: "group", expUsername: user, - expPermissions: []string{"present"}, + expPermissions: []string{"present", "message"}, }, { token: token2, group: "group", username: &user, expUsername: "", - expPermissions: []string{"present"}, + expPermissions: []string{"present", "message"}, }, } @@ -232,14 +232,14 @@ func TestTokenStorage(t *testing.T) { Token: "tok1", Group: "test", Username: &user1, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &future, }, &Stateful{ Token: "tok2", Group: "test", Username: &user2, - Permissions: []string{"present", "record"}, + Permissions: []string{"present", "record", "message"}, Expires: &nearFuture, NotBefore: &past, }, @@ -247,7 +247,7 @@ func TestTokenStorage(t *testing.T) { Token: "tok3", Group: "test", Username: &user3, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &nearFuture, }, } @@ -327,35 +327,35 @@ func TestExpire(t *testing.T) { Token: "tok1", Group: "test", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &now, }, &Stateful{ Token: "tok2", Group: "test", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &future, }, &Stateful{ Token: "tok3", Group: "test", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &now, }, &Stateful{ Token: "tok4", Group: "test", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &past, }, &Stateful{ Token: "tok5", Group: "test", Username: &user, - Permissions: []string{"present"}, + Permissions: []string{"present", "message"}, Expires: &longPast, }, }