diff --git a/cmd/api/src/api/registration/v2.go b/cmd/api/src/api/registration/v2.go index 46a6181a74..88b278270f 100644 --- a/cmd/api/src/api/registration/v2.go +++ b/cmd/api/src/api/registration/v2.go @@ -49,6 +49,7 @@ func registerV2Auth(resources v2.Resources, routerInst *router.Router, permissio routerInst.PathPrefix(fmt.Sprintf("/api/{version}/login/saml/{%s}", api.URIPathVariableSSOProviderSlug), http.HandlerFunc(managementResource.SAMLLoginRedirect)), // SAML resources + // DEPRECATED as of v6.4.0: Please use /api/v2/sso-providers/* endpoints instead of /api/v2/saml/* routerInst.GET("/api/v2/saml", managementResource.ListSAMLProviders).RequirePermissions(permissions.AuthManageProviders), routerInst.GET("/api/v2/saml/sso", managementResource.ListSAMLSignOnEndpoints), routerInst.POST("/api/v2/saml/providers", managementResource.CreateSAMLProviderMultipart).RequirePermissions(permissions.AuthManageProviders), @@ -57,6 +58,7 @@ func registerV2Auth(resources v2.Resources, routerInst *router.Router, permissio // SSO routerInst.GET("/api/v2/sso-providers", managementResource.ListAuthProviders), + routerInst.POST("/api/v2/sso-providers/saml", managementResource.CreateSAMLProviderMultipart).RequirePermissions(permissions.AuthManageProviders), routerInst.POST("/api/v2/sso-providers/oidc", managementResource.CreateOIDCProvider).CheckFeatureFlag(resources.DB, appcfg.FeatureOIDCSupport).RequirePermissions(permissions.AuthManageProviders), routerInst.DELETE(fmt.Sprintf("/api/v2/sso-providers/{%s}", api.URIPathVariableSSOProviderID), managementResource.DeleteSSOProvider).RequirePermissions(permissions.AuthManageProviders), routerInst.PATCH(fmt.Sprintf("/api/v2/sso-providers/{%s}", api.URIPathVariableSSOProviderID), managementResource.UpdateSSOProvider).RequirePermissions(permissions.AuthManageProviders), diff --git a/packages/go/openapi/doc/openapi.json b/packages/go/openapi/doc/openapi.json index 2d18616602..c36f1345f5 100644 --- a/packages/go/openapi/doc/openapi.json +++ b/packages/go/openapi/doc/openapi.json @@ -225,7 +225,8 @@ "get": { "operationId": "ListSamlProviders", "summary": "List SAML Providers", - "description": "List all registered SAML providers.", + "description": "**Deprecated**: This endpoint will no longer be supported in a future release. Please use `GET /api/v2/sso-providers` instead.\n", + "deprecated": true, "tags": [ "Auth", "Community", @@ -279,7 +280,8 @@ "get": { "operationId": "GetSamlSignSignOnEndpoints", "summary": "Get all SAML sign on endpoints", - "description": "Get all SAML sign on endpoints", + "description": "**Deprecated**: This endpoint will no longer be supported in a future release. Please use `GET /api/v2/sso-providers` instead to list available SSO endpoints.\n", + "deprecated": true, "tags": [ "Auth", "Community", @@ -323,7 +325,8 @@ "post": { "operationId": "CreateSamlProvider", "summary": "Create a New SAML Provider from Metadata", - "description": "Creates a new SAML provider with the given name and metadata XML.", + "description": "**Deprecated**: This endpoint will no longer be supported in a future release. Please use `POST /api/v2/sso-providers/saml` instead.\n", + "deprecated": true, "tags": [ "Auth", "Community", @@ -402,7 +405,8 @@ "get": { "operationId": "GetSamlProvider", "summary": "Get SAML Provider", - "description": "Get the service and identity provider configuration details for a SAML authentication provider.", + "description": "**Deprecated**: This endpoint will no longer be supported in a future release. Please use `GET /api/v2/sso-providers` to list all SAML providers instead.\n", + "deprecated": true, "tags": [ "Auth", "Community", @@ -444,7 +448,8 @@ "delete": { "operationId": "DeleteSamlProvider", "summary": "Delete a SAML Provider", - "description": "Deletes an existing BloodHound SAML provider.", + "description": "**Deprecated**: This endpoint will no longer be supported in a future release. Please use `DELETE /api/v2/sso-providers/{sso_provider_id}` instead.\n", + "deprecated": true, "tags": [ "Auth", "Community", @@ -630,6 +635,70 @@ } } }, + "/api/v2/sso-providers/saml": { + "post": { + "operationId": "CreateSSOSAMLProvider", + "summary": "Create a New SAML Provider from Metadata", + "description": "Creates a new SAML provider with the given name and metadata XML.", + "tags": [ + "Auth", + "Community", + "Enterprise" + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "properties": { + "name": { + "type": "string", + "description": "Name of the new SAML provider." + }, + "metadata": { + "type": "string", + "format": "binary", + "description": "Metadata XML file." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/model.saml-provider" + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad-request" + }, + "401": { + "$ref": "#/components/responses/unauthorized" + }, + "403": { + "$ref": "#/components/responses/forbidden" + }, + "429": { + "$ref": "#/components/responses/too-many-requests" + }, + "500": { + "$ref": "#/components/responses/internal-server-error" + } + } + } + }, "/api/v2/sso-providers/{sso_provider_id}": { "parameters": [ { diff --git a/packages/go/openapi/src/openapi.yaml b/packages/go/openapi/src/openapi.yaml index 7a24725dc4..18634a3866 100644 --- a/packages/go/openapi/src/openapi.yaml +++ b/packages/go/openapi/src/openapi.yaml @@ -1,702 +1,704 @@ -# Copyright 2024 Specter Ops, Inc. -# -# Licensed under the Apache License, Version 2.0 -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -# OpenAPI Spec: https://learn.openapis.org/specification/ - -openapi: 3.0.3 -servers: - - url: / - description: This is the base path for all endpoints, relative to the domain where the API is being hosted. -info: - title: BloodHound API - contact: - name: BloodHound Enterprise Support - url: https://support.bloodhoundenterprise.io/ - email: support@specterops.io - license: - name: Apache-2.0 - url: https://www.apache.org/licenses/LICENSE-2.0 - version: 'v2' - description: | - This is the API that drives BloodHound Enterprise and Community Edition. - Endpoint availability is denoted using the `Community` and `Enterprise` tags. - - Contact information listed is for BloodHound Enterprise customers. To get help with - BloodHound Community Edition, please join our - [Slack community](https://ghst.ly/BHSlack/). - - ## Authentication - - The BloodHound API supports two kinds of authentication: JWT bearer tokens and Signed Requests. - For quick tests or one-time calls, the JWT used by your browser may be the simplest route. For - more secure and long lived API integrations, the recommended option is signed requests. - - ### JWT Bearer Token - - The API will accept calls using the following header structure in the HTTP request: - ``` - Authorization: Bearer $JWT_TOKEN - ``` - If you open the Network tab within your browser, you will see calls against the API made utilizing - this structure. JWT bearer tokens are supported by the BloodHound API, however it is recommended - they only be used for temporary access. JWT tokens expire after a set amount of time and require - re-authentication using secret credentials. - - ### Signed Requests - - Signed requests are the recommended form of authentication for the BloodHound API. Not only are - signed requests better for long lived integrations, they also provide more security for the - requests being sent. They provide authentication of the client, as well as verification of request - integrity when received by the server. - - Signed requests consist of three main parts: The client token ID, the request timestamp, and a - base64 encoded HMAC signature. These three pieces of information are sent with the request using - the following header structure: - - ``` - Authorization: bhesignature $TOKEN_ID - RequestDate: $RFC3339_DATETIME - Signature: $BASE64ENCODED_HMAC_SIGNATURE - ``` - - To use signed requests, you will need to generate an API token. Each API token generated in the - BloodHound API comes with two parts: The Token ID, which is used in the `Authorization` header, - and the Token Key, which is used as part of the HMAC hashing process. The token ID should be - considered as public (like a username) and the token key should be considered secret (like a - password). Once an API token is generated, you can use the key to sign requests. - - For more documentation about how to work with authentication in the API, including examples - of how to generate an API token in the BloodHound UI, please refer to this support doc: - [Working with the BloodHound API](https://support.bloodhoundenterprise.io/hc/en-us/articles/11311053342619-Working-with-the-BloodHound-API). - - #### Signed Request Pseudo-code Example - - First, a digest is initiated with HMAC-SHA-256 using the token key as the digest key: - ```python - digester = hmac.new(sha256, api_token_key) - ``` - - OperationKey is the first HMAC digest link in the signature chain. This prevents replay attacks that - seek to modify the request method or URI. It is composed of concatenating the request method and - the request URI with no delimiter and computing the HMAC digest using the token key as the digest - secret: - ```python - # Example: GET /api/v2/test/resource HTTP/1.1 - # Signature Component: GET/api/v2/test/resource - digester.write(request_method + request_uri) - - # Update the digester for further chaining - digester = hmac.New(sha256, digester.hash()) - ``` - - DateKey is the next HMAC digest link in the signature chain. This encodes the RFC3339 - formatted datetime value as part of the signature to the hour to prevent replay - attacks that are older than max two hours. This value is added to the signature chain - by cutting off all values from the RFC3339 formatted datetime from the hours value - forward: - ```python - # Example: 2020-12-01T23:59:60Z - # Signature Component: 2020-12-01T23 - request_datetime = date.now() - digester.write(request_datetime[:13]) - - # Update the digester for further chaining - digester = hmac.New(sha256, digester.hash()) - ``` - - Body signing is the last HMAC digest link in the signature chain. This encodes the - request body as part of the signature to prevent replay attacks that seek to modify - the payload of a signed request. In the case where there is no body content the - HMAC digest is computed anyway, simply with no values written to the digester: - ```python - if request.body is not empty: - digester.write(request.body) - ``` - - Finally, base64 encode the final hash and write the three required headers before - sending the request: - ```python - encoded_hash = base64_encode(digester.hash()) - request.header.write('Authorization', 'bhesignature ' + token_id) - request.header.write('RequestDate', request_datetime) - request.header.write('Signature', encoded_hash) - ``` - -security: - - JWTBearerToken: [] - - SignedRequest: [] - RequestDate: [] - HMACSignature: [] - -## -# This section is used by ReDoc to organize endpoint groups in the sidebar. -# Two top level tag groups help users find Community and Enterprise features. -# -## ALL TAGS MUST BE LISTED HERE TO SHOW UP IN THE NAVIGATION. ## -x-tagGroups: - - name: Community & Enterprise - tags: - - Auth - - Roles - - Permissions - - API Tokens - - BloodHound Users - - Collectors - - Collection Uploads - - API Info - - Search - - Audit - - Config - - Asset Isolation - - Graph - - Azure Entities - - AD Base Entities - - Computers - - Containers - - Domains - - GPOs - - AIA CAs - - Root CAs - - Enterprise CAs - - NT Auth Stores - - Cert Templates - - OUs - - AD Users - - Groups - - Data Quality - - Datapipe - - Cypher - - name: Enterprise Only - tags: - - EULA - - BHE Users - - Analysis - - Client Ingest - - Clients - - Jobs - - Tasks - - Events (Schedules) - - Attack Paths - - Risk Posture - - Meta Entities - -paths: - ## - # Community + Enterprise Endpoints - ## - - # auth - /api/v2/login: - $ref: './paths/auth.login.yaml' - /api/v2/logout: - $ref: './paths/auth.logout.yaml' - /api/v2/self: - $ref: './paths/auth.self.yaml' - /api/v2/saml: - $ref: './paths/auth.saml.yaml' - /api/v2/saml/sso: - $ref: './paths/auth.saml.sso.yaml' - /api/v2/saml/providers: - $ref: './paths/auth.saml.providers.yaml' - /api/v2/saml/providers/{saml_provider_id}: - $ref: './paths/auth.saml.providers.id.yaml' - - # Undocumented, not sure how this endpoint actually works. - #/api/v2/login/saml/{saml_provider_name}: - - # sso - /api/v2/sso-providers: - $ref: './paths/sso.sso-providers.yaml' - /api/v2/sso-providers/oidc: - $ref: './paths/sso.sso-providers.oidc.yaml' - /api/v2/sso-providers/{sso_provider_id}: - $ref: './paths/sso.sso-providers.id.yaml' - /api/v2/sso-providers/{sso_provider_id}/signing-certificate: - $ref: './paths/sso.sso-providers.id.signing-certificate.yaml' - - # permissions - /api/v2/permissions: - $ref: './paths/permissions.permissions.yaml' - /api/v2/permissions/{permission_id}: - $ref: './paths/permissions.permissions.id.yaml' - - # roles - /api/v2/roles: - $ref: './paths/roles.roles.yaml' - /api/v2/roles/{role_id}: - $ref: './paths/roles.roles.id.yaml' - - # api tokens - /api/v2/tokens: - $ref: './paths/tokens.tokens.yaml' - /api/v2/tokens/{token_id}: - $ref: './paths/tokens.tokens.id.yaml' - - # user management - /api/v2/bloodhound-users: - $ref: './paths/bh-users.bloodhound-users.yaml' - /api/v2/bloodhound-users/{user_id}: - $ref: './paths/bh-users.bloodhound-users.id.yaml' - /api/v2/bloodhound-users/{user_id}/secret: - $ref: './paths/bh-users.bloodhound-users.id.secret.yaml' - /api/v2/bloodhound-users/{user_id}/mfa: - $ref: './paths/bh-users.bloodhound-users.id.mfa.yaml' - /api/v2/bloodhound-users/{user_id}/mfa-activation: - $ref: './paths/bh-users.bloodhound-users.id.mfa-activation.yaml' - - # collectors - /api/v2/collectors/{collector_type}: - $ref: './paths/collectors.collectors.type.yaml' - /api/v2/collectors/{collector_type}/{release_tag}: - $ref: './paths/collectors.collectors.type.tag.yaml' - /api/v2/collectors/{collector_type}/{release_tag}/checksum: - $ref: './paths/collectors.collectors.type.tag.checksum.yaml' - - # collection uploads - /api/v2/file-upload: - $ref: './paths/collection-uploads.file-upload.yaml' - /api/v2/file-upload/start: - $ref: './paths/collection-uploads.file-upload.start.yaml' - /api/v2/file-upload/{file_upload_job_id}: - $ref: './paths/collection-uploads.file-upload.id.yaml' - /api/v2/file-upload/{file_upload_job_id}/end: - $ref: './paths/collection-uploads.file-upload.id.end.yaml' - /api/v2/file-upload/accepted-types: - $ref: './paths/collection-uploads.file-upload.accepted-types.yaml' - - # api info - /api/version: - $ref: './paths/api-info.version.yaml' - /api/v2/spec/openapi.yaml: - $ref: './paths/api-info.spec.yaml' - - # search - /api/v2/search: - $ref: './paths/search.search.yaml' - /api/v2/available-domains: - $ref: './paths/search.available-domains.yaml' - - # audit - /api/v2/audit: - $ref: './paths/audit.audit.yaml' - - # config - /api/v2/config: - $ref: './paths/config.config.yaml' - /api/v2/features: - $ref: './paths/config.features.yaml' - /api/v2/features/{feature_id}/toggle: - $ref: './paths/config.features.id.toggle.yaml' - - # asset isolation - /api/v2/asset-groups: - $ref: './paths/asset-isolation.asset-groups.yaml' - /api/v2/asset-groups/{asset_group_id}: - $ref: './paths/asset-isolation.asset-groups.id.yaml' - /api/v2/asset-groups/{asset_group_id}/collections: - $ref: './paths/asset-isolation.asset-groups.id.collections.yaml' - /api/v2/asset-groups/{asset_group_id}/selectors: - $ref: './paths/asset-isolation.asset-groups.id.selectors.yaml' - /api/v2/asset-groups/{asset_group_id}/selectors/{asset_group_selector_id}: - $ref: './paths/asset-isolation.asset-groups.id.selectors.id.yaml' - /api/v2/asset-groups/{asset_group_id}/custom-selectors: - $ref: './paths/asset-isolation.asset-groups.id.custom-selectors.yaml' - /api/v2/asset-groups/{asset_group_id}/members: - $ref: './paths/asset-isolation.asset-groups.id.members.yaml' - /api/v2/asset-groups/{asset_group_id}/members/counts: - $ref: './paths/asset-isolation.asset-groups.id.members.counts.yaml' - - # graph - /api/v2/pathfinding: - $ref: './paths/graph.pathfinding.yaml' - /api/v2/graph-search: - $ref: './paths/graph.graph-search.yaml' - /api/v2/graphs/shortest-path: - $ref: './paths/graph.graphs.shortest-path.yaml' - /api/v2/graphs/edge-composition: - $ref: './paths/graph.graphs.edge-composition.yaml' - - # cypher - /api/v2/saved-queries: - $ref: './paths/cypher.saved-queries.yaml' - /api/v2/saved-queries/{saved_query_id}: - $ref: './paths/cypher.saved-queries.id.yaml' - /api/v2/saved-queries/{saved_query_id}/permissions: - $ref: './paths/cypher.saved-queries.id.permissions.yaml' - /api/v2/graphs/cypher: - $ref: './paths/cypher.graphs.cypher.yaml' - - # azure entities - /api/v2/azure/{entity_type}: - $ref: './paths/azure.entity.yaml' - - # ad base entities - /api/v2/base/{object_id}: - $ref: './paths/base.base.id.yaml' - /api/v2/base/{object_id}/controllables: - $ref: './paths/base.base.id.controllables.yaml' - /api/v2/base/{object_id}/controllers: - $ref: './paths/base.base.id.controllers.yaml' - - # computers - /api/v2/computers/{object_id}: - $ref: './paths/computers.computers.id.yaml' - /api/v2/computers/{object_id}/admin-rights: - $ref: './paths/computers.computers.id.admin-rights.yaml' - /api/v2/computers/{object_id}/admin-users: - $ref: './paths/computers.computers.id.admin-users.yaml' - /api/v2/computers/{object_id}/constrained-delegation-rights: - $ref: './paths/computers.computers.id.constrained-delegation-rights.yaml' - /api/v2/computers/{object_id}/constrained-users: - $ref: './paths/computers.computers.id.constrained-users.yaml' - /api/v2/computers/{object_id}/controllables: - $ref: './paths/computers.computers.id.controllables.yaml' - /api/v2/computers/{object_id}/controllers: - $ref: './paths/computers.computers.id.controllers.yaml' - /api/v2/computers/{object_id}/dcom-rights: - $ref: './paths/computers.computers.id.dcom-rights.yaml' - /api/v2/computers/{object_id}/dcom-users: - $ref: './paths/computers.computers.id.dcom-users.yaml' - /api/v2/computers/{object_id}/group-membership: - $ref: './paths/computers.computers.id.group-membership.yaml' - /api/v2/computers/{object_id}/ps-remote-rights: - $ref: './paths/computers.computers.id.ps-remote-rights.yaml' - /api/v2/computers/{object_id}/ps-remote-users: - $ref: './paths/computers.computers.id.ps-remote-users.yaml' - /api/v2/computers/{object_id}/rdp-rights: - $ref: './paths/computers.computers.id.rdp-rights.yaml' - /api/v2/computers/{object_id}/rdp-users: - $ref: './paths/computers.computers.id.rdp-users.yaml' - /api/v2/computers/{object_id}/sessions: - $ref: './paths/computers.computers.id.sessions.yaml' - /api/v2/computers/{object_id}/sql-admins: - $ref: './paths/computers.computers.id.sql-admins.yaml' - - # containers - /api/v2/containers/{object_id}: - $ref: './paths/containers.containers.id.yaml' - /api/v2/containers/{object_id}/controllers: - $ref: './paths/containers.containers.id.controllers.yaml' - - # domains - /api/v2/domains/{object_id}: - $ref: './paths/domains.domains.id.yaml' - /api/v2/domains/{object_id}/computers: - $ref: './paths/domains.domains.id.computers.yaml' - /api/v2/domains/{object_id}/controllers: - $ref: './paths/domains.domains.id.controllers.yaml' - /api/v2/domains/{object_id}/dc-syncers: - $ref: './paths/domains.domains.id.dc-syncers.yaml' - /api/v2/domains/{object_id}/foreign-admins: - $ref: './paths/domains.domains.id.foreign-admins.yaml' - /api/v2/domains/{object_id}/foreign-gpo-controllers: - $ref: './paths/domains.domains.id.foreign-gpo-controllers.yaml' - /api/v2/domains/{object_id}/foreign-groups: - $ref: './paths/domains.domains.id.foreign-groups.yaml' - /api/v2/domains/{object_id}/foreign-users: - $ref: './paths/domains.domains.id.foreign-users.yaml' - /api/v2/domains/{object_id}/gpos: - $ref: './paths/domains.domains.id.gpos.yaml' - /api/v2/domains/{object_id}/groups: - $ref: './paths/domains.domains.id.groups.yaml' - /api/v2/domains/{object_id}/inbound-trusts: - $ref: './paths/domains.domains.id.inbound-trusts.yaml' - /api/v2/domains/{object_id}/linked-gpos: - $ref: './paths/domains.domains.id.linked-gpos.yaml' - /api/v2/domains/{object_id}/ous: - $ref: './paths/domains.domains.id.ous.yaml' - /api/v2/domains/{object_id}/outbound-trusts: - $ref: './paths/domains.domains.id.outbound-trusts.yaml' - /api/v2/domains/{object_id}/users: - $ref: './paths/domains.domains.id.users.yaml' - - # gpos - /api/v2/gpos/{object_id}: - $ref: './paths/gpos.gpos.id.yaml' - /api/v2/gpos/{object_id}/computers: - $ref: './paths/gpos.gpos.id.computers.yaml' - /api/v2/gpos/{object_id}/controllers: - $ref: './paths/gpos.gpos.id.controllers.yaml' - /api/v2/gpos/{object_id}/ous: - $ref: './paths/gpos.gpos.id.ous.yaml' - /api/v2/gpos/{object_id}/tier-zero: - $ref: './paths/gpos.gpos.id.tier-zero.yaml' - /api/v2/gpos/{object_id}/users: - $ref: './paths/gpos.gpos.id.users.yaml' - - # aiacas - /api/v2/aiacas/{object_id}: - $ref: './paths/aiacas.aiaca.id.yaml' - /api/v2/aiacas/{object_id}/controllers: - $ref: './paths/aiacas.aiaca.id.controllers.yaml' - - # root cas - /api/v2/rootcas/{object_id}: - $ref: './paths/rootcas.rootcas.id.yaml' - /api/v2/rootcas/{object_id}/controllers: - $ref: './paths/rootcas.rootcas.id.controllers.yaml' - - # enterprise cas - /api/v2/enterprisecas/{object_id}: - $ref: './paths/enterprisecas.enterprisecas.id.controllers.yaml' - /api/v2/enterprisecas/{object_id}/controllers: - $ref: './paths/enterprisecas.enterprisecas.id.yaml' - - # nt auth stores - /api/v2/ntauthstores/{object_id}: - $ref: './paths/ntauthstores.ntauthstores.id.yaml' - /api/v2/ntauthstores/{object_id}/controllers: - $ref: './paths/ntauthstores.ntauthstores.id.controllers.yaml' - - # cert templates - /api/v2/certtemplates/{object_id}: - $ref: './paths/certtemplate.certtemplates.id.yaml' - /api/v2/certtemplates/{object_id}/controllers: - $ref: './paths/certtemplate.certtemplates.id.controllers.yaml' - - # ous - /api/v2/ous/{object_id}: - $ref: './paths/ous.ous.id.yaml' - /api/v2/ous/{object_id}/computers: - $ref: './paths/ous.ous.id.computers.yaml' - /api/v2/ous/{object_id}/gpos: - $ref: './paths/ous.ous.id.gpos.yaml' - /api/v2/ous/{object_id}/groups: - $ref: './paths/ous.ous.id.groups.yaml' - /api/v2/ous/{object_id}/users: - $ref: './paths/ous.ous.id.users.yaml' - - # ad users - /api/v2/users/{object_id}: - $ref: './paths/users.users.id.yaml' - /api/v2/users/{object_id}/admin-rights: - $ref: './paths/users.users.id.admin-rights.yaml' - /api/v2/users/{object_id}/constrained-delegation-rights: - $ref: './paths/users.users.id.constrained-delegation-rights.yaml' - /api/v2/users/{object_id}/controllables: - $ref: './paths/users.users.id.controllables.yaml' - /api/v2/users/{object_id}/controllers: - $ref: './paths/users.users.id.controllers.yaml' - /api/v2/users/{object_id}/dcom-rights: - $ref: './paths/users.users.id.dcom-rights.yaml' - /api/v2/users/{object_id}/memberships: - $ref: './paths/users.users.id.memberships.yaml' - /api/v2/users/{object_id}/ps-remote-rights: - $ref: './paths/users.users.id.ps-remote-rights.yaml' - /api/v2/users/{object_id}/rdp-rights: - $ref: './paths/users.users.id.rdp-rights.yaml' - /api/v2/users/{object_id}/sessions: - $ref: './paths/users.users.id.sessions.yaml' - /api/v2/users/{object_id}/sql-admin-rights: - $ref: './paths/users.users.id.sql-admin-rights.yaml' - - # groups - /api/v2/groups/{object_id}: - $ref: './paths/groups.groups.id.yaml' - /api/v2/groups/{object_id}/admin-rights: - $ref: './paths/groups.groups.id.admins-rights.yaml' - /api/v2/groups/{object_id}/controllables: - $ref: './paths/groups.groups.id.controllables.yaml' - /api/v2/groups/{object_id}/controllers: - $ref: './paths/groups.groups.id.controllers.yaml' - /api/v2/groups/{object_id}/dcom-rights: - $ref: './paths/groups.groups.id.dcom-rights.yaml' - /api/v2/groups/{object_id}/members: - $ref: './paths/groups.groups.id.members.yaml' - /api/v2/groups/{object_id}/memberships: - $ref: './paths/groups.groups.id.memberships.yaml' - /api/v2/groups/{object_id}/ps-remote-rights: - $ref: './paths/groups.groups.id.ps-remote-rights.yaml' - /api/v2/groups/{object_id}/rdp-rights: - $ref: './paths/groups.groups.id.rdp-rights.yaml' - /api/v2/groups/{object_id}/sessions: - $ref: './paths/groups.groups.id.sessions.yaml' - - # data quality - /api/v2/completeness: - $ref: './paths/data-quality.completeness.yaml' - /api/v2/ad-domains/{domain_id}/data-quality-stats: - $ref: './paths/data-quality.ad-domains.id.data-quality-stats.yaml' - /api/v2/azure-tenants/{tenant_id}/data-quality-stats: - $ref: './paths/data-quality.azure-tenants.id.data-quality-stats.yaml' - /api/v2/platform/{platform_id}/data-quality-stats: - $ref: './paths/data-quality.platform.id.data-quality-stats.yaml' - /api/v2/clear-database: - $ref: './paths/data-quality.clear-database.yaml' - - # datapipe - /api/v2/datapipe/status: - $ref: './paths/datapipe.datapipe.status.yaml' - /api/v2/analysis: - $ref: './paths/datapipe.analysis.yaml' - - ## - # Enterprise Endpoints - ## - - # auth - /api/v2/accept-eula: - $ref: './paths/eula.accept-eula.yaml' - - # # bhe user management (deprecated) - # /api/v2/bhe-users: - # $ref: './paths/bhe-users/bhe-users.yaml' - # /api/v2/bhe-users/{user_id}: - # $ref: './paths/bhe-users/bhe-users.id.yaml' - # /api/v2/bhe-users/{user_id}/secret: - # $ref: './paths/bhe-users/bhe-users.id.secret.yaml' - # /api/v2/bhe-users/{user_id}/mfa: - # $ref: './paths/bhe-users/bhe-users.id.mfa.yaml' - # /api/v2/bhe-users/{user_id}/mfa-activation: - # $ref: './paths/bhe-users/bhe-users.id.mfa-activation.yaml' - - # analysis - /api/v2/meta-nodes/{domain_id}: - $ref: './paths/analysis.meta-nodes.id.yaml' - /api/v2/meta-trees/{domain_id}: - $ref: './paths/analysis.meta-trees.id.yaml' - /api/v2/asset-groups/{asset_group_id}/combo-node: - $ref: './paths/analysis.asset-groups.id.combo-node.yaml' - - # client ingest - /api/v2/ingest: - $ref: './paths/client-ingest.ingest.yaml' - - # clients - /api/v2/clients: - $ref: './paths/clients.clients.yaml' - /api/v2/clients/error: - $ref: './paths/clients.clients.error.yaml' - /api/v2/clients/update: - $ref: './paths/clients.clients.update.yaml' - /api/v2/clients/{client_id}: - $ref: './paths/clients.clients.id.yaml' - /api/v2/clients/{client_id}/token: - $ref: './paths/clients.clients.id.token.yaml' - /api/v2/clients/{client_id}/completed-tasks: - $ref: './paths/clients.clients.id.completed-tasks.yaml' - /api/v2/clients/{client_id}/completed-jobs: - $ref: './paths/clients.clients.id.completed-jobs.yaml' - /api/v2/clients/{client_id}/tasks: - $ref: './paths/clients.clients.id.tasks.yaml' - /api/v2/clients/{client_id}/jobs: - $ref: './paths/clients.clients.id.jobs.yaml' - - # jobs - /api/v2/jobs/available: - $ref: './paths/jobs.jobs.available.yaml' - /api/v2/jobs/finished: - $ref: './paths/jobs.jobs.finished.yaml' - /api/v2/jobs: - $ref: './paths/jobs.jobs.yaml' - /api/v2/jobs/current: - $ref: './paths/jobs.jobs.current.yaml' - /api/v2/jobs/start: - $ref: './paths/jobs.jobs.start.yaml' - /api/v2/jobs/end: - $ref: './paths/jobs.jobs.end.yaml' - /api/v2/jobs/{job_id}: - $ref: './paths/jobs.jobs.id.yaml' - /api/v2/jobs/{job_id}/cancel: - $ref: './paths/jobs.jobs.id.cancel.yaml' - /api/v2/jobs/{job_id}/log: - $ref: './paths/jobs.jobs.id.log.yaml' - - # # tasks (deprecated) - # /api/v2/tasks/available: - # $ref: './paths/tasks/tasks.available.yaml' - # /api/v2/tasks/finished: - # $ref: './paths/tasks/tasks.finished.yaml' - # /api/v2/tasks/end: - # $ref: './paths/tasks/tasks.end.yaml' - # /api/v2/tasks/start: - # $ref: './paths/tasks/tasks.start.yaml' - # /api/v2/tasks: - # $ref: './paths/tasks/tasks.yaml' - # /api/v2/tasks/{task_id}: - # $ref: './paths/tasks/tasks.id.yaml' - # /api/v2/tasks/{task_id}/cancel: - # $ref: './paths/tasks/tasks.id.cancel.yaml' - # /api/v2/tasks/{task_id}/log: - # $ref: './paths/tasks/tasks.id.log.yaml' - # /api/v2/tasks/current: - # $ref: './paths/tasks/tasks.current.yaml' - - # events - /api/v2/events: - $ref: './paths/events.events.yaml' - /api/v2/events/{event_id}: - $ref: './paths/events.events.id.yaml' - - # attack paths - /api/v2/domains/{domain_id}/attack-path-findings: - $ref: './paths/attack-paths.domains.id.attack-path-findings.yaml' -# /api/v2/domains/{environment_id}/finding-trends: -# $ref: './paths/attack-paths.environment.id.finding-trends.yaml' - /api/v2/attack-path-types: - $ref: './paths/attack-paths.attack-path-types.yaml' - /api/v2/attack-paths: - $ref: './paths/attack-paths.attack-paths.yaml' - /api/v2/domains/{domain_id}/available-types: - $ref: './paths/attack-paths.domains.id.available-types.yaml' - /api/v2/domains/{domain_id}/details: - $ref: './paths/attack-paths.domains.id.details.yaml' - /api/v2/domains/{domain_id}/sparkline: - $ref: './paths/attack-paths.domains.id.sparkline.yaml' - /api/v2/attack-paths/{attack_path_id}/acceptance: - $ref: './paths/attack-paths.attack-paths.id.acceptance.yaml' - - # risk posture - /api/v2/posture-stats: - $ref: './paths/risk-posture.posture-stats.yaml' -# /api/v2/domains/{environment_id}/posture-history/{data_type}: -# $ref: './paths/risk-posture.environment.id.posture-history.type.yaml' - - # meta entity - /api/v2/meta/{object_id}: - $ref: './paths/meta-entity.meta.id.yaml' - -components: - ## - # SECURITY - ## - securitySchemes: - # JWT Bearer token - JWTBearerToken: - description: > - `Authorization: Bearer $JWT_TOKEN` - type: http - scheme: bearer - bearerFormat: JWT - # BHE Signature - SignedRequest: - description: > - `Authorization: bhesignature $TOKEN_ID` - type: apiKey - name: Authorization - in: header - RequestDate: - description: > - `RequestDate: $RFC3339_DATETIME` - type: apiKey - name: RequestDate - in: header - HMACSignature: - description: > - `Signature: $BASE64ENCODED_HMAC_SIGNATURE` - type: apiKey - name: Signature - in: header +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# OpenAPI Spec: https://learn.openapis.org/specification/ + +openapi: 3.0.3 +servers: + - url: / + description: This is the base path for all endpoints, relative to the domain where the API is being hosted. +info: + title: BloodHound API + contact: + name: BloodHound Enterprise Support + url: https://support.bloodhoundenterprise.io/ + email: support@specterops.io + license: + name: Apache-2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + version: 'v2' + description: | + This is the API that drives BloodHound Enterprise and Community Edition. + Endpoint availability is denoted using the `Community` and `Enterprise` tags. + + Contact information listed is for BloodHound Enterprise customers. To get help with + BloodHound Community Edition, please join our + [Slack community](https://ghst.ly/BHSlack/). + + ## Authentication + + The BloodHound API supports two kinds of authentication: JWT bearer tokens and Signed Requests. + For quick tests or one-time calls, the JWT used by your browser may be the simplest route. For + more secure and long lived API integrations, the recommended option is signed requests. + + ### JWT Bearer Token + + The API will accept calls using the following header structure in the HTTP request: + ``` + Authorization: Bearer $JWT_TOKEN + ``` + If you open the Network tab within your browser, you will see calls against the API made utilizing + this structure. JWT bearer tokens are supported by the BloodHound API, however it is recommended + they only be used for temporary access. JWT tokens expire after a set amount of time and require + re-authentication using secret credentials. + + ### Signed Requests + + Signed requests are the recommended form of authentication for the BloodHound API. Not only are + signed requests better for long lived integrations, they also provide more security for the + requests being sent. They provide authentication of the client, as well as verification of request + integrity when received by the server. + + Signed requests consist of three main parts: The client token ID, the request timestamp, and a + base64 encoded HMAC signature. These three pieces of information are sent with the request using + the following header structure: + + ``` + Authorization: bhesignature $TOKEN_ID + RequestDate: $RFC3339_DATETIME + Signature: $BASE64ENCODED_HMAC_SIGNATURE + ``` + + To use signed requests, you will need to generate an API token. Each API token generated in the + BloodHound API comes with two parts: The Token ID, which is used in the `Authorization` header, + and the Token Key, which is used as part of the HMAC hashing process. The token ID should be + considered as public (like a username) and the token key should be considered secret (like a + password). Once an API token is generated, you can use the key to sign requests. + + For more documentation about how to work with authentication in the API, including examples + of how to generate an API token in the BloodHound UI, please refer to this support doc: + [Working with the BloodHound API](https://support.bloodhoundenterprise.io/hc/en-us/articles/11311053342619-Working-with-the-BloodHound-API). + + #### Signed Request Pseudo-code Example + + First, a digest is initiated with HMAC-SHA-256 using the token key as the digest key: + ```python + digester = hmac.new(sha256, api_token_key) + ``` + + OperationKey is the first HMAC digest link in the signature chain. This prevents replay attacks that + seek to modify the request method or URI. It is composed of concatenating the request method and + the request URI with no delimiter and computing the HMAC digest using the token key as the digest + secret: + ```python + # Example: GET /api/v2/test/resource HTTP/1.1 + # Signature Component: GET/api/v2/test/resource + digester.write(request_method + request_uri) + + # Update the digester for further chaining + digester = hmac.New(sha256, digester.hash()) + ``` + + DateKey is the next HMAC digest link in the signature chain. This encodes the RFC3339 + formatted datetime value as part of the signature to the hour to prevent replay + attacks that are older than max two hours. This value is added to the signature chain + by cutting off all values from the RFC3339 formatted datetime from the hours value + forward: + ```python + # Example: 2020-12-01T23:59:60Z + # Signature Component: 2020-12-01T23 + request_datetime = date.now() + digester.write(request_datetime[:13]) + + # Update the digester for further chaining + digester = hmac.New(sha256, digester.hash()) + ``` + + Body signing is the last HMAC digest link in the signature chain. This encodes the + request body as part of the signature to prevent replay attacks that seek to modify + the payload of a signed request. In the case where there is no body content the + HMAC digest is computed anyway, simply with no values written to the digester: + ```python + if request.body is not empty: + digester.write(request.body) + ``` + + Finally, base64 encode the final hash and write the three required headers before + sending the request: + ```python + encoded_hash = base64_encode(digester.hash()) + request.header.write('Authorization', 'bhesignature ' + token_id) + request.header.write('RequestDate', request_datetime) + request.header.write('Signature', encoded_hash) + ``` + +security: + - JWTBearerToken: [] + - SignedRequest: [] + RequestDate: [] + HMACSignature: [] + +## +# This section is used by ReDoc to organize endpoint groups in the sidebar. +# Two top level tag groups help users find Community and Enterprise features. +# +## ALL TAGS MUST BE LISTED HERE TO SHOW UP IN THE NAVIGATION. ## +x-tagGroups: + - name: Community & Enterprise + tags: + - Auth + - Roles + - Permissions + - API Tokens + - BloodHound Users + - Collectors + - Collection Uploads + - API Info + - Search + - Audit + - Config + - Asset Isolation + - Graph + - Azure Entities + - AD Base Entities + - Computers + - Containers + - Domains + - GPOs + - AIA CAs + - Root CAs + - Enterprise CAs + - NT Auth Stores + - Cert Templates + - OUs + - AD Users + - Groups + - Data Quality + - Datapipe + - Cypher + - name: Enterprise Only + tags: + - EULA + - BHE Users + - Analysis + - Client Ingest + - Clients + - Jobs + - Tasks + - Events (Schedules) + - Attack Paths + - Risk Posture + - Meta Entities + +paths: + ## + # Community + Enterprise Endpoints + ## + + # auth + /api/v2/login: + $ref: './paths/auth.login.yaml' + /api/v2/logout: + $ref: './paths/auth.logout.yaml' + /api/v2/self: + $ref: './paths/auth.self.yaml' + /api/v2/saml: + $ref: './paths/auth.saml.yaml' + /api/v2/saml/sso: + $ref: './paths/auth.saml.sso.yaml' + /api/v2/saml/providers: + $ref: './paths/auth.saml.providers.yaml' + /api/v2/saml/providers/{saml_provider_id}: + $ref: './paths/auth.saml.providers.id.yaml' + + # Undocumented, not sure how this endpoint actually works. + #/api/v2/login/saml/{saml_provider_name}: + + # sso + /api/v2/sso-providers: + $ref: './paths/sso.sso-providers.yaml' + /api/v2/sso-providers/oidc: + $ref: './paths/sso.sso-providers.oidc.yaml' + /api/v2/sso-providers/saml: + $ref: './paths/auth.sso-providers.saml.yaml' + /api/v2/sso-providers/{sso_provider_id}: + $ref: './paths/sso.sso-providers.id.yaml' + /api/v2/sso-providers/{sso_provider_id}/signing-certificate: + $ref: './paths/sso.sso-providers.id.signing-certificate.yaml' + + # permissions + /api/v2/permissions: + $ref: './paths/permissions.permissions.yaml' + /api/v2/permissions/{permission_id}: + $ref: './paths/permissions.permissions.id.yaml' + + # roles + /api/v2/roles: + $ref: './paths/roles.roles.yaml' + /api/v2/roles/{role_id}: + $ref: './paths/roles.roles.id.yaml' + + # api tokens + /api/v2/tokens: + $ref: './paths/tokens.tokens.yaml' + /api/v2/tokens/{token_id}: + $ref: './paths/tokens.tokens.id.yaml' + + # user management + /api/v2/bloodhound-users: + $ref: './paths/bh-users.bloodhound-users.yaml' + /api/v2/bloodhound-users/{user_id}: + $ref: './paths/bh-users.bloodhound-users.id.yaml' + /api/v2/bloodhound-users/{user_id}/secret: + $ref: './paths/bh-users.bloodhound-users.id.secret.yaml' + /api/v2/bloodhound-users/{user_id}/mfa: + $ref: './paths/bh-users.bloodhound-users.id.mfa.yaml' + /api/v2/bloodhound-users/{user_id}/mfa-activation: + $ref: './paths/bh-users.bloodhound-users.id.mfa-activation.yaml' + + # collectors + /api/v2/collectors/{collector_type}: + $ref: './paths/collectors.collectors.type.yaml' + /api/v2/collectors/{collector_type}/{release_tag}: + $ref: './paths/collectors.collectors.type.tag.yaml' + /api/v2/collectors/{collector_type}/{release_tag}/checksum: + $ref: './paths/collectors.collectors.type.tag.checksum.yaml' + + # collection uploads + /api/v2/file-upload: + $ref: './paths/collection-uploads.file-upload.yaml' + /api/v2/file-upload/start: + $ref: './paths/collection-uploads.file-upload.start.yaml' + /api/v2/file-upload/{file_upload_job_id}: + $ref: './paths/collection-uploads.file-upload.id.yaml' + /api/v2/file-upload/{file_upload_job_id}/end: + $ref: './paths/collection-uploads.file-upload.id.end.yaml' + /api/v2/file-upload/accepted-types: + $ref: './paths/collection-uploads.file-upload.accepted-types.yaml' + + # api info + /api/version: + $ref: './paths/api-info.version.yaml' + /api/v2/spec/openapi.yaml: + $ref: './paths/api-info.spec.yaml' + + # search + /api/v2/search: + $ref: './paths/search.search.yaml' + /api/v2/available-domains: + $ref: './paths/search.available-domains.yaml' + + # audit + /api/v2/audit: + $ref: './paths/audit.audit.yaml' + + # config + /api/v2/config: + $ref: './paths/config.config.yaml' + /api/v2/features: + $ref: './paths/config.features.yaml' + /api/v2/features/{feature_id}/toggle: + $ref: './paths/config.features.id.toggle.yaml' + + # asset isolation + /api/v2/asset-groups: + $ref: './paths/asset-isolation.asset-groups.yaml' + /api/v2/asset-groups/{asset_group_id}: + $ref: './paths/asset-isolation.asset-groups.id.yaml' + /api/v2/asset-groups/{asset_group_id}/collections: + $ref: './paths/asset-isolation.asset-groups.id.collections.yaml' + /api/v2/asset-groups/{asset_group_id}/selectors: + $ref: './paths/asset-isolation.asset-groups.id.selectors.yaml' + /api/v2/asset-groups/{asset_group_id}/selectors/{asset_group_selector_id}: + $ref: './paths/asset-isolation.asset-groups.id.selectors.id.yaml' + /api/v2/asset-groups/{asset_group_id}/custom-selectors: + $ref: './paths/asset-isolation.asset-groups.id.custom-selectors.yaml' + /api/v2/asset-groups/{asset_group_id}/members: + $ref: './paths/asset-isolation.asset-groups.id.members.yaml' + /api/v2/asset-groups/{asset_group_id}/members/counts: + $ref: './paths/asset-isolation.asset-groups.id.members.counts.yaml' + + # graph + /api/v2/pathfinding: + $ref: './paths/graph.pathfinding.yaml' + /api/v2/graph-search: + $ref: './paths/graph.graph-search.yaml' + /api/v2/graphs/shortest-path: + $ref: './paths/graph.graphs.shortest-path.yaml' + /api/v2/graphs/edge-composition: + $ref: './paths/graph.graphs.edge-composition.yaml' + + # cypher + /api/v2/saved-queries: + $ref: './paths/cypher.saved-queries.yaml' + /api/v2/saved-queries/{saved_query_id}: + $ref: './paths/cypher.saved-queries.id.yaml' + /api/v2/saved-queries/{saved_query_id}/permissions: + $ref: './paths/cypher.saved-queries.id.permissions.yaml' + /api/v2/graphs/cypher: + $ref: './paths/cypher.graphs.cypher.yaml' + + # azure entities + /api/v2/azure/{entity_type}: + $ref: './paths/azure.entity.yaml' + + # ad base entities + /api/v2/base/{object_id}: + $ref: './paths/base.base.id.yaml' + /api/v2/base/{object_id}/controllables: + $ref: './paths/base.base.id.controllables.yaml' + /api/v2/base/{object_id}/controllers: + $ref: './paths/base.base.id.controllers.yaml' + + # computers + /api/v2/computers/{object_id}: + $ref: './paths/computers.computers.id.yaml' + /api/v2/computers/{object_id}/admin-rights: + $ref: './paths/computers.computers.id.admin-rights.yaml' + /api/v2/computers/{object_id}/admin-users: + $ref: './paths/computers.computers.id.admin-users.yaml' + /api/v2/computers/{object_id}/constrained-delegation-rights: + $ref: './paths/computers.computers.id.constrained-delegation-rights.yaml' + /api/v2/computers/{object_id}/constrained-users: + $ref: './paths/computers.computers.id.constrained-users.yaml' + /api/v2/computers/{object_id}/controllables: + $ref: './paths/computers.computers.id.controllables.yaml' + /api/v2/computers/{object_id}/controllers: + $ref: './paths/computers.computers.id.controllers.yaml' + /api/v2/computers/{object_id}/dcom-rights: + $ref: './paths/computers.computers.id.dcom-rights.yaml' + /api/v2/computers/{object_id}/dcom-users: + $ref: './paths/computers.computers.id.dcom-users.yaml' + /api/v2/computers/{object_id}/group-membership: + $ref: './paths/computers.computers.id.group-membership.yaml' + /api/v2/computers/{object_id}/ps-remote-rights: + $ref: './paths/computers.computers.id.ps-remote-rights.yaml' + /api/v2/computers/{object_id}/ps-remote-users: + $ref: './paths/computers.computers.id.ps-remote-users.yaml' + /api/v2/computers/{object_id}/rdp-rights: + $ref: './paths/computers.computers.id.rdp-rights.yaml' + /api/v2/computers/{object_id}/rdp-users: + $ref: './paths/computers.computers.id.rdp-users.yaml' + /api/v2/computers/{object_id}/sessions: + $ref: './paths/computers.computers.id.sessions.yaml' + /api/v2/computers/{object_id}/sql-admins: + $ref: './paths/computers.computers.id.sql-admins.yaml' + + # containers + /api/v2/containers/{object_id}: + $ref: './paths/containers.containers.id.yaml' + /api/v2/containers/{object_id}/controllers: + $ref: './paths/containers.containers.id.controllers.yaml' + + # domains + /api/v2/domains/{object_id}: + $ref: './paths/domains.domains.id.yaml' + /api/v2/domains/{object_id}/computers: + $ref: './paths/domains.domains.id.computers.yaml' + /api/v2/domains/{object_id}/controllers: + $ref: './paths/domains.domains.id.controllers.yaml' + /api/v2/domains/{object_id}/dc-syncers: + $ref: './paths/domains.domains.id.dc-syncers.yaml' + /api/v2/domains/{object_id}/foreign-admins: + $ref: './paths/domains.domains.id.foreign-admins.yaml' + /api/v2/domains/{object_id}/foreign-gpo-controllers: + $ref: './paths/domains.domains.id.foreign-gpo-controllers.yaml' + /api/v2/domains/{object_id}/foreign-groups: + $ref: './paths/domains.domains.id.foreign-groups.yaml' + /api/v2/domains/{object_id}/foreign-users: + $ref: './paths/domains.domains.id.foreign-users.yaml' + /api/v2/domains/{object_id}/gpos: + $ref: './paths/domains.domains.id.gpos.yaml' + /api/v2/domains/{object_id}/groups: + $ref: './paths/domains.domains.id.groups.yaml' + /api/v2/domains/{object_id}/inbound-trusts: + $ref: './paths/domains.domains.id.inbound-trusts.yaml' + /api/v2/domains/{object_id}/linked-gpos: + $ref: './paths/domains.domains.id.linked-gpos.yaml' + /api/v2/domains/{object_id}/ous: + $ref: './paths/domains.domains.id.ous.yaml' + /api/v2/domains/{object_id}/outbound-trusts: + $ref: './paths/domains.domains.id.outbound-trusts.yaml' + /api/v2/domains/{object_id}/users: + $ref: './paths/domains.domains.id.users.yaml' + + # gpos + /api/v2/gpos/{object_id}: + $ref: './paths/gpos.gpos.id.yaml' + /api/v2/gpos/{object_id}/computers: + $ref: './paths/gpos.gpos.id.computers.yaml' + /api/v2/gpos/{object_id}/controllers: + $ref: './paths/gpos.gpos.id.controllers.yaml' + /api/v2/gpos/{object_id}/ous: + $ref: './paths/gpos.gpos.id.ous.yaml' + /api/v2/gpos/{object_id}/tier-zero: + $ref: './paths/gpos.gpos.id.tier-zero.yaml' + /api/v2/gpos/{object_id}/users: + $ref: './paths/gpos.gpos.id.users.yaml' + + # aiacas + /api/v2/aiacas/{object_id}: + $ref: './paths/aiacas.aiaca.id.yaml' + /api/v2/aiacas/{object_id}/controllers: + $ref: './paths/aiacas.aiaca.id.controllers.yaml' + + # root cas + /api/v2/rootcas/{object_id}: + $ref: './paths/rootcas.rootcas.id.yaml' + /api/v2/rootcas/{object_id}/controllers: + $ref: './paths/rootcas.rootcas.id.controllers.yaml' + + # enterprise cas + /api/v2/enterprisecas/{object_id}: + $ref: './paths/enterprisecas.enterprisecas.id.controllers.yaml' + /api/v2/enterprisecas/{object_id}/controllers: + $ref: './paths/enterprisecas.enterprisecas.id.yaml' + + # nt auth stores + /api/v2/ntauthstores/{object_id}: + $ref: './paths/ntauthstores.ntauthstores.id.yaml' + /api/v2/ntauthstores/{object_id}/controllers: + $ref: './paths/ntauthstores.ntauthstores.id.controllers.yaml' + + # cert templates + /api/v2/certtemplates/{object_id}: + $ref: './paths/certtemplate.certtemplates.id.yaml' + /api/v2/certtemplates/{object_id}/controllers: + $ref: './paths/certtemplate.certtemplates.id.controllers.yaml' + + # ous + /api/v2/ous/{object_id}: + $ref: './paths/ous.ous.id.yaml' + /api/v2/ous/{object_id}/computers: + $ref: './paths/ous.ous.id.computers.yaml' + /api/v2/ous/{object_id}/gpos: + $ref: './paths/ous.ous.id.gpos.yaml' + /api/v2/ous/{object_id}/groups: + $ref: './paths/ous.ous.id.groups.yaml' + /api/v2/ous/{object_id}/users: + $ref: './paths/ous.ous.id.users.yaml' + + # ad users + /api/v2/users/{object_id}: + $ref: './paths/users.users.id.yaml' + /api/v2/users/{object_id}/admin-rights: + $ref: './paths/users.users.id.admin-rights.yaml' + /api/v2/users/{object_id}/constrained-delegation-rights: + $ref: './paths/users.users.id.constrained-delegation-rights.yaml' + /api/v2/users/{object_id}/controllables: + $ref: './paths/users.users.id.controllables.yaml' + /api/v2/users/{object_id}/controllers: + $ref: './paths/users.users.id.controllers.yaml' + /api/v2/users/{object_id}/dcom-rights: + $ref: './paths/users.users.id.dcom-rights.yaml' + /api/v2/users/{object_id}/memberships: + $ref: './paths/users.users.id.memberships.yaml' + /api/v2/users/{object_id}/ps-remote-rights: + $ref: './paths/users.users.id.ps-remote-rights.yaml' + /api/v2/users/{object_id}/rdp-rights: + $ref: './paths/users.users.id.rdp-rights.yaml' + /api/v2/users/{object_id}/sessions: + $ref: './paths/users.users.id.sessions.yaml' + /api/v2/users/{object_id}/sql-admin-rights: + $ref: './paths/users.users.id.sql-admin-rights.yaml' + + # groups + /api/v2/groups/{object_id}: + $ref: './paths/groups.groups.id.yaml' + /api/v2/groups/{object_id}/admin-rights: + $ref: './paths/groups.groups.id.admins-rights.yaml' + /api/v2/groups/{object_id}/controllables: + $ref: './paths/groups.groups.id.controllables.yaml' + /api/v2/groups/{object_id}/controllers: + $ref: './paths/groups.groups.id.controllers.yaml' + /api/v2/groups/{object_id}/dcom-rights: + $ref: './paths/groups.groups.id.dcom-rights.yaml' + /api/v2/groups/{object_id}/members: + $ref: './paths/groups.groups.id.members.yaml' + /api/v2/groups/{object_id}/memberships: + $ref: './paths/groups.groups.id.memberships.yaml' + /api/v2/groups/{object_id}/ps-remote-rights: + $ref: './paths/groups.groups.id.ps-remote-rights.yaml' + /api/v2/groups/{object_id}/rdp-rights: + $ref: './paths/groups.groups.id.rdp-rights.yaml' + /api/v2/groups/{object_id}/sessions: + $ref: './paths/groups.groups.id.sessions.yaml' + + # data quality + /api/v2/completeness: + $ref: './paths/data-quality.completeness.yaml' + /api/v2/ad-domains/{domain_id}/data-quality-stats: + $ref: './paths/data-quality.ad-domains.id.data-quality-stats.yaml' + /api/v2/azure-tenants/{tenant_id}/data-quality-stats: + $ref: './paths/data-quality.azure-tenants.id.data-quality-stats.yaml' + /api/v2/platform/{platform_id}/data-quality-stats: + $ref: './paths/data-quality.platform.id.data-quality-stats.yaml' + /api/v2/clear-database: + $ref: './paths/data-quality.clear-database.yaml' + + # datapipe + /api/v2/datapipe/status: + $ref: './paths/datapipe.datapipe.status.yaml' + /api/v2/analysis: + $ref: './paths/datapipe.analysis.yaml' + + ## + # Enterprise Endpoints + ## + + # auth + /api/v2/accept-eula: + $ref: './paths/eula.accept-eula.yaml' + + # # bhe user management (deprecated) + # /api/v2/bhe-users: + # $ref: './paths/bhe-users/bhe-users.yaml' + # /api/v2/bhe-users/{user_id}: + # $ref: './paths/bhe-users/bhe-users.id.yaml' + # /api/v2/bhe-users/{user_id}/secret: + # $ref: './paths/bhe-users/bhe-users.id.secret.yaml' + # /api/v2/bhe-users/{user_id}/mfa: + # $ref: './paths/bhe-users/bhe-users.id.mfa.yaml' + # /api/v2/bhe-users/{user_id}/mfa-activation: + # $ref: './paths/bhe-users/bhe-users.id.mfa-activation.yaml' + + # analysis + /api/v2/meta-nodes/{domain_id}: + $ref: './paths/analysis.meta-nodes.id.yaml' + /api/v2/meta-trees/{domain_id}: + $ref: './paths/analysis.meta-trees.id.yaml' + /api/v2/asset-groups/{asset_group_id}/combo-node: + $ref: './paths/analysis.asset-groups.id.combo-node.yaml' + + # client ingest + /api/v2/ingest: + $ref: './paths/client-ingest.ingest.yaml' + + # clients + /api/v2/clients: + $ref: './paths/clients.clients.yaml' + /api/v2/clients/error: + $ref: './paths/clients.clients.error.yaml' + /api/v2/clients/update: + $ref: './paths/clients.clients.update.yaml' + /api/v2/clients/{client_id}: + $ref: './paths/clients.clients.id.yaml' + /api/v2/clients/{client_id}/token: + $ref: './paths/clients.clients.id.token.yaml' + /api/v2/clients/{client_id}/completed-tasks: + $ref: './paths/clients.clients.id.completed-tasks.yaml' + /api/v2/clients/{client_id}/completed-jobs: + $ref: './paths/clients.clients.id.completed-jobs.yaml' + /api/v2/clients/{client_id}/tasks: + $ref: './paths/clients.clients.id.tasks.yaml' + /api/v2/clients/{client_id}/jobs: + $ref: './paths/clients.clients.id.jobs.yaml' + + # jobs + /api/v2/jobs/available: + $ref: './paths/jobs.jobs.available.yaml' + /api/v2/jobs/finished: + $ref: './paths/jobs.jobs.finished.yaml' + /api/v2/jobs: + $ref: './paths/jobs.jobs.yaml' + /api/v2/jobs/current: + $ref: './paths/jobs.jobs.current.yaml' + /api/v2/jobs/start: + $ref: './paths/jobs.jobs.start.yaml' + /api/v2/jobs/end: + $ref: './paths/jobs.jobs.end.yaml' + /api/v2/jobs/{job_id}: + $ref: './paths/jobs.jobs.id.yaml' + /api/v2/jobs/{job_id}/cancel: + $ref: './paths/jobs.jobs.id.cancel.yaml' + /api/v2/jobs/{job_id}/log: + $ref: './paths/jobs.jobs.id.log.yaml' + + # # tasks (deprecated) + # /api/v2/tasks/available: + # $ref: './paths/tasks/tasks.available.yaml' + # /api/v2/tasks/finished: + # $ref: './paths/tasks/tasks.finished.yaml' + # /api/v2/tasks/end: + # $ref: './paths/tasks/tasks.end.yaml' + # /api/v2/tasks/start: + # $ref: './paths/tasks/tasks.start.yaml' + # /api/v2/tasks: + # $ref: './paths/tasks/tasks.yaml' + # /api/v2/tasks/{task_id}: + # $ref: './paths/tasks/tasks.id.yaml' + # /api/v2/tasks/{task_id}/cancel: + # $ref: './paths/tasks/tasks.id.cancel.yaml' + # /api/v2/tasks/{task_id}/log: + # $ref: './paths/tasks/tasks.id.log.yaml' + # /api/v2/tasks/current: + # $ref: './paths/tasks/tasks.current.yaml' + + # events + /api/v2/events: + $ref: './paths/events.events.yaml' + /api/v2/events/{event_id}: + $ref: './paths/events.events.id.yaml' + + # attack paths + /api/v2/domains/{domain_id}/attack-path-findings: + $ref: './paths/attack-paths.domains.id.attack-path-findings.yaml' +# /api/v2/domains/{environment_id}/finding-trends: +# $ref: './paths/attack-paths.environment.id.finding-trends.yaml' + /api/v2/attack-path-types: + $ref: './paths/attack-paths.attack-path-types.yaml' + /api/v2/attack-paths: + $ref: './paths/attack-paths.attack-paths.yaml' + /api/v2/domains/{domain_id}/available-types: + $ref: './paths/attack-paths.domains.id.available-types.yaml' + /api/v2/domains/{domain_id}/details: + $ref: './paths/attack-paths.domains.id.details.yaml' + /api/v2/domains/{domain_id}/sparkline: + $ref: './paths/attack-paths.domains.id.sparkline.yaml' + /api/v2/attack-paths/{attack_path_id}/acceptance: + $ref: './paths/attack-paths.attack-paths.id.acceptance.yaml' + + # risk posture + /api/v2/posture-stats: + $ref: './paths/risk-posture.posture-stats.yaml' +# /api/v2/domains/{environment_id}/posture-history/{data_type}: +# $ref: './paths/risk-posture.environment.id.posture-history.type.yaml' + + # meta entity + /api/v2/meta/{object_id}: + $ref: './paths/meta-entity.meta.id.yaml' + +components: + ## + # SECURITY + ## + securitySchemes: + # JWT Bearer token + JWTBearerToken: + description: > + `Authorization: Bearer $JWT_TOKEN` + type: http + scheme: bearer + bearerFormat: JWT + # BHE Signature + SignedRequest: + description: > + `Authorization: bhesignature $TOKEN_ID` + type: apiKey + name: Authorization + in: header + RequestDate: + description: > + `RequestDate: $RFC3339_DATETIME` + type: apiKey + name: RequestDate + in: header + HMACSignature: + description: > + `Signature: $BASE64ENCODED_HMAC_SIGNATURE` + type: apiKey + name: Signature + in: header diff --git a/packages/go/openapi/src/paths/auth.saml.providers.id.yaml b/packages/go/openapi/src/paths/auth.saml.providers.id.yaml index b5545439fe..50b0e8852b 100644 --- a/packages/go/openapi/src/paths/auth.saml.providers.id.yaml +++ b/packages/go/openapi/src/paths/auth.saml.providers.id.yaml @@ -26,8 +26,10 @@ parameters: get: operationId: GetSamlProvider summary: Get SAML Provider - description: Get the service and identity provider configuration details for a - SAML authentication provider. + description: > + **Deprecated**: This endpoint will no longer be supported in a future release. + Please use `GET /api/v2/sso-providers` to list all SAML providers instead. + deprecated: true tags: - Auth - Community @@ -55,7 +57,10 @@ get: delete: operationId: DeleteSamlProvider summary: Delete a SAML Provider - description: Deletes an existing BloodHound SAML provider. + description: > + **Deprecated**: This endpoint will no longer be supported in a future release. + Please use `DELETE /api/v2/sso-providers/{sso_provider_id}` instead. + deprecated: true tags: - Auth - Community diff --git a/packages/go/openapi/src/paths/auth.saml.providers.yaml b/packages/go/openapi/src/paths/auth.saml.providers.yaml index 6a1edd47c0..e1cff664d7 100644 --- a/packages/go/openapi/src/paths/auth.saml.providers.yaml +++ b/packages/go/openapi/src/paths/auth.saml.providers.yaml @@ -17,7 +17,10 @@ post: operationId: CreateSamlProvider summary: Create a New SAML Provider from Metadata - description: Creates a new SAML provider with the given name and metadata XML. + description: > + **Deprecated**: This endpoint will no longer be supported in a future release. + Please use `POST /api/v2/sso-providers/saml` instead. + deprecated: true tags: - Auth - Community diff --git a/packages/go/openapi/src/paths/auth.saml.sso.yaml b/packages/go/openapi/src/paths/auth.saml.sso.yaml index 2608443212..b8ff72c8e7 100644 --- a/packages/go/openapi/src/paths/auth.saml.sso.yaml +++ b/packages/go/openapi/src/paths/auth.saml.sso.yaml @@ -19,7 +19,10 @@ parameters: get: operationId: GetSamlSignSignOnEndpoints summary: Get all SAML sign on endpoints - description: Get all SAML sign on endpoints + description: > + **Deprecated**: This endpoint will no longer be supported in a future release. + Please use `GET /api/v2/sso-providers` instead to list available SSO endpoints. + deprecated: true tags: - Auth - Community diff --git a/packages/go/openapi/src/paths/auth.saml.yaml b/packages/go/openapi/src/paths/auth.saml.yaml index 81a2279e40..472eea0167 100644 --- a/packages/go/openapi/src/paths/auth.saml.yaml +++ b/packages/go/openapi/src/paths/auth.saml.yaml @@ -19,7 +19,10 @@ parameters: get: operationId: ListSamlProviders summary: List SAML Providers - description: List all registered SAML providers. + description: > + **Deprecated**: This endpoint will no longer be supported in a future release. + Please use `GET /api/v2/sso-providers` instead. + deprecated: true tags: - Auth - Community diff --git a/packages/go/openapi/src/paths/auth.sso-providers.saml.yaml b/packages/go/openapi/src/paths/auth.sso-providers.saml.yaml new file mode 100644 index 0000000000..70c254f007 --- /dev/null +++ b/packages/go/openapi/src/paths/auth.sso-providers.saml.yaml @@ -0,0 +1,57 @@ +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +post: + operationId: CreateSSOSAMLProvider + summary: Create a New SAML Provider from Metadata + description: Creates a new SAML provider with the given name and metadata XML. + tags: + - Auth + - Community + - Enterprise + requestBody: + required: true + content: + multipart/form-data: + schema: + properties: + name: + type: string + description: Name of the new SAML provider. + metadata: + type: string + format: binary + description: Metadata XML file. + responses: + 200: + description: OK + content: + application/json: + schema: + type: object + properties: + data: + $ref: './../schemas/model.saml-provider.yaml' + 400: + $ref: './../responses/bad-request.yaml' + 401: + $ref: './../responses/unauthorized.yaml' + 403: + $ref: './../responses/forbidden.yaml' + 429: + $ref: './../responses/too-many-requests.yaml' + 500: + $ref: './../responses/internal-server-error.yaml' diff --git a/packages/javascript/bh-shared-ui/src/views/SSOConfiguration/SSOConfiguration.test.tsx b/packages/javascript/bh-shared-ui/src/views/SSOConfiguration/SSOConfiguration.test.tsx index 685d79dfab..c20baa6a91 100644 --- a/packages/javascript/bh-shared-ui/src/views/SSOConfiguration/SSOConfiguration.test.tsx +++ b/packages/javascript/bh-shared-ui/src/views/SSOConfiguration/SSOConfiguration.test.tsx @@ -88,10 +88,13 @@ const server = setupServer( ); }), - rest.post('/api/v2/saml/providers', (req, res, ctx) => { - ssoProviders.push(newSAMLProvider); - return res(ctx.json({ ...newSAMLProvider, ...(newSAMLProvider.details as SAMLProviderInfo) })); - }) + rest.post( + '/api/v2/sso-providers/saml', + (req, res, ctx) => { + ssoProviders.push(newSAMLProvider); + return res(ctx.json({ ...newSAMLProvider, ...(newSAMLProvider.details as SAMLProviderInfo) })); + } + ) ); beforeEach(() => { diff --git a/packages/javascript/js-client-library/src/client.ts b/packages/javascript/js-client-library/src/client.ts index eaf6c08b83..313f8109b3 100644 --- a/packages/javascript/js-client-library/src/client.ts +++ b/packages/javascript/js-client-library/src/client.ts @@ -640,18 +640,11 @@ class BHEAPIClient { logout = (options?: types.RequestOptions) => this.baseClient.post('/api/v2/logout', options); - listSAMLSignOnEndpoints = (options?: types.RequestOptions) => this.baseClient.get('/api/v2/saml/sso', options); - - listSAMLProviders = (options?: types.RequestOptions) => this.baseClient.get(`/api/v2/saml`, options); - - getSAMLProvider = (samlProviderId: string, options?: types.RequestOptions) => - this.baseClient.get(`/api/v2/saml/providers/${samlProviderId}`, options); - createSAMLProviderFromFile = (data: { name: string; metadata: File }, options?: types.RequestOptions) => { const formData = new FormData(); formData.append('name', data.name); formData.append('metadata', data.metadata); - return this.baseClient.post(`/api/v2/saml/providers`, formData, options); + return this.baseClient.post(`/api/v2/sso-providers/saml`, formData, options); }; updateSAMLProviderFromFile = ( @@ -669,30 +662,6 @@ class BHEAPIClient { return this.baseClient.patch(`/api/v2/sso-providers/${ssoProviderId}`, formData, options); }; - validateSAMLProvider = ( - data: { - name: string; - displayName: string; - signingCertificate: string; - issuerUri: string; - singleSignOnUri: string; - principalAttributeMappings: string[]; - }, - options?: types.RequestOptions - ) => - this.baseClient.post( - `/api/v2/saml/validate-idp`, - { - name: data.name, - display_name: data.displayName, - signing_certificate: data.signingCertificate, - issuer_uri: data.issuerUri, - single_signon_uri: data.singleSignOnUri, - principal_attribute_mappings: data.principalAttributeMappings, - }, - options - ); - getSAMLProviderSigningCertificate = (ssoProviderId: types.SSOProvider['id'], options?: types.RequestOptions) => this.baseClient.get(`/api/v2/sso-providers/${ssoProviderId}/signing-certificate`, options);