name: keytos-ezradius
description: A package for Keytos EZRADIUS (RADIUS server) SIEM logs.
testCases:
  - event:
      rawString: '{"SubscriptionId":"3f2504e0-4f89-41d3-9a0c-0305e82c3301","RadiusSubscriptionTenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","PolicyID":"a1b2c3d4-0000-1111-2222-333344445555","AccessPolicyId":"b2c3d4e5-1111-2222-3333-444455556666","AccessPolicyName":"Corp Wi-Fi Users","Successful":true,"Message":"Successful EAP-TLS Authentication","UserName":"jdoe@contoso.com","CertificateThumbprint":"9F8E7D6C5B4A39281706F5E4D3C2B1A09F8E7D6C","CertificateSubjectName":"CN=jdoe@contoso.com","AuthenticationType":"EAP-TLS","DateCreated":"2026-07-24T18:30:12.4820000+00:00","RequestingIP":"203.0.113.24:52344","RADIUSIP":"westus-radius-01","NasIpAddress":"10.10.0.5","NasIdentifier":"ap-bldg2-floor3"}'
    assertions:
      - fieldsHaveValues:
          "#Vendor": "keytos"
          "#event.module": "ezradius"
          "#event.kind": "event"
          "#event.dataset": "ezradius"
          "event.category[0]": "authentication"
          "event.type[0]": "info"
          "event.action": "EAP-TLS"
          "event.reason": "Successful EAP-TLS Authentication"
          "user.name": "jdoe@contoso.com"
          "user.email": "jdoe@contoso.com"
          "x509.subject.common_name": "CN=jdoe@contoso.com"
          "tls.server.hash.sha1": "9F8E7D6C5B4A39281706F5E4D3C2B1A09F8E7D6C"
  - event:
      rawString: '{"Policy":{"PolicyName":"Corp Wi-Fi","PolicyID":"a1b2c3d4-0000-1111-2222-333344445555"},"User":{"UserName":"admin@contoso.com","Email":"admin@contoso.com","UserGuid":"9c1e7a20-1234-4bcd-9876-abcdef012345"},"Action":"PolicyUpdated"}'
    assertions:
      - fieldsHaveValues:
          "#event.module": "ezradius"
          "#event.dataset": "ezradius"
          "event.category[0]": "authentication"
          "event.type[0]": "info"
          "event.action": "PolicyUpdated"
          "user.name": "admin@contoso.com"
          "user.email": "admin@contoso.com"
          "user.id": "9c1e7a20-1234-4bcd-9876-abcdef012345"
fieldsToBeRemovedBeforeParsing: []
$schema: https://schemas.humio.com/parser/v0.3.0
script: |-
  // #region PREPARSE
  /************************************************************
  ****** Parse timestamp and log headers
  ****** Extract message field for parsing
  ****** Parse structured data
  ************************************************************/

  // parseJson captures EVERY field EZRADIUS emits into Vendor.* automatically,
  // including nested objects (e.g. Vendor.User.UserName, Vendor.Policy.PolicyName).
  parseJson(prefix="Vendor.", excludeEmpty=true, handleNull=discard)
  // Only authentication/accounting records carry DateCreated (ISO-8601 with a
  // numeric offset, e.g. ".4820000+00:00"), which parseTimestamp handles natively.
  // Config-change records have no timestamp, so fall back to ingest time — do NOT
  // call findTimestamp() here, it errors when no timestamp is present and aborts parsing.
  | case {
      Vendor.DateCreated = *
        | parseTimestamp(field=Vendor.DateCreated);
      *;
  }

  // #endregion

  // #region METADATA
  /************************************************************
  ****** Static Metadata Definitions
  ************************************************************/

  | Parser.version := "1.2.0"
  | Cps.version := "1.2.0"
  | ecs.version := "9.3.0"
  | Vendor := "keytos"
  | event.module := "ezradius"
  | event.kind := "event"

  // #endregion

  // #region NORMALIZATION
  /************************************************************
  ****** Parse unstructured data (i.e. message field)
  ****** Normalize fields to data model
  ************************************************************/

  // Write EZRADIUS values straight through. The same ECS field is written from
  // each candidate source in turn; a missing source is a no-op, so whichever the
  // record populates wins (auth uses AuthenticationType, config-change uses Action).
  | event.action := coalesce([Vendor.Action, Vendor.AuthenticationType])
  | event.reason := Vendor.Message
  | message := Vendor.Message
  | event.dataset := "ezradius"

  // CPS requires at least one event.category and event.type. EZRADIUS is a
  // network authentication service.
  | array:append(array="event.category[]", values=["authentication"])
  | array:append(array="event.type[]", values=["info"])

  // Identity — auth/accounting use UserName; config-change events nest it under User.
  | user.name := coalesce([Vendor.UserName, Vendor.User.UserName])
  | case { user.name = /@/ | user.email := lower(user.name); *; }
  | user.id := Vendor.User.UserGuid

  // Authentication certificate
  | x509.subject.common_name := Vendor.CertificateSubjectName
  // Only a valid 40-char SHA-1 goes into the typed hash field (CPS validates length).
  | case {
      Vendor.CertificateThumbprint = /^[0-9A-Fa-f]{40}$/
        | tls.server.hash.sha1 := Vendor.CertificateThumbprint;
      *;
  }

  // #endregion

  // #region POST-NORMALIZATION
  /************************************************************
  ****** Post Normalization
  ****** Custom parser logic needed after normalization
  ************************************************************/

  // #endregion
tagFields:
  - Vendor
  - Cps.version
  - ecs.version
  - event.dataset
  - event.kind
  - event.module
  - observer.type
