status-im/status-go

View on GitHub
protocol/communities/roles_authorization.go

Summary

Maintainability
A
0 mins
Test Coverage
A
93%
package communities

import (
    "golang.org/x/exp/slices"

    "github.com/status-im/status-go/protocol/protobuf"
)

var adminAuthorizedEventTypes = []protobuf.CommunityEvent_EventType{
    protobuf.CommunityEvent_COMMUNITY_EDIT,
    protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE,
    protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE,
    protobuf.CommunityEvent_COMMUNITY_CATEGORY_CREATE,
    protobuf.CommunityEvent_COMMUNITY_CATEGORY_DELETE,
    protobuf.CommunityEvent_COMMUNITY_CATEGORY_EDIT,
    protobuf.CommunityEvent_COMMUNITY_CHANNEL_CREATE,
    protobuf.CommunityEvent_COMMUNITY_CHANNEL_DELETE,
    protobuf.CommunityEvent_COMMUNITY_CHANNEL_EDIT,
    protobuf.CommunityEvent_COMMUNITY_CATEGORY_REORDER,
    protobuf.CommunityEvent_COMMUNITY_CHANNEL_REORDER,
    protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_ACCEPT,
    protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_REJECT,
    protobuf.CommunityEvent_COMMUNITY_MEMBER_KICK,
    protobuf.CommunityEvent_COMMUNITY_MEMBER_BAN,
    protobuf.CommunityEvent_COMMUNITY_MEMBER_UNBAN,
    protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES,
}

var tokenMasterAuthorizedEventTypes = append(adminAuthorizedEventTypes, []protobuf.CommunityEvent_EventType{
    protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD,
}...)

var ownerAuthorizedEventTypes = tokenMasterAuthorizedEventTypes

var rolesToAuthorizedEventTypes = map[protobuf.CommunityMember_Roles][]protobuf.CommunityEvent_EventType{
    protobuf.CommunityMember_ROLE_NONE:         []protobuf.CommunityEvent_EventType{},
    protobuf.CommunityMember_ROLE_OWNER:        ownerAuthorizedEventTypes,
    protobuf.CommunityMember_ROLE_ADMIN:        adminAuthorizedEventTypes,
    protobuf.CommunityMember_ROLE_TOKEN_MASTER: tokenMasterAuthorizedEventTypes,
}

var adminAuthorizedPermissionTypes = []protobuf.CommunityTokenPermission_Type{
    protobuf.CommunityTokenPermission_BECOME_MEMBER,
    protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
    protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL,
}

var tokenMasterAuthorizedPermissionTypes = append(adminAuthorizedPermissionTypes, []protobuf.CommunityTokenPermission_Type{}...)

var ownerAuthorizedPermissionTypes = append(tokenMasterAuthorizedPermissionTypes, []protobuf.CommunityTokenPermission_Type{
    protobuf.CommunityTokenPermission_BECOME_ADMIN,
    protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
}...)

var rolesToAuthorizedPermissionTypes = map[protobuf.CommunityMember_Roles][]protobuf.CommunityTokenPermission_Type{
    protobuf.CommunityMember_ROLE_NONE:         []protobuf.CommunityTokenPermission_Type{},
    protobuf.CommunityMember_ROLE_OWNER:        ownerAuthorizedPermissionTypes,
    protobuf.CommunityMember_ROLE_ADMIN:        adminAuthorizedPermissionTypes,
    protobuf.CommunityMember_ROLE_TOKEN_MASTER: tokenMasterAuthorizedPermissionTypes,
}

func canRolesPerformEvent(roles []protobuf.CommunityMember_Roles, eventType protobuf.CommunityEvent_EventType) bool {
    for _, role := range roles {
        if slices.Contains(rolesToAuthorizedEventTypes[role], eventType) {
            return true
        }
    }
    return false
}

func canRolesModifyPermission(roles []protobuf.CommunityMember_Roles, permissionType protobuf.CommunityTokenPermission_Type) bool {
    for _, role := range roles {
        if slices.Contains(rolesToAuthorizedPermissionTypes[role], permissionType) {
            return true
        }
    }
    return false
}

func canRolesKickOrBanMember(senderRoles []protobuf.CommunityMember_Roles, memberRoles []protobuf.CommunityMember_Roles) bool {
    // Owner can kick everyone
    if slices.Contains(senderRoles, protobuf.CommunityMember_ROLE_OWNER) {
        return true
    }

    // TokenMaster can kick normal members and admins
    if (slices.Contains(senderRoles, protobuf.CommunityMember_ROLE_TOKEN_MASTER)) &&
        !(slices.Contains(memberRoles, protobuf.CommunityMember_ROLE_TOKEN_MASTER) ||
            slices.Contains(memberRoles, protobuf.CommunityMember_ROLE_OWNER)) {
        return true
    }

    // Admins can kick normal members
    if (slices.Contains(senderRoles, protobuf.CommunityMember_ROLE_ADMIN)) &&
        !(slices.Contains(memberRoles, protobuf.CommunityMember_ROLE_ADMIN) ||
            slices.Contains(memberRoles, protobuf.CommunityMember_ROLE_TOKEN_MASTER) ||
            slices.Contains(memberRoles, protobuf.CommunityMember_ROLE_OWNER)) {
        return true
    }

    // Normal members can't kick anyone
    return false
}

func RolesAuthorizedToPerformEvent(senderRoles []protobuf.CommunityMember_Roles, memberRoles []protobuf.CommunityMember_Roles, event *CommunityEvent) bool {
    if !canRolesPerformEvent(senderRoles, event.Type) {
        return false
    }

    if event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE ||
        event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE {
        return canRolesModifyPermission(senderRoles, event.TokenPermission.Type)
    }

    if event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_BAN ||
        event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_KICK ||
        event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_UNBAN ||
        event.Type == protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES {
        return canRolesKickOrBanMember(senderRoles, memberRoles)
    }

    return true
}