Source code for arkouda.security

"""
Security and user identity utilities for Arkouda clients.

The `arkouda.security` module provides functionality for managing access credentials,
user identity, and secure client-side metadata used in communicating with the Arkouda server.

Features
--------
- Platform-independent retrieval of the current user’s username and home directory.
- Creation and management of a `.arkouda` directory for client-specific data.
- Secure generation of authentication tokens using Python’s `secrets` module.
- Serialization of user credentials for use with token-based server authentication.

Functions
---------
generate_token(length=32)
    Generate a secure hexadecimal token using `secrets.token_hex`.

generate_username_token_json(token)
    Return a JSON-formatted string containing both the current user's username and a token.

get_home_directory()
    Return the user's home directory in a cross-platform manner.

get_arkouda_client_directory()
    Get or create the `.arkouda` directory where client configuration and credentials are stored.

get_username()
    Determine the system username based on the user's home directory path.

Notes
-----
- The `.arkouda` directory can be overridden using the `ARKOUDA_CLIENT_DIRECTORY` environment variable.
- This module supports Linux, macOS (Darwin), and Windows platforms.
- Token storage conventions in this module differ from the Arkouda server’s expectations /
and must not be confused.

Examples
--------
>>> from arkouda.security import generate_token, get_username, generate_username_token_json
>>> token = generate_token()
>>> print(token)  # doctest: +SKIP
'8f3a52e1b75f44d1a3a57a869488b637'

>>> user = get_username()
>>> print(user)  # doctest: +SKIP
'emma'

>>> generate_username_token_json(token)  # doctest: +SKIP
'{"username": "emma", "token": "8f3a52e1b75f44d1a3a57a869488b637"}'

"""

import json
import os
import platform
import secrets
from collections import defaultdict
from os.path import expanduser
from pathlib import Path

from typeguard import typechecked

from arkouda.pandas import io_util

__all__ = [
    "generate_token",
    "generate_username_token_json",
    "get_arkouda_client_directory",
    "get_home_directory",
    "get_username",
]


username_tokenizer = defaultdict(lambda x: x.split("/"))  # type:ignore
username_tokenizer["Windows"] = lambda x: x.split("\\")
username_tokenizer["Linux"] = lambda x: x.split("/")
username_tokenizer["Darwin"] = lambda x: x.split("/")


[docs] @typechecked def generate_token(length: int = 32) -> str: """ Use the secrets.token_hex() method to generate a hexidecimal token. Parameters ---------- length : int The desired length of token Returns ------- str The hexidecimal string generated by Python Notes ----- This method uses the Python secrets.token_hex method """ return secrets.token_hex(length // 2)
[docs] def get_home_directory() -> str: """ Find a path to the current user's home directory in a platform-independent manner. Returns ------- str The user's home directory path Notes ----- This method uses the Python os.path.expanduser method to retrieve the user's home directory """ return expanduser("~")
[docs] def get_arkouda_client_directory() -> Path: """ Find a path to the current user's .arkouda directory. Artifacts such as server access tokens are stored in a platform-independent manner in the .arkouda directory. Returns ------- Path Path corresponding to the user's .arkouda directory path Notes ----- The default implementation is to place the .arkouda directory in the current user's home directory. The default can be overridden by setting the ARKOUDA_CLIENT_DIRECTORY environment variable. It is important this is not the same location as the server's token directory as the file format is different. """ arkouda_parent_dir = os.getenv("ARKOUDA_CLIENT_DIRECTORY") if not arkouda_parent_dir: arkouda_parent_dir = get_home_directory() return io_util.get_directory("{}{}.arkouda".format(arkouda_parent_dir, os.sep)).absolute()
[docs] def get_username() -> str: """ Retrieve the current user's username for the host system in a platform-independent manner. Returns ------- str The username in the form of string Raises ------ EnvironmentError Raised if the host OS is unsupported Notes ----- The currently supported operating systems are Windows, Linux, and MacOS AKA Darwin """ try: u_tokens = username_tokenizer[platform.system()](get_home_directory()) except KeyError as ke: raise EnvironmentError(f"Unsupported OS: {ke}") return u_tokens[-1]
[docs] @typechecked def generate_username_token_json(token: str) -> str: """ Generate a JSON object encapsulating the user's username and token. These credentials are for connecting to an arkouda server with basic authentication enabled. Parameters ---------- token : string The token to be used to access arkouda server Returns ------- str The JSON-formatted string encapsulating username and token """ return json.dumps({"username": get_username(), "token": token})