#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# Aruba ClearPass
# +------------------------------------------------------------------+
#      _____ ____   _____    _____  ____  _               _____  
#     |_   _|  _ \ / ____|  / ____|/ __ \| |        /\   |  __ \ 
#       | | | |_) | |      | (___ | |  | | |       /  \  | |__) |
#       | | |  _ <| |       \___ \| |  | | |      / /\ \ |  _  / 
#      _| |_| |_) | |____   ____) | |__| | |____ / ____ \| | \ \ 
#     |_____|____/ \_____| |_____/ \____/|______/_/    \_\_|  \_\
#                                                                                                                        
# +------------------------------------------------------------------+
# Copyright  IBC SOLAR AG - License: GNU General Public License v2.
# Author: Marvin Sobeck
# Link: https://www.ibc-solar.com/
# Written for Aruba ClearPass Monitoring. Based on "Clearpass API Monitoring" 
# originally created by Benjamin Hoch at pronexon GmbH (https://exchange.checkmk.com/p/checkmk-mkp-clearpass)
# My version (2.4.0) cleaned the code a bit and added the monitoring of the last 20 log lines of
# the ClearPass extensions. Because sometimes the extension is running, but not working correctly because
# of errors found within the log. Version 2.5.0 added the monitoring for the CA trust list.
#
# === ORIGINAL HEADER START ===
# SPDX-License-Identifier: MIT
# Copyright © 2025 Benjamin Hoch
#
# @NAME         : agent_clearpass_api.py
# @DESCRIPTION  : check clearpass via api
# @AUTHOR       : Benjamin Hoch <b.hoch@pronexon.de>
# @VERSION      : 2.3.0
# @CREATED      : 2025-06-09
# @UPDATED      : 2025-08-04
# @URL          : https://pronexon.de
# @USAGE        : z.B. './agent_clearpass_api.py -ip 10.115.0.211 -cid test_api_user -cs "NvHZ/zMrhqEa2TcBoH/iQbgFTFgXsKdLGc6S5Ievcdk7"'
# @REQUIRES     :
#
# @SETUP:
#  I)    create clearpass api user:
#        - navigate to the clearpass server via browser and login
#        - ClearPass Guest > Administration > API Services > API Clients > Create API client
#
#  II)   api documentation:
#        - https://developer.arubanetworks.com/aruba-cppm/reference/tokenendpointpost
#
# @NOTES:
#	-
#
# @HISTORY:
#   Version   Date        Author             Description
#   -------   ----------  -----------------  ------------------------------
#   1.0.0     2025-06-09  Benjamin Hoch      initial release
#   1.1.0     2025-08-04  Benjamin Hoch      added api call for pluigns
#   2.2.0     2025-06-09  Benjamin Hoch      added levels for user count
#   2.3.0     2025-08-04  Benjamin Hoch      added functionality for plugins
#
# @TODO:
#   -
# === ORIGINAL HEADER END ===

import argparse
import logging
import requests
import json
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


# parser
parser = argparse.ArgumentParser()
parser.add_argument("--debug", "-d", action="store_true")
requiredArgs = parser.add_argument_group("required arguments")
requiredArgs.add_argument("--ip_address", "-ip", type=str, required=True)
requiredArgs.add_argument("--client_id", "-cid", type=str, required=True)
requiredArgs.add_argument("--client_secret", "-cs", type=str, required= True)
args = parser.parse_args()

# debug
debug = False

if args.debug:
    debug = args.debug

# logging
if debug:
    logging.basicConfig(filename="aruba-clearpass-log", level=logging.INFO, filemode="a", format='%(name)s:%(levelname)s: %(message)s')
    logger = logging.getLogger("aruba-clearpass-log")

# payload
payload = {
    "client_id": args.client_id,
    "grant_type": "client_credentials",
    "client_secret": args.client_secret,
}

# header_access
headers_access = {
    "accept": "application/json",
    "Authorization": "Bearer <TOKEN>",
    "content-type": "application/json"
}

# token
token = "Bearer "

def get_token():
    try:
        response_token = requests.post(f"https://{args.ip_address}/api/oauth", json=payload, verify=False)
    except Exception as error:
        if debug:
            logger.error(error)
    else:
        if response_token.status_code == 200:
            if debug:
                logger.info("Login successful")
            return response_token.json()["access_token"]
        else:
            if debug:
                logger.error("Login failed!")
            return None


def api_get(endpoint, token, params=None):
    headers = {
        "accept": "application/json",
        "Authorization": f"Bearer {token}",
        "content-type": "application/json",
    }

    try:
        response = requests.get(
            f"https://{args.ip_address}/{endpoint.lstrip('/')}",
            headers=headers,
            params=params,
            verify=False,
            timeout=30,
        )
        response.raise_for_status()
        return response.json()
    except Exception as error:
        if debug:
            logger.error(f"API GET failed for {endpoint}: {error}")
        return None


def strip_large_cert_fields(payload):
    if not isinstance(payload, dict):
        return payload

    items = payload.get("_embedded", {}).get("items", [])

    if not isinstance(items, list):
        return payload

    for item in items:
        if isinstance(item, dict):
            item.pop("cert_file", None)

    return payload


def print_section(section_name, payload):
    print(f"<<<{section_name}>>>")
    print(json.dumps(payload if payload is not None else {}, default=str))

if __name__ == "__main__":
    # first get the token
    token = get_token()

    # then get the data
    if token != None:
        # header_certs
        headers_cert = {
            "accept": "application/json",
            "Authorization": f"Bearer {token}",
        }

        try:
            # certificates
            response_cert = requests.get(f"https://{args.ip_address}/api/server-cert", headers=headers_cert, verify=False)
            print("<<<clearpass_cert:sep(0)>>>")
            print(response_cert.json())

            # license
            response_license = requests.get(f"https://{args.ip_address}/api/application-license", headers=headers_cert, verify=False)
            print("<<<clearpass_license:sep(0)>>>")
            print(response_license.json())

            # user count
            response_user_count = requests.get(f"https://{args.ip_address}/api/application-license/summary", headers=headers_cert, verify=False)
            print("<<<clearpass_user_count:sep(0)>>>")
            print(response_user_count.json())

            # plugins
            response_plugins = requests.get(f"https://{args.ip_address}/api/extension/instance", headers=headers_cert, verify=False)
            print("<<<clearpass_plugins:sep(0)>>>")
            plugins_raw = response_plugins.json()
            print(plugins_raw)
            
            # CA trust list            
            trust_list = api_get(
                "api/cert-trust-list-details",
                token,
                params={
                    "limit": 1000,
                    "offset": 0,
                    "sort": "+id",
                    "calculate_count": "false",
                },
            )

            trust_list = strip_large_cert_fields(trust_list)
            print_section("clearpass_cert_trust_list", trust_list)

            # plugin logs
            print("<<<clearpass_plugin_logs:sep(0)>>>")
            plugins = plugins_raw.get("_embedded", {}).get("items", [])

            for plugin in plugins:
                response_plugin_log = requests.get(
                    f"https://{args.ip_address}/api/extension/instance/{plugin['id']}/log"
                    "?stdout=true&stderr=true&timestamps=true&tail=20",
                    headers=headers_cert,
                    verify=False,
                )

                try:
                    log_data = response_plugin_log.text.splitlines()
                except Exception:
                    log_data = []

                print({
                    "id": plugin["id"],
                    "name": plugin["name"],
                    "log": log_data,
                })


        except Exception as error:
            if debug:
                logger.error(error)