Implement chanroles, as discussed with nenolod.

The theory behind this is that services sends an ENCAP * GRANT #channel
UID :+flagspec message specifying the chanroles the user has. They are
mapped into flag bits and applied to the membership of the user. They
then are restricted or permitted to what they can do based on the
permissions mask regardless of rank.

For backwards compatibility, the default permission bit (without a GRANT
statement) allows a user to to anything an existing op can do ONLY if
they are an op.

Todo: make CHANROLE_STATUS work (the ability to apply +ov to people),
which is at the moment controlled by CHANROLE_MODE.
This commit is contained in:
Elizabeth Jennifer Myers 2011-07-06 13:35:11 -04:00
parent 0351022738
commit 8aabb973c0
8 changed files with 148 additions and 9 deletions

View file

@ -60,7 +60,7 @@ struct Channel
rb_dlink_list members; /* channel members */ rb_dlink_list members; /* channel members */
rb_dlink_list locmembers; /* local channel members */ rb_dlink_list locmembers; /* local channel members */
rb_dlink_list invites; rb_dlink_list invites;
rb_dlink_list banlist; rb_dlink_list banlist;
rb_dlink_list exceptlist; rb_dlink_list exceptlist;
rb_dlink_list invexlist; rb_dlink_list invexlist;
@ -87,6 +87,7 @@ struct membership
struct Channel *chptr; struct Channel *chptr;
struct Client *client_p; struct Client *client_p;
unsigned int flags; unsigned int flags;
unsigned int roles;
unsigned long bants; unsigned long bants;
}; };
@ -187,11 +188,26 @@ typedef int (*ExtbanFunc)(const char *data, struct Client *client_p,
#define MODE_ADD 1 #define MODE_ADD 1
#define MODE_DEL -1 #define MODE_DEL -1
/* Channel roles */
#define CHANROLE_NONE 0x000
#define CHANROLE_UNSET 0x001 /* Special value */
#define CHANROLE_KICK 0x002 /* Can kick */
#define CHANROLE_STATUS 0x004 /* Can change status modes */
#define CHANROLE_GRANT 0x008 /* Can grant (unused atm) */
#define CHANROLE_MODE 0x010 /* Can change modes */
#define CHANROLE_TOPIC 0x020 /* Can change topic */
#define SecretChannel(x) ((x) && ((x)->mode.mode & MODE_SECRET)) #define SecretChannel(x) ((x) && ((x)->mode.mode & MODE_SECRET))
#define HiddenChannel(x) ((x) && ((x)->mode.mode & MODE_PRIVATE)) #define HiddenChannel(x) ((x) && ((x)->mode.mode & MODE_PRIVATE))
#define PubChannel(x) ((!x) || ((x)->mode.mode &\ #define PubChannel(x) ((!x) || ((x)->mode.mode &\
(MODE_PRIVATE | MODE_SECRET)) == 0) (MODE_PRIVATE | MODE_SECRET)) == 0)
#define HasChanRole(m, r) (((m)->roles & r) != 0)
#define SetChanRole(m, r) ((m)->roles |= r)
#define RemoveChanRole(m, r) ((m)->roles &= ~r)
#define IsChanRoleSet(m, r)
/* channel visible */ /* channel visible */
#define ShowChannel(v,c) (PubChannel(c) || IsMember((v),(c))) #define ShowChannel(v,c) (PubChannel(c) || IsMember((v),(c)))
@ -279,7 +295,7 @@ extern int match_extban(const char *banstr, struct Client *client_p, struct Chan
extern int valid_extban(const char *banstr, struct Client *client_p, struct Channel *chptr, long mode_type); extern int valid_extban(const char *banstr, struct Client *client_p, struct Channel *chptr, long mode_type);
const char * get_extban_string(void); const char * get_extban_string(void);
extern int get_channel_access(struct Client *source_p, struct membership *msptr); extern int get_channel_access(struct Client *source_p, struct membership *msptr, int role);
extern void send_channel_join(struct Channel *chptr, struct Client *client_p); extern void send_channel_join(struct Channel *chptr, struct Client *client_p);

View file

@ -65,6 +65,7 @@ TSRCS = \
m_dline.c \ m_dline.c \
m_encap.c \ m_encap.c \
m_etrace.c \ m_etrace.c \
m_grant.c \
m_help.c \ m_help.c \
m_info.c \ m_info.c \
m_invite.c \ m_invite.c \

View file

@ -97,7 +97,7 @@ m_kick(struct Client *client_p, struct Client *source_p, int parc, const char *p
return 0; return 0;
} }
if(get_channel_access(source_p, msptr) < CHFL_CHANOP) if(get_channel_access(source_p, msptr, CHANROLE_KICK) < CHFL_CHANOP)
{ {
if(MyConnect(source_p)) if(MyConnect(source_p))
{ {

102
modules/m_grant.c Normal file
View file

@ -0,0 +1,102 @@
/*
* charybdis: an advanced ircd
* m_grant: handle services grant commands
*/
#include "stdinc.h"
#include "client.h"
#include "ircd.h"
#include "numeric.h"
#include "s_serv.h"
#include "send.h"
#include "msg.h"
#include "parse.h"
#include "modules.h"
#include "s_conf.h"
#include "hash.h"
struct flag_list
{
char *name;
int flag;
};
static struct flag_list flaglist[] = {
{"kick", CHANROLE_KICK},
{"grant", CHANROLE_GRANT},
{"mode", CHANROLE_MODE},
{"topic", CHANROLE_TOPIC},
{"status", CHANROLE_STATUS},
{NULL, 0},
};
static int me_grant(struct Client *, struct Client *, int, const char **);
struct Message grant_msgtab = {
"GRANT", 0, 0, 0, MFLG_SLOW,
{mg_ignore, mg_ignore, mg_ignore, mg_ignore, {me_grant, 4}, mg_ignore}
};
mapi_clist_av1 grant_clist[] = { &grant_msgtab, NULL };
DECLARE_MODULE_AV1(grant, NULL, NULL, grant_clist, NULL, NULL, "Charybdis development team");
/*
* me_grant
*
* parv[1] = channel
* parv[2] = target UID
* parv[3] = flag spec
*/
static int
me_grant(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
{
struct Channel *chptr;
struct Client *target_p;
struct membership *msptr;
char *s, *t, *p;
if (!(chptr = find_channel(parv[1])))
return 0;
if (!(target_p = find_person(parv[2])))
return 0;
/* Makes no sense to do this for non-local users */
if(!MyClient(target_p))
return 0;
if (!(msptr = find_channel_membership(chptr, target_p)))
return 0;
/* Unset */
RemoveChanRole(msptr, CHANROLE_UNSET);
t = LOCAL_COPY(parv[3]);
for (s = rb_strtok_r(t, " ", &p); s; s = rb_strtok_r(NULL, " ", &p))
{
const char *priv = s + 1; /* The actual priv */
struct flag_list *fl;
int flag = 0;
/* Go through the flags list... */
for(fl = flaglist; fl->name != NULL; fl++)
{
if (strcasecmp(fl->name, priv) == 0)
{
flag = fl->flag;
break;
}
}
/* Ack no flag! */
if (!flag)
continue;
if (s[0] == '-')
RemoveChanRole(msptr, flag);
else if (s[0] == '+')
SetChanRole(msptr, flag);
}
return 0;
}

View file

@ -118,7 +118,7 @@ m_topic(struct Client *client_p, struct Client *source_p, int parc, const char *
} }
if(((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || if(((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
get_channel_access(source_p, msptr) >= CHFL_CHANOP) && get_channel_access(source_p, msptr, CHANROLE_TOPIC) >= CHFL_CHANOP) &&
(!MyClient(source_p) || (!MyClient(source_p) ||
can_send(chptr, source_p, msptr))) can_send(chptr, source_p, msptr)))
{ {

View file

@ -236,6 +236,9 @@ add_user_to_channel(struct Channel *chptr, struct Client *client_p, int flags)
msptr->client_p = client_p; msptr->client_p = client_p;
msptr->flags = flags; msptr->flags = flags;
/* Default to no chanroles until services says we're something else */
msptr->roles = CHANROLE_UNSET;
rb_dlinkAdd(msptr, &msptr->usernode, &client_p->user->channel); rb_dlinkAdd(msptr, &msptr->usernode, &client_p->user->channel);
rb_dlinkAdd(msptr, &msptr->channode, &chptr->members); rb_dlinkAdd(msptr, &msptr->channode, &chptr->members);

View file

@ -187,7 +187,7 @@ cflag_orphan(char c_)
} }
int int
get_channel_access(struct Client *source_p, struct membership *msptr) get_channel_access(struct Client *source_p, struct membership *msptr, int role)
{ {
hook_data_channel_approval moduledata; hook_data_channel_approval moduledata;
@ -201,7 +201,24 @@ get_channel_access(struct Client *source_p, struct membership *msptr)
moduledata.chptr = msptr->chptr; moduledata.chptr = msptr->chptr;
moduledata.msptr = msptr; moduledata.msptr = msptr;
moduledata.target = NULL; moduledata.target = NULL;
moduledata.approved = is_chanop(msptr) ? CHFL_CHANOP : CHFL_PEON;
if (is_chanop(msptr))
{
/* Their chanrole is unset by GRANT, for backwards compat let them pass */
if(HasChanRole(msptr, CHANROLE_UNSET))
{
moduledata.approved = CHFL_CHANOP;
goto finish_access;
}
}
/* Check if they have the proper role */
if(HasChanRole(msptr, role))
moduledata.approved = CHFL_CHANOP;
else
moduledata.approved = CHFL_PEON;
finish_access:
call_hook(h_get_channel_access, &moduledata); call_hook(h_get_channel_access, &moduledata);
@ -1200,7 +1217,7 @@ chm_forward(struct Client *source_p, struct Channel *chptr,
if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET)) if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET))
{ {
if((msptr = find_channel_membership(targptr, source_p)) == NULL || if((msptr = find_channel_membership(targptr, source_p)) == NULL ||
get_channel_access(source_p, msptr) != CHFL_CHANOP) get_channel_access(source_p, msptr, CHANROLE_MODE) != CHFL_CHANOP)
{ {
sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
me.name, source_p->name, targptr->chname); me.name, source_p->name, targptr->chname);
@ -1618,7 +1635,7 @@ set_channel_mode(struct Client *client_p, struct Client *source_p,
mode_limit = 0; mode_limit = 0;
mode_limit_simple = 0; mode_limit_simple = 0;
alevel = get_channel_access(source_p, msptr); alevel = get_channel_access(source_p, msptr, CHANROLE_MODE);
/* Hide connecting server on netburst -- jilles */ /* Hide connecting server on netburst -- jilles */
if (ConfigServerHide.flatten_links && IsServer(source_p) && !has_id(source_p) && !HasSentEob(source_p)) if (ConfigServerHide.flatten_links && IsServer(source_p) && !has_id(source_p) && !HasSentEob(source_p))

View file

@ -507,7 +507,7 @@ static const char * replies[] = {
/* 479 ERR_BADCHANNAME */ "%s :Illegal channel name", /* 479 ERR_BADCHANNAME */ "%s :Illegal channel name",
/* 480 ERR_THROTTLE */ ":%s 480 %s %s :Cannot join channel (+j) - throttle exceeded, try again later", /* 480 ERR_THROTTLE */ ":%s 480 %s %s :Cannot join channel (+j) - throttle exceeded, try again later",
/* 481 ERR_NOPRIVILEGES, */ ":Permission Denied - You're not an IRC operator", /* 481 ERR_NOPRIVILEGES, */ ":Permission Denied - You're not an IRC operator",
/* 482 ERR_CHANOPRIVSNEEDED, */ ":%s 482 %s %s :You're not a channel operator", /* 482 ERR_CHANOPRIVSNEEDED, */ ":%s 482 %s %s :You're not a channel operator or have no privilege",
/* 483 ERR_CANTKILLSERVER, */ ":You can't kill a server!", /* 483 ERR_CANTKILLSERVER, */ ":You can't kill a server!",
/* 484 ERR_ISCHANSERVICE */ ":%s 484 %s %s %s :Cannot kick or deop a network service", /* 484 ERR_ISCHANSERVICE */ ":%s 484 %s %s %s :Cannot kick or deop a network service",
/* 485 ERR_BANNEDNICK, */ NULL, /* 485 ERR_BANNEDNICK, */ NULL,