netosecret.py - Python class and CLI

Python containing the NetoSecret class to encode and decode a netosecret string and a CLI to interact with the secret on command line.

1

CLI usage: python netosecret.py --help

You can run this as a standalone CLI for encoding and decoding netosecret values, or use the NetoSecret class in your Python.

"""Encode/Decode netosecret format for Netography API.
usage: netosecret.py [-h] [--url URL] [--appkey APPKEY] [--sharedsecret SHAREDSECRET] [--save SAVE] [--envname ENVNAME] [--print] [--decode] [--env] [--envfile ENVFILE] [shortname] [appname]
Encode/Decode netosecret format for Netography API.
positional arguments:
  shortname             Set the shortname
  appname               Set the appname
options:
  -h, --help            show this help message and exit
  --url URL             Set a custom API URL
  --appkey APPKEY       Set the appkey (prompt if not provided)
  --sharedsecret SHAREDSECRET
                        Set the sharedsecret (prompt if not provided)
  --save SAVE           Save to env file
  --envname ENVNAME     Variable name to write to in envfile
  --print               Print the encoded netosecret
  --decode              Decode NETOSECRET and print the JSON (contains secrets)
  --env                 Read all variables from environment
  --envfile ENVFILE     Read environment variables from the file

Example:
% python netosecret.py --appkey myappkey --sharedsecret mysharedsecret myshortname myappname
netosecret saved to netosecret.env
% python netosecret.py --decode --envfile netosecret.env
{
  "url": "https://api.netography.com/api/v1",
  "shortname": "myshortname",
  "appname": "myappname",
  "sharedsecret": "mysharedsecret",
  "appkey": "myappkey"
}
% python netosecret.py --help
"""
2

class NetoSecret definition

These 11 lines are all you need to replace setting the 5 individual authentication fields used by the Fusion APi with the netosecret.

NetoSecret.encode()

import argparse
import base64
import json
import os
from getpass import getpass
from typing import Dict

class NetoSecret:
    @staticmethod
    def encode(api_key: Dict[str, str]) -> str:
        api_key_json = json.dumps(api_key)
        api_key_base64 = base64.b64encode(api_key_json.encode()).decode()
        return api_key_base64

    @staticmethod
    def decode(api_key_base64: str) -> Dict[str, str]:
        api_key_json = base64.b64decode(api_key_base64).decode()
        api_key = json.loads(api_key_json)
        return api_key
3

NetoSecret.encode() takes Python Dict in this format and returns netosecret string.

{ "url":"", "shortname":"", "appname":"", "sharedsecret":"", "appkey":"" }

    @staticmethod
    def encode(api_key: Dict[str, str]) -> str:
        api_key_json = json.dumps(api_key)
        api_key_base64 = base64.b64encode(api_key_json.encode()).decode()
        return api_key_base64
4

NetoSecret.decode()

NetoSecret.decode() takes a netosecret string as argument and returns a Python Dict in format below:

{ "url":"", "shortname":"", "appname":"", "sharedsecret":"", "appkey":"" }

    @staticmethod
    def decode(api_key_base64: str) -> Dict[str, str]:
        api_key_json = base64.b64decode(api_key_base64).decode()
        api_key = json.loads(api_key_json)
        return api_key
5

CLI argument parsing

Parses command line arguments when running this file

def get_env_value(is_secret: bool, *keys: str) -> str:
    for key in keys:
        value = os.getenv(key)
        if value:
            if is_secret:
                print(f"Read from env {key}=******", file=os.stderr)
            else:
                print(f"Read from env {key}={value}", file=os.stderr)
            return value
    return ""

def main():
    parser = argparse.ArgumentParser(
        description="Encode/Decode netosecret format for Netography API."
    )

    parser.add_argument("shortname", nargs="?", help="Set the shortname")
    parser.add_argument("appname", nargs="?", help="Set the appname")
    parser.add_argument(
        "--url",
        help="Set a custom API URL",
        default="https://api.netography.com/api/v1",
    )
    parser.add_argument("--appkey", help="Set the appkey (prompt if not provided)")
    parser.add_argument(
        "--sharedsecret", help="Set the sharedsecret (prompt if not provided)"
    )
    parser.add_argument("--save", help="Save to env file", default="netosecret.env")
    parser.add_argument(
        "--envname", help="Variable name to write to in envfile", default="NETOSECRET"
    )
    parser.add_argument(
        "--print", action="store_true", help="Print the encoded netosecret"
    )
    parser.add_argument(
        "--decode",
        action="store_true",
        help="Decode NETOSECRET and print the JSON (contains secrets)",
    )
    parser.add_argument(
        "--env", action="store_true", help="Read all variables from environment"
    )
    parser.add_argument("--envfile", help="Read environment variables from the file")

    args = parser.parse_args()

    if args.envfile:
        with open(args.envfile) as f:
            for line in f:
                if not line.startswith("#") and "=" in line:
                    key, value = line.strip().split("=", 1)
                    os.environ[key] = value
6

CLI: decode

Decodes netosecret to JSON

    if args.decode:
        netosecret_to_decode = os.getenv(args.envname)
        if not netosecret_to_decode:
            netosecret_to_decode = getpass("Enter netosecret: ")
        decoded = NetoSecret.decode(netosecret_to_decode)
        print(json.dumps(decoded, indent=2))
        return
7

CLI: encode

Encodes individual authentication fields to netosecret string

    if args.env or args.envfile:
        args.url = (
            get_env_value(False, "NETO__API__URL", "NETO_URL", "NETO__URL") or args.url
        )
        args.appname = (
            get_env_value(False, "NETO__API__APP_NAME", "NETO_APP_NAME", "NETO_APPNAME")
            or args.appname
        )
        args.shortname = (
            get_env_value(
                False, "NETO__API__SHORTNAME", "NETO_SHORTNAME", "NETO__SHORTNAME"
            )
            or args.shortname
        )
        args.sharedsecret = (
            get_env_value(
                True,
                "NETO__API__CREDENTIALS__SHARED_SECRET",
                "NETO_SHAREDSECRET",
                "NETO__SHAREDSECRET",
                "NETO_SHARED_SECRET",
            )
            or args.sharedsecret
        )
        args.appkey = (
            get_env_value(
                True,
                "NETO__API__CREDENTIALS__APP_KEY",
                "NETO__APPKEY",
                "NETO__APP_KEY",
                "NETO_APPKEY",
            )
            or args.appkey
        )

    if not args.shortname:
        args.shortname = input("Enter shortname: ")
    if not args.appname:
        args.appname = input("Enter appname: ")
    if not args.appkey:
        args.appkey = getpass("Enter appkey: ")
    if not args.sharedsecret:
        args.sharedsecret = getpass("Enter sharedsecret: ")

    if not all([args.shortname, args.appname, args.appkey, args.sharedsecret]):
        parser.error(
            "All fields (shortname, appname, appkey, sharedsecret) must be provided."
        )

    api_key = {
        "url": args.url,
        "shortname": args.shortname,
        "appname": args.appname,
        "sharedsecret": args.sharedsecret,
        "appkey": args.appkey,
    }

    encoded = NetoSecret.encode(api_key)

    if args.print:
        print(encoded)
    else:
        with open(args.save, "w") as f:
            f.write(f"{args.envname}={encoded}\n")
        print(f"netosecret saved to {args.save}")


if __name__ == "__main__":
    main()

Last updated