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
|
||||
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
|
||||
|
||||
|
|
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
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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{})
|
||||
}
|
||||
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue