diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..6bcaf0b --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,64 @@ + +name: Documentation Build Test + +on: + push: + branches: + - '**' + tags: + - '*' + pull_request: + release: + types: [published] + +jobs: + build-docs: + runs-on: ubuntu-latest + name: Build Documentation + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y doxygen + + - name: Install Python dependencies + run: | + pip install -r docs/requirements.txt + + - name: Build documentation + working-directory: docs + run: | + make html + + - name: Upload artifact for GitHub Pages + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') || github.event_name == 'release' + uses: actions/upload-pages-artifact@v3 + with: + path: docs/_build/html + + deploy-docs: + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') || github.event_name == 'release' + needs: build-docs + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 378eac2..5d878ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ build + +# Documentation build artifacts +docs/_build/ diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..03548d8 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,18 @@ +# ReadTheDocs Configuration File +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + apt_packages: + - doxygen + +sphinx: + configuration: docs/conf.py + +python: + install: + - requirements: docs/requirements.txt \ No newline at end of file diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..771d003 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,51 @@ +# Doxyfile configuration for SciTokens C++ library + +# Project related configuration options +PROJECT_NAME = "SciTokens C++" +PROJECT_NUMBER = "1.0.2" +PROJECT_BRIEF = "A C++ Library to interface to scitokens" +OUTPUT_DIRECTORY = docs/_build/doxygen + +# Build related configuration options +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = NO + +# Configuration options related to the input files +INPUT = src/scitokens.h +FILE_PATTERNS = *.h *.hpp *.cpp +RECURSIVE = NO +EXCLUDE_PATTERNS = */vendor/* */build/* */_build/* + +# Configuration options related to source browsing +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES + +# Configuration options related to the alphabetical class index +ALPHABETICAL_INDEX = YES + +# Configuration options related to the HTML output +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_OUTPUT = xml + +# Configuration options related to the preprocessor +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = src/ +PREDEFINED = __cplusplus + +# Configuration options related to external references +TAGFILES = +GENERATE_TAGFILE = + +# Configuration options related to the dot tool +HAVE_DOT = NO + +# Configuration options related to the search engine +SEARCHENGINE = NO \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..6fb5427 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..194d50b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,60 @@ +# SciTokens C++ Documentation + +This directory contains the Sphinx documentation for the SciTokens C++ library. + +## Building the Documentation + +### Prerequisites + +1. Install Python dependencies: + ```bash + pip install -r requirements.txt + ``` + +2. Install Doxygen (for API extraction): + ```bash + # Ubuntu/Debian + sudo apt install doxygen + + # CentOS/RHEL + sudo yum install doxygen + ``` + +### Building + +From this directory, run: + +```bash +make html +``` + +Or using sphinx-build directly: + +```bash +sphinx-build -b html . _build/html +``` + +The generated documentation will be in `_build/html/`. + +## Documentation Structure + +- `index.rst` - Main documentation page +- `installation.rst` - Installation and building instructions +- `api.rst` - API reference (auto-generated from source comments) +- `examples.rst` - Usage examples +- `conf.py` - Sphinx configuration +- `requirements.txt` - Python dependencies + +## ReadTheDocs Integration + +This documentation is configured for ReadTheDocs. See `.readthedocs.yml` in the project root for the configuration. + +The documentation will automatically build when pushed to the repository. + +## Adding Examples + +Examples in `examples.rst` are based on the test cases in the `test/` directory. When adding new functionality, please: + +1. Add appropriate docstring comments to the public API functions in `src/scitokens.h` +2. Add usage examples to `examples.rst` +3. Test that the documentation builds without warnings \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..c78cde8 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,8 @@ +API Reference +============= + +This page provides the complete API reference for the SciTokens C++ library. +The API is primarily exposed as a C interface for maximum compatibility. + +.. doxygenfile:: scitokens.h + :project: scitokens-cpp \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..7451285 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,53 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'SciTokens C++' +copyright = '2024, SciTokens Team' +author = 'SciTokens Team' +release = '1.0.2' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'breathe', + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon' +] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# -- Options for HTML output ------------------------------------------------ +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] + +# -- Breathe Configuration -------------------------------------------------- + +breathe_projects = { + "scitokens-cpp": "_build/doxygen/xml" +} +breathe_default_project = "scitokens-cpp" + +# -- Doxygen integration ---------------------------------------------------- + +import subprocess +import os + +def run_doxygen(app, config): + """Run doxygen to generate XML for breathe""" + try: + subprocess.run(['doxygen', 'Doxyfile'], cwd='..', check=True) + except subprocess.CalledProcessError: + print("Failed to run doxygen") + +def setup(app): + app.connect('config-inited', run_doxygen) \ No newline at end of file diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..a177ca0 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,285 @@ +Examples +======== + +This page provides practical examples of using the SciTokens C++ library for common tasks. + +Simple Token Creation +--------------------- + +This example shows how to create a basic SciToken without signing: + +.. code-block:: c + + #include + #include + + int main() { + // Create a token without a key (for testing) + SciToken token = scitoken_create(NULL); + if (token == NULL) { + fprintf(stderr, "Failed to create token\n"); + return 1; + } + + // Clean up + scitoken_destroy(token); + printf("Token created successfully\n"); + return 0; + } + +Creating and Signing a Token +---------------------------- + +This example demonstrates how to create a key pair and sign a token: + +.. code-block:: c + + #include + #include + #include + + // Example EC private key + const char ec_private[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIESSMxT7PLTR9A/aqd+CM0/6vv6fQWqDm0mNx8uE9EbpoAoGCCqGSM49\n" + "AwEHoUQDQgAE1i+ImZ//iQhOPh0OMfZzdbmPH+3G1ouWezolCugQYWIRqNmwq3zR\n" + "EnTbe4EmymTpJ1MJTPP/tCEUP3G/QqQuhA==\n" + "-----END EC PRIVATE KEY-----\n"; + + // Example EC public key + const char ec_public[] = + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1i+ImZ//iQhOPh0OMfZzdbmPH+3G\n" + "1ouWezolCugQYWIRqNmwq3zREnTbe4EmymTpJ1MJTPP/tCEUP3G/QqQuhA==\n" + "-----END PUBLIC KEY-----\n"; + + int main() { + char *err_msg = NULL; + + // Create a key for signing + SciTokenKey key = scitoken_key_create("test-key", "ES256", + ec_public, ec_private, &err_msg); + if (key == NULL) { + fprintf(stderr, "Failed to create key: %s\n", err_msg); + free(err_msg); + return 1; + } + + // Create a token with the key + SciToken token = scitoken_create(key); + if (token == NULL) { + fprintf(stderr, "Failed to create token\n"); + scitoken_key_destroy(key); + return 1; + } + + // Set issuer claim + int rv = scitoken_set_claim_string(token, "iss", + "https://example.org", &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to set issuer: %s\n", err_msg); + free(err_msg); + scitoken_destroy(token); + scitoken_key_destroy(key); + return 1; + } + + // Serialize the token + char *serialized_token; + rv = scitoken_serialize(token, &serialized_token, &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to serialize: %s\n", err_msg); + free(err_msg); + } else { + printf("Serialized token: %s\n", serialized_token); + free(serialized_token); + } + + // Clean up + scitoken_destroy(token); + scitoken_key_destroy(key); + + return 0; + } + +Token Validation with Enforcer +------------------------------ + +This example shows how to use an Enforcer to generate ACLs from a token: + +.. code-block:: c + + #include + #include + #include + + int main() { + char *err_msg = NULL; + + // First, create and serialize a token (see previous example) + // For this example, we'll assume we have a token + + // Create an enforcer + const char *audiences[] = {"https://example.org/", NULL}; + Enforcer enforcer = enforcer_create("https://example.org", + audiences, &err_msg); + if (enforcer == NULL) { + fprintf(stderr, "Failed to create enforcer: %s\n", err_msg); + free(err_msg); + return 1; + } + + // Assuming we have a valid token from previous steps + SciToken token = NULL; // This would be your deserialized token + + // Generate ACLs from the token + Acl *acls = NULL; + int rv = enforcer_generate_acls(enforcer, token, &acls, &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to generate ACLs: %s\n", err_msg); + free(err_msg); + } else { + // Print the ACLs + for (int i = 0; acls[i].authz != NULL || acls[i].resource != NULL; i++) { + printf("ACL %d: %s on %s\n", i, + acls[i].authz ? acls[i].authz : "(null)", + acls[i].resource ? acls[i].resource : "(null)"); + } + + // Free the ACLs + enforcer_acl_free(acls); + } + + // Clean up + enforcer_destroy(enforcer); + if (token) scitoken_destroy(token); + + return 0; + } + +Complete Example: Create, Sign, and Validate +-------------------------------------------- + +This comprehensive example demonstrates the full workflow: + +.. code-block:: c + + #include + #include + #include + + int main() { + char *err_msg = NULL; + int rv; + + // Keys for this example + const char ec_private[] = "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIESSMxT7PLTR9A/aqd+CM0/6vv6fQWqDm0mNx8uE9EbpoAoGCCqGSM49\n" + "AwEHoUQDQgAE1i+ImZ//iQhOPh0OMfZzdbmPH+3G1ouWezolCugQYWIRqNmwq3zR\n" + "EnTbe4EmymTpJ1MJTPP/tCEUP3G/QqQuhA==\n" + "-----END EC PRIVATE KEY-----\n"; + + const char ec_public[] = "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1i+ImZ//iQhOPh0OMfZzdbmPH+3G\n" + "1ouWezolCugQYWIRqNmwq3zREnTbe4EmymTpJ1MJTPP/tCEUP3G/QqQuhA==\n" + "-----END PUBLIC KEY-----\n"; + + // Step 1: Create signing key + SciTokenKey key = scitoken_key_create("1", "ES256", + ec_public, ec_private, &err_msg); + if (!key) { + fprintf(stderr, "Failed to create key: %s\n", err_msg); + free(err_msg); + return 1; + } + + // Step 2: Create and configure token + SciToken token = scitoken_create(key); + if (!token) { + fprintf(stderr, "Failed to create token\n"); + scitoken_key_destroy(key); + return 1; + } + + // Set token claims + rv = scitoken_set_claim_string(token, "iss", + "https://demo.scitokens.org/gtest", &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to set issuer: %s\n", err_msg); + goto cleanup; + } + + rv = scitoken_set_claim_string(token, "aud", + "https://demo.scitokens.org/", &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to set audience: %s\n", err_msg); + goto cleanup; + } + + rv = scitoken_set_claim_string(token, "scope", "read:/data", &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to set scope: %s\n", err_msg); + goto cleanup; + } + + // Step 3: Serialize token + char *serialized; + rv = scitoken_serialize(token, &serialized, &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to serialize: %s\n", err_msg); + goto cleanup; + } + + printf("Created token: %s\n", serialized); + + // Step 4: Store public key for validation + rv = scitoken_store_public_ec_key("https://demo.scitokens.org/gtest", + "1", ec_public, &err_msg); + if (rv != 0) { + fprintf(stderr, "Failed to store public key: %s\n", err_msg); + free(serialized); + goto cleanup; + } + + // Step 5: Deserialize and validate + SciToken parsed_token; + const char *allowed_issuers[] = {"https://demo.scitokens.org/gtest", NULL}; + rv = scitoken_deserialize(serialized, &parsed_token, + allowed_issuers, &err_msg); + free(serialized); + + if (rv != 0) { + fprintf(stderr, "Failed to deserialize: %s\n", err_msg); + goto cleanup; + } + + printf("Token validation successful!\n"); + + // Step 6: Create enforcer and generate ACLs + const char *audiences[] = {"https://demo.scitokens.org/", NULL}; + Enforcer enforcer = enforcer_create("https://demo.scitokens.org/gtest", + audiences, &err_msg); + if (enforcer) { + Acl *acls; + rv = enforcer_generate_acls(enforcer, parsed_token, &acls, &err_msg); + if (rv == 0) { + printf("Generated ACLs:\n"); + for (int i = 0; acls[i].authz || acls[i].resource; i++) { + printf(" %s: %s\n", + acls[i].authz ? acls[i].authz : "(null)", + acls[i].resource ? acls[i].resource : "(null)"); + } + enforcer_acl_free(acls); + } + enforcer_destroy(enforcer); + } + + scitoken_destroy(parsed_token); + + cleanup: + if (err_msg) free(err_msg); + scitoken_destroy(token); + scitoken_key_destroy(key); + + return rv; + } \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..03e64c0 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,38 @@ +SciTokens C++ Library Documentation +==================================== + +SciTokens provide a token format for distributed authorization. The tokens are self-describing, +can be verified in a distributed fashion (no need to contact the issuer to determine if the token is valid). +This is convenient for a federated environment where several otherwise-independent storage endpoints +want to delegate trust for an issuer for managing a storage allocation. + +The SciTokens C++ library implements a minimal library for creating and using SciTokens from C or C++. + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + installation + api + examples + +Quick Start +----------- + +This library provides both C and C++ APIs for working with SciTokens. The primary interface +is through the C API defined in ``scitokens.h``. + +Key Features: + +* Create and sign SciTokens +* Validate and verify SciTokens +* Generate Access Control Lists (ACLs) from tokens +* Support for multiple token profiles (SciTokens 1.0/2.0, WLCG, AT+JWT) +* Asynchronous token operations + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..e0f0a05 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,68 @@ +Installation +============ + +Dependencies +------------ + +To build the ``scitokens-cpp`` library, the following dependencies are needed: + +* `jwt-cpp `_ v0.5.0 or later: A header-only C++ library for manipulating JWTs. +* OpenSSL 1.0 or later +* ``sqlite3`` +* ``libcurl`` +* ``libuuid`` + +On Ubuntu/Debian systems:: + + sudo apt install libcurl4-openssl-dev libssl-dev libsqlite3-dev uuid-dev pkg-config + +On CentOS/RHEL systems:: + + sudo yum install libcurl-devel openssl-devel sqlite-devel libuuid-devel pkgconfig + +Building from Source +-------------------- + +CMake is used for the build system. To build, from the source directory:: + + mkdir build + cd build + cmake .. + make + +The library will be built as ``libSciTokens.so`` and the following utilities will be created: + +* ``scitokens-verify`` - Verify a SciToken +* ``scitokens-create`` - Create a new SciToken +* ``scitokens-test`` - Test utility +* ``scitokens-test-access`` - Test access patterns +* ``scitokens-list-access`` - List access permissions + +Installation +------------ + +To install the library and headers:: + + sudo make install + +This will install: + +* Library: ``libSciTokens.so`` to ``/usr/local/lib`` +* Headers: ``scitokens.h`` to ``/usr/local/include/scitokens/`` +* Utilities: Command-line tools to ``/usr/local/bin`` + +Package Installation +-------------------- + +RPM packages are available for CentOS/RHEL systems. Check the project releases page for available packages. + +Testing the Installation +------------------------ + +The easiest way to test ``scitokens-cpp`` is to head to the `SciTokens Demo app `_ +and copy the generated token. Then, from the build directory:: + + echo "" | ./scitokens-verify + +Replace the given token above with the fresh one you just generated; using an old token should give an expired +token error. The token must be provided via standard input (stdin). \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..c2821de --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +sphinx>=4.0 +breathe>=4.30 +sphinx-rtd-theme>=1.0 \ No newline at end of file diff --git a/src/scitokens.h b/src/scitokens.h index b87003a..0c43acf 100644 --- a/src/scitokens.h +++ b/src/scitokens.h @@ -14,17 +14,31 @@ extern "C" { #include #endif +/** @brief Opaque handle for cryptographic keys used to sign/verify tokens */ typedef void *SciTokenKey; + +/** @brief Opaque handle for SciToken objects */ typedef void *SciToken; + +/** @brief Opaque handle for token validators */ typedef void *Validator; + +/** @brief Opaque handle for token enforcers that generate ACLs */ typedef void *Enforcer; + +/** @brief Opaque handle for asynchronous operation status */ typedef void *SciTokenStatus; + +/** @brief Opaque handle for configuration objects */ typedef void *Configuration; +/** @brief Function pointer type for custom string validation */ typedef int (*StringValidatorFunction)(const char *value, char **err_msg); + +/** @brief Access Control List entry containing authorization and resource */ typedef struct Acl_s { - const char *authz; - const char *resource; + const char *authz; /**< Authorization type (e.g., "read", "write") */ + const char *resource; /**< Resource path or pattern */ } Acl; /** @@ -45,19 +59,63 @@ typedef enum _profile { AT_JWT } SciTokenProfile; +/** + * @brief Create a cryptographic key for signing tokens + * + * @param key_id Identifier for the key (used in 'kid' header) + * @param algorithm Signing algorithm (e.g., "ES256", "RS256") + * @param public_contents PEM-encoded public key + * @param private_contents PEM-encoded private key + * @param err_msg Output parameter for error messages (caller must free) + * @return SciTokenKey handle on success, NULL on failure + */ SciTokenKey scitoken_key_create(const char *key_id, const char *algorithm, const char *public_contents, const char *private_contents, char **err_msg); +/** + * @brief Destroy a key object and free associated memory + * + * @param private_key Key handle to destroy + */ void scitoken_key_destroy(SciTokenKey private_key); +/** + * @brief Create a new SciToken + * + * @param private_key Key to use for signing, or NULL for unsigned token + * @return SciToken handle on success, NULL on failure + */ SciToken scitoken_create(SciTokenKey private_key); +/** + * @brief Destroy a token object and free associated memory + * + * @param token Token handle to destroy + */ void scitoken_destroy(SciToken token); +/** + * @brief Set a string claim in the token + * + * @param token Token to modify + * @param key Claim name (e.g., "iss", "aud", "scope") + * @param value Claim value + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int scitoken_set_claim_string(SciToken token, const char *key, const char *value, char **err_msg); +/** + * @brief Get a string claim from the token + * + * @param token Token to query + * @param key Claim name to retrieve + * @param value Output parameter for claim value (caller must free) + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int scitoken_get_claim_string(const SciToken token, const char *key, char **value, char **err_msg); @@ -83,11 +141,33 @@ void scitoken_free_string_list(char **value); int scitoken_set_claim_string_list(const SciToken token, const char *key, const char **values, char **err_msg); +/** + * @brief Get the expiration time of the token + * + * @param token Token to query + * @param value Output parameter for expiration time (Unix timestamp) + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int scitoken_get_expiration(const SciToken token, long long *value, char **err_msg); +/** + * @brief Set the lifetime of the token in seconds + * + * @param token Token to modify + * @param lifetime Lifetime in seconds from creation + */ void scitoken_set_lifetime(SciToken token, int lifetime); +/** + * @brief Serialize the token to a JWT string + * + * @param token Token to serialize + * @param value Output parameter for JWT string (caller must free) + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int scitoken_serialize(const SciToken token, char **value, char **err_msg); /** @@ -100,6 +180,16 @@ void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile); void scitoken_set_deserialize_profile(SciToken token, SciTokenProfile profile); +/** + * @brief Deserialize a JWT string into a SciToken + * + * @param value JWT string to parse + * @param token Output parameter for created token (caller must destroy) + * @param allowed_issuers NULL-terminated array of allowed issuer URLs, or NULL + * for any + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int scitoken_deserialize(const char *value, SciToken *token, char const *const *allowed_issuers, char **err_msg); @@ -139,9 +229,23 @@ int scitoken_deserialize_continue(SciToken *token, SciTokenStatus *status, int scitoken_deserialize_v2(const char *value, SciToken token, char const *const *allowed_issuers, char **err_msg); +/** + * @brief Store a public EC key for token verification + * + * @param issuer Issuer URL that will use this key + * @param keyid Key identifier + * @param value PEM-encoded public key + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *value, char **err_msg); +/** + * @brief Create a new token validator + * + * @return Validator handle on success, NULL on failure + */ Validator validator_create(); /** @@ -162,17 +266,40 @@ int validator_add(Validator validator, const char *claim, int validator_add_critical_claims(Validator validator, const char **claims, char **err_msg); +/** + * @brief Validate a SciToken using the configured validator + * + * @param validator Validator to use + * @param scitoken Token to validate + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int validator_validate(Validator validator, SciToken scitoken, char **err_msg); /** - * Destroy a validator object. + * @brief Destroy a validator object and free associated memory + * + * @param validator Validator handle to destroy */ -void validator_destroy(Validator); +void validator_destroy(Validator validator); +/** + * @brief Create a new token enforcer + * + * @param issuer Required issuer URL for tokens + * @param audience NULL-terminated array of acceptable audience values + * @param err_msg Output parameter for error messages (caller must free) + * @return Enforcer handle on success, NULL on failure + */ Enforcer enforcer_create(const char *issuer, const char **audience, char **err_msg); -void enforcer_destroy(Enforcer); +/** + * @brief Destroy an enforcer object and free associated memory + * + * @param enforcer Enforcer handle to destroy + */ +void enforcer_destroy(Enforcer enforcer); /** * Set the profile used for enforcing ACLs; when set to COMPAT (default), then @@ -187,6 +314,16 @@ void enforcer_set_validate_profile(Enforcer, SciTokenProfile profile); */ int enforcer_set_time(Enforcer enf, time_t now, char **err_msg); +/** + * @brief Generate Access Control Lists from a token + * + * @param enf Enforcer to use + * @param scitokens Token to process + * @param acls Output parameter for ACL array (caller must free with + * enforcer_acl_free) + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 on success, non-zero on failure + */ int enforcer_generate_acls(const Enforcer enf, const SciToken scitokens, Acl **acls, char **err_msg); @@ -199,8 +336,22 @@ int enforcer_generate_acls_start(const Enforcer enf, const SciToken scitokens, int enforcer_generate_acls_continue(const Enforcer enf, SciTokenStatus *status, Acl **acls, char **err_msg); +/** + * @brief Free an array of ACLs returned by enforcer_generate_acls + * + * @param acls ACL array to free + */ void enforcer_acl_free(Acl *acls); +/** + * @brief Test if a token grants access for a specific ACL + * + * @param enf Enforcer to use + * @param sci Token to test + * @param acl ACL to test against + * @param err_msg Output parameter for error messages (caller must free) + * @return 0 if access granted, non-zero if denied or error + */ int enforcer_test(const Enforcer enf, const SciToken sci, const Acl *acl, char **err_msg);