mirror of
https://github.com/jech/galene.git
synced 2024-11-22 00:25:58 +01:00
Implement message permission and shutup command.
This commit is contained in:
parent
2b145317a5
commit
1315084185
9 changed files with 85 additions and 38 deletions
2
CHANGES
2
CHANGES
|
@ -17,6 +17,8 @@ Galene 0.9 (unreleased)
|
||||||
* Implemented a contextual menu that triggers on a double click on
|
* Implemented a contextual menu that triggers on a double click on
|
||||||
a chat entry.
|
a chat entry.
|
||||||
* Added a new command "/stopshare".
|
* Added a new command "/stopshare".
|
||||||
|
* Added a new permission "message" and new commands "shutup" and
|
||||||
|
"unshutup".
|
||||||
|
|
||||||
14 April 2024: Galene 0.8.2
|
14 April 2024: Galene 0.8.2
|
||||||
|
|
||||||
|
|
2
README
2
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
|
- `users`: is a dictionary that maps user names to dictionaries with
|
||||||
entries `password` and `permissions`; `permissions` should be one of
|
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`
|
- `wildcard-user` is a dictionaries with entries `password` and `permissions`
|
||||||
that will be used for usernames with no matching entry in the `users`
|
that will be used for usernames with no matching entry in the `users`
|
||||||
dictionary;
|
dictionary;
|
||||||
|
|
|
@ -24,8 +24,9 @@ type Permissions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var permissionsMap = map[string][]string{
|
var permissionsMap = map[string][]string{
|
||||||
"op": []string{"op", "present", "token"},
|
"op": []string{"op", "present", "message", "token"},
|
||||||
"present": []string{"present"},
|
"present": []string{"present", "message"},
|
||||||
|
"message": []string{"message"},
|
||||||
"observe": []string{},
|
"observe": []string{},
|
||||||
"admin": []string{"admin"},
|
"admin": []string{"admin"},
|
||||||
}
|
}
|
||||||
|
@ -528,7 +529,7 @@ func upgradeDescription(desc *Description) error {
|
||||||
desc.Presenter = nil
|
desc.Presenter = nil
|
||||||
}
|
}
|
||||||
if desc.Other != nil {
|
if desc.Other != nil {
|
||||||
upgradeUsers(desc.Other, "observe")
|
upgradeUsers(desc.Other, "message")
|
||||||
desc.Other = nil
|
desc.Other = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,11 +63,11 @@ var descJSON = `
|
||||||
"users": {
|
"users": {
|
||||||
"jch": {"password": "topsecret", "permissions": "op"},
|
"jch": {"password": "topsecret", "permissions": "op"},
|
||||||
"john": {"password": "secret", "permissions": "present"},
|
"john": {"password": "secret", "permissions": "present"},
|
||||||
"james": {"password": "secret2", "permissions": "observe"},
|
"james": {"password": "secret2", "permissions": "message"},
|
||||||
"peter": {"password": "secret4"}
|
"peter": {"password": "secret4"}
|
||||||
},
|
},
|
||||||
"wildcard-user":
|
"wildcard-user":
|
||||||
{"permissions": "observe", "password": {"type":"wildcard"}}
|
{"permissions": "message", "password": {"type":"wildcard"}}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
func TestDescriptionJSON(t *testing.T) {
|
func TestDescriptionJSON(t *testing.T) {
|
||||||
|
@ -139,6 +139,10 @@ func TestUpgradeDescription(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v1 := range d1.Users {
|
for k, v1 := range d1.Users {
|
||||||
|
if k == "peter" {
|
||||||
|
// not representable in the old format
|
||||||
|
continue
|
||||||
|
}
|
||||||
v2 := d2.Users[k]
|
v2 := d2.Users[k]
|
||||||
if !reflect.DeepEqual(v1.Password, v2.Password) ||
|
if !reflect.DeepEqual(v1.Password, v2.Password) ||
|
||||||
!permissionsEqual(
|
!permissionsEqual(
|
||||||
|
|
|
@ -114,19 +114,19 @@ type credPerm struct {
|
||||||
var goodClients = []credPerm{
|
var goodClients = []credPerm{
|
||||||
{
|
{
|
||||||
ClientCredentials{Username: &jch, Password: "topsecret"},
|
ClientCredentials{Username: &jch, Password: "topsecret"},
|
||||||
[]string{"op", "present", "token"},
|
[]string{"op", "present", "message", "token"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ClientCredentials{Username: &john, Password: "secret"},
|
ClientCredentials{Username: &john, Password: "secret"},
|
||||||
[]string{"present"},
|
[]string{"present", "message"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ClientCredentials{Username: &james, Password: "secret2"},
|
ClientCredentials{Username: &james, Password: "secret2"},
|
||||||
[]string{},
|
[]string{"message"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ClientCredentials{Username: &paul, Password: "secret3"},
|
ClientCredentials{Username: &paul, Password: "secret3"},
|
||||||
[]string{},
|
[]string{"message"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ClientCredentials{Username: &peter, Password: "secret4"},
|
ClientCredentials{Username: &peter, Password: "secret4"},
|
||||||
|
@ -189,29 +189,29 @@ func TestExtraPermissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doit("jch", []string{"op", "token", "present"})
|
doit("jch", []string{"op", "token", "present", "message"})
|
||||||
doit("john", []string{"present"})
|
doit("john", []string{"present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
|
|
||||||
d.AllowRecording = true
|
d.AllowRecording = true
|
||||||
d.UnrestrictedTokens = false
|
d.UnrestrictedTokens = false
|
||||||
|
|
||||||
doit("jch", []string{"op", "record", "token", "present"})
|
doit("jch", []string{"op", "record", "token", "present", "message"})
|
||||||
doit("john", []string{"present"})
|
doit("john", []string{"present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
|
|
||||||
d.AllowRecording = false
|
d.AllowRecording = false
|
||||||
d.UnrestrictedTokens = true
|
d.UnrestrictedTokens = true
|
||||||
|
|
||||||
doit("jch", []string{"op", "token", "present"})
|
doit("jch", []string{"op", "token", "present", "message"})
|
||||||
doit("john", []string{"token", "present"})
|
doit("john", []string{"token", "present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
|
|
||||||
d.AllowRecording = true
|
d.AllowRecording = true
|
||||||
d.UnrestrictedTokens = true
|
d.UnrestrictedTokens = true
|
||||||
|
|
||||||
doit("jch", []string{"op", "record", "token", "present"})
|
doit("jch", []string{"op", "record", "token", "present", "message"})
|
||||||
doit("john", []string{"token", "present"})
|
doit("john", []string{"token", "present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1349,6 +1349,10 @@ func setPermissions(g *group.Group, id string, perm string) error {
|
||||||
c.permissions = addnew("present", c.permissions)
|
c.permissions = addnew("present", c.permissions)
|
||||||
case "unpresent":
|
case "unpresent":
|
||||||
c.permissions = remove("present", c.permissions)
|
c.permissions = remove("present", c.permissions)
|
||||||
|
case "shutup":
|
||||||
|
c.permissions = remove("message", c.permissions)
|
||||||
|
case "unshutup":
|
||||||
|
c.permissions = addnew("message", c.permissions)
|
||||||
default:
|
default:
|
||||||
return group.UserError("unknown permission")
|
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"))
|
return c.error(group.UserError("join a group first"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !member("message", c.permissions) {
|
||||||
|
return c.error(group.UserError("not authorised"))
|
||||||
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
if m.Type == "chat" {
|
if m.Type == "chat" {
|
||||||
|
@ -1855,7 +1863,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
||||||
return c.error(group.UserError("join a group first"))
|
return c.error(group.UserError("join a group first"))
|
||||||
}
|
}
|
||||||
switch m.Kind {
|
switch m.Kind {
|
||||||
case "op", "unop", "present", "unpresent":
|
case "op", "unop", "present", "unpresent", "shutup", "unshutup":
|
||||||
if !member("op", c.permissions) {
|
if !member("op", c.permissions) {
|
||||||
return c.error(group.UserError("not authorised"))
|
return c.error(group.UserError("not authorised"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3288,10 +3288,13 @@ function makeToken(template) {
|
||||||
v["not-before"] = template["not-before"];
|
v["not-before"] = template["not-before"];
|
||||||
if('permissions' in template)
|
if('permissions' in template)
|
||||||
v.permissions = template.permissions;
|
v.permissions = template.permissions;
|
||||||
else if(serverConnection.permissions.indexOf('present') >= 0)
|
else {
|
||||||
v.permissions = ['present'];
|
|
||||||
else
|
|
||||||
v.permissions = [];
|
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);
|
serverConnection.groupAction('maketoken', v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3520,6 +3523,20 @@ commands.unpresent = {
|
||||||
f: userCommand,
|
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 = {
|
commands.mute = {
|
||||||
parameters: 'user',
|
parameters: 'user',
|
||||||
description: 'mute a remote user',
|
description: 'mute a remote user',
|
||||||
|
|
|
@ -106,6 +106,15 @@ func (token *Stateful) Check(host, group string, username *string) (string, []st
|
||||||
return user, token.Permissions, nil
|
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.
|
// load updates the state from the corresponding file.
|
||||||
// called locked
|
// called locked
|
||||||
func (state *state) load() (string, error) {
|
func (state *state) load() (string, error) {
|
||||||
|
@ -155,6 +164,12 @@ func (state *state) load() (string, error) {
|
||||||
state.fileSize = 0
|
state.fileSize = 0
|
||||||
return "", err
|
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
|
ts[t.Token] = &t
|
||||||
}
|
}
|
||||||
state.tokens = ts
|
state.tokens = ts
|
||||||
|
|
|
@ -48,27 +48,27 @@ func TestStatefulCheck(t *testing.T) {
|
||||||
Token: "token",
|
Token: "token",
|
||||||
Group: "group",
|
Group: "group",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &future,
|
Expires: &future,
|
||||||
}
|
}
|
||||||
token2 := &Stateful{
|
token2 := &Stateful{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
Group: "group",
|
Group: "group",
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &future,
|
Expires: &future,
|
||||||
}
|
}
|
||||||
token3 := &Stateful{
|
token3 := &Stateful{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
Group: "group",
|
Group: "group",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &past,
|
Expires: &past,
|
||||||
}
|
}
|
||||||
token4 := &Stateful{
|
token4 := &Stateful{
|
||||||
Token: "token",
|
Token: "token",
|
||||||
Group: "group",
|
Group: "group",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &future,
|
Expires: &future,
|
||||||
NotBefore: &nearFuture,
|
NotBefore: &nearFuture,
|
||||||
}
|
}
|
||||||
|
@ -85,27 +85,27 @@ func TestStatefulCheck(t *testing.T) {
|
||||||
group: "group",
|
group: "group",
|
||||||
username: &user,
|
username: &user,
|
||||||
expUsername: user,
|
expUsername: user,
|
||||||
expPermissions: []string{"present"},
|
expPermissions: []string{"present", "message"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
token: token1,
|
token: token1,
|
||||||
group: "group",
|
group: "group",
|
||||||
username: &user2,
|
username: &user2,
|
||||||
expUsername: user,
|
expUsername: user,
|
||||||
expPermissions: []string{"present"},
|
expPermissions: []string{"present", "message"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
token: token1,
|
token: token1,
|
||||||
group: "group",
|
group: "group",
|
||||||
expUsername: user,
|
expUsername: user,
|
||||||
expPermissions: []string{"present"},
|
expPermissions: []string{"present", "message"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
token: token2,
|
token: token2,
|
||||||
group: "group",
|
group: "group",
|
||||||
username: &user,
|
username: &user,
|
||||||
expUsername: "",
|
expUsername: "",
|
||||||
expPermissions: []string{"present"},
|
expPermissions: []string{"present", "message"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,14 +232,14 @@ func TestTokenStorage(t *testing.T) {
|
||||||
Token: "tok1",
|
Token: "tok1",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user1,
|
Username: &user1,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &future,
|
Expires: &future,
|
||||||
},
|
},
|
||||||
&Stateful{
|
&Stateful{
|
||||||
Token: "tok2",
|
Token: "tok2",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user2,
|
Username: &user2,
|
||||||
Permissions: []string{"present", "record"},
|
Permissions: []string{"present", "record", "message"},
|
||||||
Expires: &nearFuture,
|
Expires: &nearFuture,
|
||||||
NotBefore: &past,
|
NotBefore: &past,
|
||||||
},
|
},
|
||||||
|
@ -247,7 +247,7 @@ func TestTokenStorage(t *testing.T) {
|
||||||
Token: "tok3",
|
Token: "tok3",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user3,
|
Username: &user3,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &nearFuture,
|
Expires: &nearFuture,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -327,35 +327,35 @@ func TestExpire(t *testing.T) {
|
||||||
Token: "tok1",
|
Token: "tok1",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &now,
|
Expires: &now,
|
||||||
},
|
},
|
||||||
&Stateful{
|
&Stateful{
|
||||||
Token: "tok2",
|
Token: "tok2",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &future,
|
Expires: &future,
|
||||||
},
|
},
|
||||||
&Stateful{
|
&Stateful{
|
||||||
Token: "tok3",
|
Token: "tok3",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &now,
|
Expires: &now,
|
||||||
},
|
},
|
||||||
&Stateful{
|
&Stateful{
|
||||||
Token: "tok4",
|
Token: "tok4",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &past,
|
Expires: &past,
|
||||||
},
|
},
|
||||||
&Stateful{
|
&Stateful{
|
||||||
Token: "tok5",
|
Token: "tok5",
|
||||||
Group: "test",
|
Group: "test",
|
||||||
Username: &user,
|
Username: &user,
|
||||||
Permissions: []string{"present"},
|
Permissions: []string{"present", "message"},
|
||||||
Expires: &longPast,
|
Expires: &longPast,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue