class_docstr: Implementation of ZAP authentication for zmq connections.
  
  Note:
  
  - libzmq provides four levels of security: default NULL (which the Authenticator does
    not see), and authenticated NULL, PLAIN, CURVE, and GSSAPI, which the Authenticator can see.
  - until you add policies, all incoming NULL connections are allowed.
    (classic ZeroMQ behavior), and all PLAIN and CURVE connections are denied.
  - GSSAPI requires no configuration.
docstr: Perform ZAP authentication
docstr_tok: ["Perform","ZAP","authentication"]
code: def handle_zap_message(self, msg):
      """Perform ZAP authentication"""
      if len(msg) < 6:
          self.log.error(
              "Invalid ZAP message, not enough frames: %r", msg
          )
          if len(msg) < 2:
              self.log.error("Not enough information to reply")
          else:
              self._send_zap_reply(msg[1], b"400", b"Not enough frames")
          return
  
      version, request_id, domain, address, identity, mechanism = msg[
          :6
      ]
      credentials = msg[6:]
  
      domain = u(domain, self.encoding, "replace")
      address = u(address, self.encoding, "replace")
  
      if version != VERSION:
          self.log.error("Invalid ZAP version: %r", msg)
          self._send_zap_reply(request_id, b"400", b"Invalid version")
          return
  
      self.log.debug(
          "version: %r, request_id: %r, domain: %r,"
          " address: %r, identity: %r, mechanism: %r",
          version,
          request_id,
          domain,
          address,
          identity,
          mechanism,
      )
  
      # Is address is explicitly whitelisted or blacklisted?
      allowed = False
      denied = False
      reason = b"NO ACCESS"
  
      if self.whitelist:
          if address in self.whitelist:
              allowed = True
              self.log.debug("PASSED (whitelist) address=%s", address)
          else:
              denied = True
              reason = b"Address not in whitelist"
              self.log.debug(
                  "DENIED (not in whitelist) address=%s", address
              )
  
      elif self.blacklist:
          if address in self.blacklist:
              denied = True
              reason = b"Address is blacklisted"
              self.log.debug("DENIED (blacklist) address=%s", address)
          else:
              allowed = True
              self.log.debug(
                  "PASSED (not in blacklist) address=%s", address
              )
  
      # Perform authentication mechanism-specific checks if necessary
      username = u("anonymous")
      if not denied:
          if mechanism == b"NULL" and not allowed:
              # For NULL, we allow if the address wasn't blacklisted
              self.log.debug("ALLOWED (NULL)")
              allowed = True
  
          elif mechanism == b"PLAIN":
              # For PLAIN, even a whitelisted address must authenticate
              if len(credentials) != 2:
                  self.log.error(
                      "Invalid PLAIN credentials: %r", credentials
                  )
                  self._send_zap_reply(
                      request_id, b"400", b"Invalid credentials"
                  )
                  return
              username, password = [
                  u(c, self.encoding, "replace") for c in credentials
              ]
              allowed, reason = self._authenticate_plain(
                  domain, username, password
              )
  
          elif mechanism == b"CURVE":
              # For CURVE, even a whitelisted address must authenticate
              if len(credentials) != 1:
                  self.log.error(
                      "Invalid CURVE credentials: %r", credentials
                  )
                  self._send_zap_reply(
                      request_id, b"400", b"Invalid credentials"
                  )
                  return
              key = credentials[0]
              allowed, reason = self._authenticate_curve(domain, key)
              if allowed:
                  username = self.curve_user_id(key)
  
          elif mechanism == b"GSSAPI":
              if len(credentials) != 1:
                  self.log.error(
                      "Invalid GSSAPI credentials: %r", credentials
                  )
                  self._send_zap_reply(
                      request_id, b"400", b"Invalid credentials"
                  )
                  return
              # use principal as user-id for now
              principal = username = credentials[0]
              allowed, reason = self._authenticate_gssapi(
                  domain, principal
              )
  
      if allowed:
          self._send_zap_reply(request_id, b"200", b"OK", username)
      else:
          self._send_zap_reply(request_id, b"400", reason)
  
imports: import logging
  import zmq
  from zmq.utils import z85
  from zmq.utils.strtypes import bytes, unicode, b, u
  from zmq.error import _check_version
  from .certs import load_certificates
label_desc: Use image node_id 11 for calling the Authenticator obj's underlying member method code with example usage: obj.handle_zap_message(msg) without return types