diff --git a/src/ACL.cpp b/src/ACL.cpp index ec780715c41..7971041ebb8 100644 --- a/src/ACL.cpp +++ b/src/ACL.cpp @@ -99,6 +99,8 @@ bool ChanACL::hasPermission(ServerUser *p, Channel *chan, QFlags< Perm > perm, A return ((granted & perm) != None); } +# include + // Return effective permissions. QFlags< ChanACL::Perm > ChanACL::effectivePermissions(ServerUser *p, Channel *chan, ACLCache *cache) { // Superuser @@ -141,36 +143,67 @@ QFlags< ChanACL::Perm > ChanACL::effectivePermissions(ServerUser *p, Channel *ch bool write = false; ChanACL *acl; + // Iterate over all parent channels including the channel the user is in while (!chanstack.isEmpty()) { ch = chanstack.pop(); - if (!ch->bInheritACL) + if (!ch->bInheritACL) { granted = def; + } foreach (acl, ch->qlACL) { bool matchUser = (acl->iUserId != -1) && (acl->iUserId == p->iId); bool matchGroup = Group::appliesToUser(*chan, *ch, acl->qsGroup, *p); + + bool applyFromSelf = (ch == chan && acl->bApplyHere); + bool applyFromParent = (ch != chan && acl->bApplySubs); + bool applyFromAny = applyFromSelf || applyFromParent; + + // Denying traverse is special, because denying it for any parent channel + // implicitly means all sub channels are entirely inaccessible as well. + // However, we still want to make sure that unchecking "applies to + // this channel" for the parent channel is honored. + bool applyDenyTraverse = applyFromParent || acl->bApplyHere; + if (matchUser || matchGroup) { - if (acl->pAllow & Traverse) + if (applyFromAny && acl->pAllow & Traverse) { traverse = true; - if (acl->pDeny & Traverse) + } + if (applyDenyTraverse && acl->pDeny & Traverse) { traverse = false; - if (acl->pAllow & Write) + } + + if (applyFromAny && acl->pAllow & Write) { write = true; - if (acl->pDeny & Write) + qDebug() << "Write true from " << ch->iID; + } + if (applyFromAny && acl->pDeny & Write) { write = false; - if (ch->iId == 0 && chan == ch && acl->bApplyHere) { - if (acl->pAllow & Kick) + qDebug() << "Write false from " << ch->iID; + } + + // These permissions are only grantable from the root channel + // as they affect the users globally. For example: You can not + // kick a client from a channel without kicking them from the server. + if (ch->iId == 0 && applyFromSelf) { + if (acl->pAllow & Kick) { granted |= Kick; - if (acl->pAllow & Ban) + } + if (acl->pAllow & Ban) { granted |= Ban; - if (acl->pAllow & ResetUserContent) + } + if (acl->pAllow & ResetUserContent) { granted |= ResetUserContent; - if (acl->pAllow & Register) + } + if (acl->pAllow & Register) { granted |= Register; - if (acl->pAllow & SelfRegister) + } + if (acl->pAllow & SelfRegister) { granted |= SelfRegister; + } } - if ((ch == chan && acl->bApplyHere) || (ch != chan && acl->bApplySubs)) { + + // Every other regular ACL is handled here + if (applyFromAny) { granted |= (acl->pAllow & ~(Kick | Ban | ResetUserContent | Register | SelfRegister | Cached)); granted &= ~acl->pDeny; }