package permissions import ( "context" "database/sql" ) // ComputeChannelPermissions returns effective permissions for a user in a specific channel, // layering channel overrides on top of server-wide role permissions. func (c *Checker) ComputeChannelPermissions(ctx context.Context, serverID, channelID, userID string) (int64, error) { base, err := c.GetUserPermissions(ctx, serverID, userID) if err != nil { return 0, err } if Has(base, ADMINISTRATOR) { return base, nil } // Everyone role override applies first. var everyoneID string _ = c.db.QueryRowContext(ctx, `SELECT id FROM roles WHERE server_id = $1 AND is_default = TRUE LIMIT 1`, serverID).Scan(&everyoneID) if everyoneID != "" { allow, deny, err := c.getOverride(ctx, channelID, "role", everyoneID) if err != nil { return 0, err } base |= allow base &^= deny } // Member-specific role overrides. rows, err := c.db.QueryContext(ctx, ` SELECT r.id FROM roles r INNER JOIN member_roles mr ON mr.role_id = r.id WHERE mr.user_id = $1 AND mr.server_id = $2 `, userID, serverID) if err != nil { return 0, err } defer rows.Close() for rows.Next() { var roleID string if err := rows.Scan(&roleID); err != nil { return 0, err } allow, deny, err := c.getOverride(ctx, channelID, "role", roleID) if err != nil { return 0, err } base |= allow base &^= deny } if err := rows.Err(); err != nil { return 0, err } // User-specific override wins last. allow, deny, err := c.getOverride(ctx, channelID, "user", userID) if err != nil { return 0, err } base |= allow base &^= deny return base, nil } func (c *Checker) getOverride(ctx context.Context, channelID, targetType, targetID string) (allow, deny int64, err error) { err = c.db.QueryRowContext(ctx, ` SELECT allow_bitflags, deny_bitflags FROM channel_overrides WHERE channel_id = $1 AND target_type = $2 AND target_id = $3 `, channelID, targetType, targetID).Scan(&allow, &deny) if err == sql.ErrNoRows { return 0, 0, nil } return allow, deny, err }