groupserv: gate channel access on the c flag, document !group
All checks were successful
CI / check (push) Successful in 3m43s
All checks were successful
CI / check (push) Successful in 3m43s
This commit is contained in:
parent
b87a0c56a2
commit
96654b5e5a
6 changed files with 23 additions and 14 deletions
|
|
@ -478,9 +478,15 @@ impl Db {
|
|||
}
|
||||
|
||||
/// Whether an account is in a group (its founder or a member).
|
||||
pub fn is_group_member(&self, name: &str, account: &str) -> bool {
|
||||
// Whether `account` inherits a group's channel access (when the group is on a
|
||||
// channel's access list): the group's founder, or a member holding the 'c'
|
||||
// (channel-access) flag. Plain membership is not enough — the flag gates it.
|
||||
fn group_grants_channel_access(&self, name: &str, account: &str) -> bool {
|
||||
let k = key(name);
|
||||
self.net.groups.iter().find(|g| key(&g.name) == k).is_some_and(|g| g.founder.eq_ignore_ascii_case(account) || g.members.iter().any(|m| m.account.eq_ignore_ascii_case(account)))
|
||||
self.net.groups.iter().find(|g| key(&g.name) == k).is_some_and(|g| {
|
||||
g.founder.eq_ignore_ascii_case(account)
|
||||
|| g.members.iter().any(|m| m.account.eq_ignore_ascii_case(account) && m.flags.contains('c'))
|
||||
})
|
||||
}
|
||||
|
||||
/// An account's effective channel capabilities, combining its direct access
|
||||
|
|
@ -494,7 +500,7 @@ impl Db {
|
|||
let mut caps = Caps::default();
|
||||
for a in &c.access {
|
||||
let applies = match a.account.strip_prefix('!') {
|
||||
Some(g) => self.is_group_member(&format!("!{g}"), account),
|
||||
Some(g) => self.group_grants_channel_access(&format!("!{g}"), account),
|
||||
None => a.account.eq_ignore_ascii_case(account),
|
||||
};
|
||||
if applies {
|
||||
|
|
|
|||
|
|
@ -751,14 +751,14 @@
|
|||
// Sanity: every reference is in place.
|
||||
assert_eq!(db.channel("#foo").unwrap().join_mode("alice"), Some("+o"), "alice starts as an op");
|
||||
assert_eq!(db.channel_successor("#bar").as_deref(), Some("alice"));
|
||||
assert!(db.is_group_member("!bobgrp", "alice"));
|
||||
assert!(db.group("!bobgrp").unwrap().members.iter().any(|m| m.account.eq_ignore_ascii_case("alice")));
|
||||
assert!(db.group("!alicegrp").is_some());
|
||||
|
||||
// Dropping alice erases all of them.
|
||||
assert!(db.drop_account("alice").unwrap());
|
||||
assert_eq!(db.channel("#foo").unwrap().join_mode("alice"), None, "access grant purged");
|
||||
assert_eq!(db.channel_successor("#bar"), None, "successorship purged");
|
||||
assert!(!db.is_group_member("!bobgrp", "alice"), "group membership purged");
|
||||
assert!(!db.group("!bobgrp").unwrap().members.iter().any(|m| m.account.eq_ignore_ascii_case("alice")), "group membership purged");
|
||||
assert!(db.group("!alicegrp").is_none(), "founderless group dropped");
|
||||
|
||||
// Re-registering the name must not inherit the old op access.
|
||||
|
|
|
|||
|
|
@ -4438,7 +4438,7 @@
|
|||
}
|
||||
|
||||
// GroupServ interconnection: a channel grants access to a !group, and every
|
||||
// group member inherits that channel access (auto-op on join).
|
||||
// group member holding the c (channel-access) flag inherits it (auto-op on join).
|
||||
#[test]
|
||||
fn groupserv_grants_channel_access() {
|
||||
use echo_chanserv::ChanServ;
|
||||
|
|
@ -4474,8 +4474,11 @@
|
|||
assert!(has(&gs(&mut e, "000AAAAAA", "ADD !team bob"), "Added"), "bob added");
|
||||
assert!(has(&cs(&mut e, "000AAAAAA", "FLAGS #room !team +o"), "now holds"), "group granted channel op");
|
||||
|
||||
// bob, a group member, is auto-opped on join; carol (not in the group) isn't.
|
||||
assert!(opped(&e.handle(NetEvent::Join { uid: "000AAAAAB".into(), channel: "#room".into(), op: false }), "000AAAAAB"), "group member auto-opped");
|
||||
// A member without the c (channel-access) flag does NOT inherit; +c makes
|
||||
// bob auto-opped on join. carol (not in the group) never is.
|
||||
assert!(!opped(&e.handle(NetEvent::Join { uid: "000AAAAAB".into(), channel: "#room".into(), op: false }), "000AAAAAB"), "no c flag, no access");
|
||||
gs(&mut e, "000AAAAAA", "FLAGS !team bob +c");
|
||||
assert!(opped(&e.handle(NetEvent::Join { uid: "000AAAAAB".into(), channel: "#room".into(), op: false }), "000AAAAAB"), "c flag -> auto-opped");
|
||||
assert!(!opped(&e.handle(NetEvent::Join { uid: "000AAAAAC".into(), channel: "#room".into(), op: false }), "000AAAAAC"), "non-member not opped");
|
||||
|
||||
// Removing bob from the group revokes his inherited channel access.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue