diff --git a/backend/spec.json b/backend/spec.json index 1f9c0a498..adb8af411 100644 --- a/backend/spec.json +++ b/backend/spec.json @@ -16,6 +16,165 @@ } ], "paths": { + "/api/v1/identities/": { + "post": { + "summary": "Create identity", + "description": "Create identity", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/components/schemas/Identity" + } + }, + "description": "Details of the created identity" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of entity to create", + "example": "development" + }, + "organizationId": { + "type": "string", + "description": "ID of organization where to create identity", + "example": "dev-environment" + }, + "role": { + "type": "string", + "description": "Role to assume for organization membership", + "example": "no-access" + } + }, + "required": [ + "name", + "organizationId", + "role" + ] + } + } + } + } + } + }, + "/api/v1/identities/{identityId}": { + "patch": { + "summary": "Update identity", + "description": "Update identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity to update" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/components/schemas/Identity" + } + }, + "description": "Details of the updated identity" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of entity to update to", + "example": "development" + }, + "role": { + "type": "string", + "description": "Role to update to for organization membership", + "example": "no-access" + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete identity", + "description": "Delete identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/components/schemas/Identity" + } + }, + "description": "Details of the deleted identity" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, "/api/v1/secret/{secretId}/secret-versions": { "get": { "summary": "Return secret versions", @@ -176,7 +335,25 @@ "schema": { "type": "string" }, - "description": "ID of project" + "description": "ID of project where to get secret snapshots for" + }, + { + "name": "environment", + "description": "Slug of environment where to get secret snapshots for", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "directory", + "description": "Path where to get secret snapshots for like / or /foo/bar. Default is /", + "required": false, + "in": "query", + "schema": { + "type": "string" + } }, { "name": "offset", @@ -220,7 +397,8 @@ }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ] } @@ -257,7 +435,7 @@ "schema": { "type": "string" }, - "description": "ID of project" + "description": "ID of project where to roll back" } ], "responses": { @@ -283,7 +461,8 @@ }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ], "requestBody": { @@ -293,6 +472,14 @@ "schema": { "type": "object", "properties": { + "environment": { + "type": "string", + "description": "Slug of environment where to roll back" + }, + "directory": { + "type": "string", + "description": "Path where to roll back for like / or /foo/bar. Default is /" + }, "version": { "type": "integer", "description": "Version of secret snapshot to roll back to" @@ -978,80 +1165,6 @@ } } }, - "/api/v3/service-token/me/key": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/service-token/me/token": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/service-token/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v3/service-token/{serviceTokenDataId}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "serviceTokenDataId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "serviceTokenDataId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, "/api/v1/secret-rotation-providers/{workspaceId}": { "get": { "description": "", @@ -1218,6 +1331,525 @@ } } }, + "/api/v1/auth/token/renew": { + "post": { + "summary": "Renew access token", + "description": "Renew access token", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { + "type": "string", + "description": "(Same) Access token after successful renewal" + }, + "expiresIn": { + "type": "number", + "description": "TTL of access token in seconds" + }, + "tokenType": { + "type": "string", + "description": "Type of access token (e.g. Bearer)" + } + }, + "description": "Access token and its details" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { + "type": "string", + "description": "Access token to renew", + "example": "..." + } + } + } + } + } + } + } + }, + "/api/v1/auth/universal-auth/login": { + "post": { + "summary": "Login with Universal Auth", + "description": "Login with Universal Auth", + "parameters": [ + { + "name": "user-agent", + "in": "header", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { + "type": "string", + "description": "Access token issued after successful login" + }, + "expiresIn": { + "type": "number", + "description": "TTL of access token in seconds" + }, + "tokenType": { + "type": "string", + "description": "Type of access token (e.g. Bearer)" + } + }, + "description": "Access token and its details" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "description": "Client ID for identity to login with Universal Auth", + "example": "..." + }, + "clientSecret": { + "type": "string", + "description": "Client Secret for identity to login with Universal Auth", + "example": "..." + } + } + } + } + } + } + } + }, + "/api/v1/auth/universal-auth/identities/{identityId}": { + "post": { + "summary": "Attach Universal Auth configuration onto identity", + "description": "Attach Universal Auth configuration onto identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity to attach Universal Auth onto" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityUniversalAuth": { + "$ref": "#/components/schemas/IdentityUniversalAuth" + } + }, + "description": "Details of attached Universal Auth" + } + } + } + }, + "400": { + "description": "Bad Request" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretTrustedIps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "description": "IP address to trust", + "default": "0.0.0.0/0" + } + } + }, + "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "...", + "default": [ + { + "ipAddress": "0.0.0.0/0" + } + ] + }, + "accessTokenTTL": { + "type": "number", + "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", + "example": "...", + "default": 100 + }, + "accessTokenMaxTTL": { + "type": "number", + "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", + "example": "...", + "default": 2592000 + }, + "accessTokenNumUsesLimit": { + "type": "number", + "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", + "example": "...", + "default": 0 + }, + "accessTokenTrustedIps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "description": "IP address to trust", + "default": "0.0.0.0/0" + } + } + }, + "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "...", + "default": [ + { + "ipAddress": "0.0.0.0/0" + } + ] + } + } + } + } + } + } + }, + "patch": { + "summary": "Update Universal Auth configuration on identity", + "description": "Update Universal Auth configuration on identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity to update Universal Auth on" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityUniversalAuth": { + "$ref": "#/components/schemas/IdentityUniversalAuth" + } + }, + "description": "Details of updated Universal Auth" + } + } + } + }, + "400": { + "description": "Bad Request" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretTrustedIps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "description": "IP address to trust" + } + } + }, + "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "..." + }, + "accessTokenTTL": { + "type": "number", + "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", + "example": "..." + }, + "accessTokenMaxTTL": { + "type": "number", + "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", + "example": "..." + }, + "accessTokenNumUsesLimit": { + "type": "number", + "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", + "example": "..." + }, + "accessTokenTrustedIps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "description": "IP address to trust" + } + } + }, + "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "..." + } + } + } + } + } + } + }, + "get": { + "summary": "Retrieve Universal Auth configuration on identity", + "description": "Retrieve Universal Auth configuration on identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity to retrieve Universal Auth on" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityUniversalAuth": { + "$ref": "#/components/schemas/IdentityUniversalAuth" + } + }, + "description": "Details of retrieved Universal Auth" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/auth/universal-auth/identities/{identityId}/client-secrets": { + "post": { + "summary": "Create Universal Auth Client Secret for identity", + "description": "Create Universal Auth Client Secret for identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity to create Universal Auth Client Secret for" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecret": { + "type": "string", + "description": "The created Client Secret" + }, + "clientSecretData": { + "$ref": "#/components/schemas/IdentityUniversalAuthClientSecretData" + } + }, + "description": "Details of the created Client Secret" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "A description for the Client Secret to create.", + "example": "..." + }, + "ttl": { + "type": "number", + "description": "The time-to-live for the Client Secret to create. By default, the TTL will be set to 0 which implies that the Client Secret will never expire; a value of 0 implies an infinite lifetime.", + "example": "...", + "default": 0 + }, + "numUsesLimit": { + "type": "number", + "description": "The maximum number of times that the Client Secret can be used together with the Client ID to get back an access token; a value of 0 implies infinite number of uses.", + "example": "...", + "default": 0 + } + } + } + } + } + } + }, + "get": { + "summary": "List Universal Auth Client Secrets for identity", + "description": "List Universal Auth Client Secrets for identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity for which to get Client Secrets for" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretData": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IdentityUniversalAuthClientSecretData" + } + } + }, + "description": "Details of the Client Secrets" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v1/auth/universal-auth/identities/{identityId}/client-secrets/{clientSecretId}/revoke": { + "post": { + "summary": "Revoke Universal Auth Client Secret for identity", + "description": "Revoke Universal Auth Client Secret for identity", + "parameters": [ + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity under which Client Secret was issued for" + }, + { + "name": "clientSecretId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of Client Secret to revoke" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretData": { + "$ref": "#/components/schemas/IdentityUniversalAuthClientSecretData" + } + }, + "description": "Details of the revoked Client Secret" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, "/api/v1/admin/config": { "get": { "description": "", @@ -1807,6 +2439,9 @@ "responses": { "200": { "description": "OK" + }, + "400": { + "description": "Bad Request" } } } @@ -4130,8 +4765,8 @@ }, "/api/v2/organizations/{organizationId}/memberships": { "get": { - "summary": "Return organization memberships", - "description": "Return organization memberships", + "summary": "Return organization user memberships", + "description": "Return organization user memberships", "parameters": [ { "name": "organizationId", @@ -4166,15 +4801,16 @@ }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ] } }, "/api/v2/organizations/{organizationId}/memberships/{membershipId}": { "patch": { - "summary": "Update organization membership", - "description": "Update organization membership", + "summary": "Update organization user membership", + "description": "Update organization user membership", "parameters": [ { "name": "organizationId", @@ -4211,11 +4847,15 @@ } } } + }, + "400": { + "description": "Bad Request" } }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ], "requestBody": { @@ -4236,8 +4876,8 @@ } }, "delete": { - "summary": "Delete organization membership", - "description": "Delete organization membership", + "summary": "Delete organization user membership", + "description": "Delete organization user membership", "parameters": [ { "name": "organizationId", @@ -4278,7 +4918,8 @@ }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ] } @@ -4356,6 +4997,49 @@ } } }, + "/api/v2/organizations/{organizationId}/identity-memberships": { + "get": { + "summary": "Return organization identity memberships", + "description": "Return organization identity memberships", + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of organization" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMemberships": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IdentityMembershipOrg" + }, + "description": "Identity memberships of organization" + } + } + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, "/api/v2/workspace/{workspaceId}/memberships": { "post": { "description": "", @@ -4376,8 +5060,8 @@ } }, "get": { - "summary": "Return project memberships", - "description": "Return project memberships", + "summary": "Return project user memberships", + "description": "Return project user memberships", "parameters": [ { "name": "workspaceId", @@ -4412,7 +5096,8 @@ }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ] } @@ -4878,8 +5563,8 @@ }, "/api/v2/workspace/{workspaceId}/memberships/{membershipId}": { "patch": { - "summary": "Update project membership", - "description": "Update project membership", + "summary": "Update project user membership", + "description": "Update project user membership", "parameters": [ { "name": "workspaceId", @@ -4920,7 +5605,8 @@ }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ], "requestBody": { @@ -4932,7 +5618,7 @@ "properties": { "role": { "type": "string", - "description": "Role of membership - either admin or member" + "description": "Role to update to for project membership" } } } @@ -4941,8 +5627,8 @@ } }, "delete": { - "summary": "Delete project membership", - "description": "Delete project membership", + "summary": "Delete project user membership", + "description": "Delete project user membership", "parameters": [ { "name": "workspaceId", @@ -4983,7 +5669,8 @@ }, "security": [ { - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] } ] } @@ -5008,6 +5695,187 @@ } } }, + "/api/v2/workspace/{workspaceId}/identity-memberships/{identityId}": { + "post": { + "description": "", + "parameters": [ + { + "name": "workspaceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "summary": "Update project identity membership", + "description": "Update project identity membership", + "parameters": [ + { + "name": "workspaceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of project" + }, + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity whose membership to update in project" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMembership": { + "$ref": "#/components/schemas/IdentityMembership", + "description": "Updated identity membership" + } + } + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "Role to update to for identity project membership" + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete project identity membership", + "description": "Delete project identity membership", + "parameters": [ + { + "name": "workspaceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of project" + }, + { + "name": "identityId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of identity whose membership to delete in project" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMembership": { + "$ref": "#/components/schemas/IdentityMembership", + "description": "Deleted identity membership" + } + } + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/v2/workspace/{workspaceId}/identity-memberships": { + "get": { + "summary": "Return project identity memberships", + "description": "Return project identity memberships", + "parameters": [ + { + "name": "workspaceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "ID of project" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMemberships": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IdentityMembership" + }, + "description": "Identity memberships of project" + } + } + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, "/api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}": { "post": { "description": "", @@ -6171,26 +7039,6 @@ } } }, - "/api/v3/workspaces/{workspaceId}/service-token": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, "/api/v3/signup/complete-account/signup": { "post": { "description": "", @@ -6291,6 +7139,137 @@ } } }, + "Identity": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "" + }, + "name": { + "type": "string", + "example": "Machine 1" + }, + "authMethod": { + "type": "string", + "example": "universal-auth" + } + } + }, + "IdentityUniversalAuth": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "" + }, + "identity": { + "type": "string", + "example": "" + }, + "clientId": { + "type": "string", + "example": "..." + }, + "clientSecretTrustedIps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "example": "0.0.0.0" + }, + "type": { + "type": "string", + "example": "ipv4" + }, + "prefix": { + "type": "string", + "example": "0" + } + } + } + }, + "accessTokenTTL": { + "type": "number", + "example": 7200 + }, + "accessTokenMaxTTL": { + "type": "number", + "example": 2592000 + }, + "accessTokenNumUsesLimit": { + "type": "number", + "example": 0 + }, + "accessTokenTrustedIps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "example": "0.0.0.0" + }, + "type": { + "type": "string", + "example": "ipv4" + }, + "prefix": { + "type": "string", + "example": "0" + } + } + } + } + } + }, + "IdentityUniversalAuthClientSecretData": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "" + }, + "identityUniversalAuth": { + "type": "string", + "example": "" + }, + "isClientSecretRevoked": { + "type": "boolean", + "example": false + }, + "description": { + "type": "string", + "example": "" + }, + "clientSecretPrefix": { + "type": "string", + "example": "abc" + }, + "clientSecretNumUses": { + "type": "number", + "example": 0 + }, + "clientSecretNumUsesLimit": { + "type": "number", + "example": 0 + }, + "clientSecretTTL": { + "type": "number", + "example": 0 + }, + "createdAt": { + "type": "string", + "example": "2023-01-13T14:16:12.210Z" + }, + "updatedAt": { + "type": "string", + "example": "2023-01-13T14:16:12.210Z" + } + } + }, "Membership": { "type": "object", "properties": { @@ -6387,6 +7366,70 @@ } } }, + "IdentityMembership": { + "type": "object", + "properties": { + "identity": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "" + }, + "name": { + "type": "string", + "example": "Machine 1" + }, + "authMethod": { + "type": "string", + "example": "universal-auth" + } + } + }, + "workspace": { + "type": "string", + "example": "" + }, + "role": { + "type": "string", + "example": "member" + } + } + }, + "IdentityMembershipOrg": { + "type": "object", + "properties": { + "identity": { + "type": "object", + "properties": { + "_id": { + "type": "string", + "example": "" + }, + "name": { + "type": "string", + "example": "Machine 1" + }, + "authMethod": { + "type": "string", + "example": "universal-auth" + } + } + }, + "organization": { + "type": "string", + "example": "" + }, + "role": { + "type": "string", + "example": "member" + }, + "status": { + "type": "string", + "example": "accepted" + } + } + }, "Organization": { "type": "object", "properties": { @@ -6990,7 +8033,7 @@ "type": "http", "scheme": "bearer", "bearerFormat": "JWT", - "description": "A service token in Infisical" + "description": "An access token in Infisical" }, "apiKeyAuth": { "type": "apiKey", diff --git a/backend/src/controllers/v1/universalAuthController.ts b/backend/src/controllers/v1/universalAuthController.ts index 750c35639..65f59efbe 100644 --- a/backend/src/controllers/v1/universalAuthController.ts +++ b/backend/src/controllers/v1/universalAuthController.ts @@ -60,6 +60,52 @@ const packageUniversalAuthClientSecretData = (identityUniversalAuthClientSecret: * @param res */ export const renewAccessToken = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Renew access token' + #swagger.description = 'Renew access token' + + #swagger.requestBody = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { + "type": "string", + "description": "Access token to renew", + "example": "..." + } + } + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { + "type": "string", + "description": "(Same) Access token after successful renewal" + }, + "expiresIn": { + "type": "number", + "description": "TTL of access token in seconds" + }, + "tokenType": { + "type": "string", + "description": "Type of access token (e.g. Bearer)" + } + }, + "description": "Access token and its details" + } + } + } + } + */ const { body: { accessToken @@ -150,6 +196,57 @@ export const renewAccessToken = async (req: Request, res: Response) => { * @param res */ export const loginIdentityUniversalAuth = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Login with Universal Auth' + #swagger.description = 'Login with Universal Auth' + + #swagger.requestBody = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "description": "Client ID for identity to login with Universal Auth", + "example": "..." + }, + "clientSecret": { + "type": "string", + "description": "Client Secret for identity to login with Universal Auth", + "example": "..." + } + } + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { + "type": "string", + "description": "Access token issued after successful login" + }, + "expiresIn": { + "type": "number", + "description": "TTL of access token in seconds" + }, + "tokenType": { + "type": "string", + "description": "Type of access token (e.g. Bearer)" + } + }, + "description": "Access token and its details" + } + } + } + } + */ const { body: { clientId, @@ -303,7 +400,105 @@ export const loginIdentityUniversalAuth = async (req: Request, res: Response) => }); } -export const addIdentityUniversalAuth = async (req: Request, res: Response) => { +/** + * Attach identity universal auth method onto identity with id [identityId] + * @param req + * @param res + */ +export const attachIdentityUniversalAuth = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Attach Universal Auth configuration onto identity' + #swagger.description = 'Attach Universal Auth configuration onto identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity to attach Universal Auth onto", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.requestBody = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretTrustedIps": { + type: "array", + items: { + type: "object", + "properties": { + "ipAddress": { + type: "string", + description: "IP address to trust", + default: "0.0.0.0/0" + } + } + }, + "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "...", + "default": [{ ipAddress: "0.0.0.0/0" }] + }, + "accessTokenTTL": { + "type": "number", + "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", + "example": "...", + "default": 100 + }, + "accessTokenMaxTTL": { + "type": "number", + "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", + "example": "...", + "default": 2592000 + }, + "accessTokenNumUsesLimit": { + "type": "number", + "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", + "example": "...", + "default": 0 + }, + "accessTokenTrustedIps": { + type: "array", + items: { + type: "object", + "properties": { + "ipAddress": { + type: "string", + description: "IP address to trust", + default: "0.0.0.0/0" + } + } + }, + "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "...", + "default": [{ ipAddress: "0.0.0.0/0" }] + } + } + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityUniversalAuth": { + $ref: '#/definitions/IdentityUniversalAuth' + } + }, + "description": "Details of attached Universal Auth" + } + } + } + } + */ const { params: { identityId }, body: { @@ -414,7 +609,98 @@ export const addIdentityUniversalAuth = async (req: Request, res: Response) => { }); } +/** + * Update identity universal auth method on identity with id [identityId] + * @param req + * @param res + */ export const updateIdentityUniversalAuth = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Update Universal Auth configuration on identity' + #swagger.description = 'Update Universal Auth configuration on identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity to update Universal Auth on", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.requestBody = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretTrustedIps": { + type: "array", + items: { + type: "object", + "properties": { + "ipAddress": { + type: "string", + description: "IP address to trust" + } + } + }, + "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "...", + }, + "accessTokenTTL": { + "type": "number", + "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", + "example": "...", + }, + "accessTokenMaxTTL": { + "type": "number", + "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", + "example": "...", + }, + "accessTokenNumUsesLimit": { + "type": "number", + "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", + "example": "...", + }, + "accessTokenTrustedIps": { + type: "array", + items: { + type: "object", + "properties": { + "ipAddress": { + type: "string", + description: "IP address to trust" + } + } + }, + "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", + "example": "...", + } + } + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityUniversalAuth": { + $ref: '#/definitions/IdentityUniversalAuth' + } + }, + "description": "Details of updated Universal Auth" + } + } + } + } + */ const { params: { identityId }, body: { @@ -526,7 +812,43 @@ export const updateIdentityUniversalAuth = async (req: Request, res: Response) = }); } +/** + * Return identity universal auth method on identity with id [identityId] + * @param req + * @param res + */ export const getIdentityUniversalAuth = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Retrieve Universal Auth configuration on identity' + #swagger.description = 'Retrieve Universal Auth configuration on identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity to retrieve Universal Auth on", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityUniversalAuth": { + $ref: '#/definitions/IdentityUniversalAuth' + } + }, + "description": "Details of retrieved Universal Auth" + } + } + } + } + */ const { params: { identityId } } = await validateRequest(reqValidator.GetUniversalAuthForIdentityV1, req); @@ -577,7 +899,77 @@ export const getIdentityUniversalAuth = async (req: Request, res: Response) => { }); } + +/** + * Create client secret for identity universal auth method on identity with id [identityId] + * @param req + * @param res + */ export const createUniversalAuthClientSecret = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Create Universal Auth Client Secret for identity' + #swagger.description = 'Create Universal Auth Client Secret for identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity to create Universal Auth Client Secret for", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.requestBody = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "A description for the Client Secret to create.", + "example": "..." + }, + "ttl": { + "type": "number", + "description": "The time-to-live for the Client Secret to create. By default, the TTL will be set to 0 which implies that the Client Secret will never expire; a value of 0 implies an infinite lifetime.", + "example": "...", + "default": 0 + }, + "numUsesLimit": { + "type": "number", + "description": "The maximum number of times that the Client Secret can be used together with the Client ID to get back an access token; a value of 0 implies infinite number of uses.", + "example": "...", + "default": 0 + } + } + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecret": { + "type": "string", + "description": "The created Client Secret" + }, + "clientSecretData": { + $ref: '#/definitions/IdentityUniversalAuthClientSecretData' + } + }, + "description": "Details of the created Client Secret" + } + } + } + } + */ const { params: { identityId }, body: { @@ -660,7 +1052,46 @@ export const createUniversalAuthClientSecret = async (req: Request, res: Respons }); } -export const getUniversalAuthClientSecrets = async (req: Request, res: Response) => { +/** + * Return list of client secret details for identity universal auth method on identity with id [identityId] + * @param req + * @param res + */ +export const getUniversalAuthClientSecretsDetails = async (req: Request, res: Response) => { + /* + #swagger.summary = 'List Universal Auth Client Secrets for identity' + #swagger.description = 'List Universal Auth Client Secrets for identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity for which to get Client Secrets for", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretData": { + type: "array", + items: { + $ref: '#/definitions/IdentityUniversalAuthClientSecretData' + } + } + }, + "description": "Details of the Client Secrets" + } + } + } + } + */ const { params: { identityId } } = await validateRequest(reqValidator.GetUniversalAuthClientSecretsV1, req); @@ -720,7 +1151,50 @@ export const getUniversalAuthClientSecrets = async (req: Request, res: Response) }); } +/** + * Revoke client secret for identity universal auth method on identity with id [identityId] + * @param req + * @param res + */ export const revokeUniversalAuthClientSecret = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Revoke Universal Auth Client Secret for identity' + #swagger.description = 'Revoke Universal Auth Client Secret for identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity under which Client Secret was issued for", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.parameters['clientSecretId'] = { + "description": "ID of Client Secret to revoke", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "clientSecretData": { + $ref: '#/definitions/IdentityUniversalAuthClientSecretData' + } + }, + "description": "Details of the revoked Client Secret" + } + } + } + } + */ const { params: { identityId, clientSecretId } } = await validateRequest(reqValidator.RevokeUniversalAuthClientSecretV1, req); diff --git a/backend/src/controllers/v2/organizationsController.ts b/backend/src/controllers/v2/organizationsController.ts index f8ffd46c8..f939f9072 100644 --- a/backend/src/controllers/v2/organizationsController.ts +++ b/backend/src/controllers/v2/organizationsController.ts @@ -33,11 +33,12 @@ import { ForbiddenError } from "@casl/ability"; */ export const getOrganizationMemberships = async (req: Request, res: Response) => { /* - #swagger.summary = 'Return organization memberships' - #swagger.description = 'Return organization memberships' + #swagger.summary = 'Return organization user memberships' + #swagger.description = 'Return organization user memberships' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['organizationId'] = { @@ -94,11 +95,12 @@ export const getOrganizationMemberships = async (req: Request, res: Response) => */ export const updateOrganizationMembership = async (req: Request, res: Response) => { /* - #swagger.summary = 'Update organization membership' - #swagger.description = 'Update organization membership' + #swagger.summary = 'Update organization user membership' + #swagger.description = 'Update organization user membership' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['organizationId'] = { @@ -214,11 +216,12 @@ export const updateOrganizationMembership = async (req: Request, res: Response) */ export const deleteOrganizationMembership = async (req: Request, res: Response) => { /* - #swagger.summary = 'Delete organization membership' - #swagger.description = 'Delete organization membership' + #swagger.summary = 'Delete organization user membership' + #swagger.description = 'Delete organization user membership' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['organizationId'] = { @@ -425,6 +428,40 @@ export const deleteOrganizationById = async (req: Request, res: Response) => { * @returns */ export const getOrganizationIdentityMemberships = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Return organization identity memberships' + #swagger.description = 'Return organization identity memberships' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['organizationId'] = { + "description": "ID of organization", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMemberships": { + "type": "array", + "items": { + $ref: "#/components/schemas/IdentityMembershipOrg" + }, + "description": "Identity memberships of organization" + } + } + } + } + } + } + */ const { params: { organizationId } } = await validateRequest(reqValidator.GetOrgIdentityMembershipsV2, req); diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts index 8899b69c3..dca217a1b 100644 --- a/backend/src/controllers/v2/workspaceController.ts +++ b/backend/src/controllers/v2/workspaceController.ts @@ -249,11 +249,12 @@ export const getWorkspaceServiceTokenData = async (req: Request, res: Response) */ export const getWorkspaceMemberships = async (req: Request, res: Response) => { /* - #swagger.summary = 'Return project memberships' - #swagger.description = 'Return project memberships' + #swagger.summary = 'Return project user memberships' + #swagger.description = 'Return project user memberships' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['workspaceId'] = { @@ -312,11 +313,12 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => { */ export const updateWorkspaceMembership = async (req: Request, res: Response) => { /* - #swagger.summary = 'Update project membership' - #swagger.description = 'Update project membership' + #swagger.summary = 'Update project user membership' + #swagger.description = 'Update project user membership' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['workspaceId'] = { @@ -340,7 +342,7 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) => "properties": { "role": { "type": "string", - "description": "Role of membership - either admin or member", + "description": "Role to update to for project membership", } } } @@ -402,11 +404,12 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) => */ export const deleteWorkspaceMembership = async (req: Request, res: Response) => { /* - #swagger.summary = 'Delete project membership' - #swagger.description = 'Delete project membership' + #swagger.summary = 'Delete project user membership' + #swagger.description = 'Delete project user membership' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['workspaceId'] = { @@ -594,7 +597,60 @@ export const addIdentityToWorkspace = async (req: Request, res: Response) => { * @param req * @param res */ -export const updateIdentityWorkspaceRole = async (req: Request, res: Response) => { + export const updateIdentityWorkspaceRole = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Update project identity membership' + #swagger.description = 'Update project identity membership' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['workspaceId'] = { + "description": "ID of project", + "required": true, + "type": "string" + } + + #swagger.parameters['identityId'] = { + "description": "ID of identity whose membership to update in project", + "required": true, + "type": "string" + } + + #swagger.requestBody = { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "Role to update to for identity project membership", + } + } + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMembership": { + $ref: "#/components/schemas/IdentityMembership", + "description": "Updated identity membership" + } + } + } + } + } + } + */ const { params: { workspaceId, identityId }, body: { @@ -676,12 +732,48 @@ export const updateIdentityWorkspaceRole = async (req: Request, res: Response) = } /** - * Delete identity with id [identityId] to workspace + * Delete identity with id [identityId] from workspace * with id [workspaceId] * @param req * @param res */ -export const deleteIdentityFromWorkspace = async (req: Request, res: Response) => { + export const deleteIdentityFromWorkspace = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Delete project identity membership' + #swagger.description = 'Delete project identity membership' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['workspaceId'] = { + "description": "ID of project", + "required": true, + "type": "string" + } + + #swagger.parameters['identityId'] = { + "description": "ID of identity whose membership to delete in project", + "required": true, + "type": "string" + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMembership": { + $ref: "#/components/schemas/IdentityMembership", + "description": "Deleted identity membership" + } + } + } + } + } + } + */ const { params: { workspaceId, identityId } } = await validateRequest(reqValidator.DeleteIdentityFromWorkspaceV2, req); @@ -732,7 +824,41 @@ export const deleteIdentityFromWorkspace = async (req: Request, res: Response) = * @param res * @returns */ -export const getWorkspaceIdentityMemberships = async (req: Request, res: Response) => { + export const getWorkspaceIdentityMemberships = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Return project identity memberships' + #swagger.description = 'Return project identity memberships' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['workspaceId'] = { + "description": "ID of project", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identityMemberships": { + "type": "array", + "items": { + $ref: "#/components/schemas/IdentityMembership" + }, + "description": "Identity memberships of project" + } + } + } + } + } + } + */ const { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceIdentityMembersV2, req); diff --git a/backend/src/ee/controllers/v1/identitiesController.ts b/backend/src/ee/controllers/v1/identitiesController.ts index c4e88f7c2..179beeda3 100644 --- a/backend/src/ee/controllers/v1/identitiesController.ts +++ b/backend/src/ee/controllers/v1/identitiesController.ts @@ -42,6 +42,58 @@ import { ForbiddenError } from "@casl/ability"; * @returns */ export const createIdentity = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Create identity' + #swagger.description = 'Create identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.requestBody = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of entity to create", + "example": "development" + }, + "organizationId": { + "type": "string", + "description": "ID of organization where to create identity", + "example": "dev-environment" + }, + "role": { + "type": "string", + "description": "Role to assume for organization membership", + "example": "no-access" + } + }, + "required": ["name", "organizationId", "role"] + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identity": { + $ref: '#/definitions/Identity' + } + }, + "description": "Details of the created identity" + } + } + } + } + */ const { body: { name, @@ -120,6 +172,59 @@ export const createIdentity = async (req: Request, res: Response) => { * @returns */ export const updateIdentity = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Update identity' + #swagger.description = 'Update identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity to update", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.requestBody = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of entity to update to", + "example": "development" + }, + "role": { + "type": "string", + "description": "Role to update to for organization membership", + "example": "no-access" + } + } + } + } + } + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identity": { + $ref: '#/definitions/Identity' + } + }, + "description": "Details of the updated identity" + } + } + } + } + */ const { params: { identityId }, body: { @@ -242,6 +347,37 @@ export const createIdentity = async (req: Request, res: Response) => { * @returns */ export const deleteIdentity = async (req: Request, res: Response) => { + /* + #swagger.summary = 'Delete identity' + #swagger.description = 'Delete identity' + + #swagger.security = [{ + "bearerAuth": [] + }] + + #swagger.parameters['identityId'] = { + "description": "ID of identity", + "required": true, + "type": "string", + "in": "path" + } + + #swagger.responses[200] = { + content: { + "application/json": { + "schema": { + "type": "object", + "properties": { + "identity": { + $ref: '#/definitions/Identity' + } + }, + "description": "Details of the deleted identity" + } + } + } + } + */ const { params: { identityId } } = await validateRequest(reqValidator.DeleteIdentityV1, req); diff --git a/backend/src/ee/controllers/v1/workspaceController.ts b/backend/src/ee/controllers/v1/workspaceController.ts index 9f63de3d9..70d612792 100644 --- a/backend/src/ee/controllers/v1/workspaceController.ts +++ b/backend/src/ee/controllers/v1/workspaceController.ts @@ -62,15 +62,30 @@ export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) = #swagger.description = 'Return project secret snapshots ids' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['workspaceId'] = { - "description": "ID of project", + "description": "ID of project where to get secret snapshots for", "required": true, "type": "string" } + #swagger.parameters['environment'] = { + "description": "Slug of environment where to get secret snapshots for", + "required": true, + "type": "string", + "in": "query" + } + + #swagger.parameters['directory'] = { + "description": "Path where to get secret snapshots for like / or /foo/bar. Default is /", + "required": false, + "type": "string", + "in": "query" + } + #swagger.parameters['offset'] = { "description": "Number of secret snapshots to skip", "required": false, @@ -195,11 +210,12 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons #swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.' #swagger.security = [{ - "apiKeyAuth": [] + "apiKeyAuth": [], + "bearerAuth": [] }] #swagger.parameters['workspaceId'] = { - "description": "ID of project", + "description": "ID of project where to roll back", "required": true, "type": "string" } @@ -211,6 +227,14 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons "schema": { "type": "object", "properties": { + "environment": { + "type": "string", + "description": "Slug of environment where to roll back" + }, + "directory": { + "type": "string", + "description": "Path where to roll back for like / or /foo/bar. Default is /" + }, "version": { "type": "integer", "description": "Version of secret snapshot to roll back to", diff --git a/backend/src/ee/routes/v1/workspace.ts b/backend/src/ee/routes/v1/workspace.ts index f9be77e78..ace4458cc 100644 --- a/backend/src/ee/routes/v1/workspace.ts +++ b/backend/src/ee/routes/v1/workspace.ts @@ -7,7 +7,7 @@ import { workspaceController } from "../../controllers/v1"; router.get( "/:workspaceId/secret-snapshots", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.getWorkspaceSecretSnapshots ); @@ -23,7 +23,7 @@ router.get( router.post( "/:workspaceId/secret-snapshots/rollback", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.rollbackWorkspaceSecretSnapshot ); @@ -31,7 +31,7 @@ router.post( router.get( "/:workspaceId/audit-logs", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.getWorkspaceAuditLogs ); diff --git a/backend/src/routes/v1/secretImps.ts b/backend/src/routes/v1/secretImps.ts index 5dba7fc9d..478714e54 100644 --- a/backend/src/routes/v1/secretImps.ts +++ b/backend/src/routes/v1/secretImps.ts @@ -7,7 +7,7 @@ import { AuthMode } from "../../variables"; router.post( "/", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), secretImpsController.createSecretImp ); @@ -15,7 +15,7 @@ router.post( router.put( "/:id", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), secretImpsController.updateSecretImport ); @@ -23,7 +23,7 @@ router.put( router.delete( "/:id", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), secretImpsController.deleteSecretImport ); @@ -31,7 +31,7 @@ router.delete( router.get( "/", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), secretImpsController.getSecretImports ); diff --git a/backend/src/routes/v1/secretsFolder.ts b/backend/src/routes/v1/secretsFolder.ts index ed296373c..e7bfc8987 100644 --- a/backend/src/routes/v1/secretsFolder.ts +++ b/backend/src/routes/v1/secretsFolder.ts @@ -12,7 +12,7 @@ import { AuthMode } from "../../variables"; router.post( "/", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), createFolder ); @@ -20,7 +20,7 @@ router.post( router.patch( "/:folderName", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), updateFolderById ); @@ -28,7 +28,7 @@ router.patch( router.delete( "/:folderName", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), deleteFolder ); @@ -36,7 +36,7 @@ router.delete( router.get( "/", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), getFolders ); diff --git a/backend/src/routes/v1/universalAuth.ts b/backend/src/routes/v1/universalAuth.ts index d232f0ee8..b9d180040 100644 --- a/backend/src/routes/v1/universalAuth.ts +++ b/backend/src/routes/v1/universalAuth.ts @@ -18,15 +18,15 @@ router.post( router.post( "/universal-auth/identities/:identityId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), - universalAuthController.addIdentityUniversalAuth + universalAuthController.attachIdentityUniversalAuth ); router.patch( "/universal-auth/identities/:identityId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), universalAuthController.updateIdentityUniversalAuth ); @@ -34,7 +34,7 @@ router.patch( router.get( "/universal-auth/identities/:identityId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), universalAuthController.getIdentityUniversalAuth ); @@ -42,7 +42,7 @@ router.get( router.post( "/universal-auth/identities/:identityId/client-secrets", requireAuth({ - acceptedAuthModes: [AuthMode.JWT] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), universalAuthController.createUniversalAuthClientSecret ); @@ -50,15 +50,15 @@ router.post( router.get( "/universal-auth/identities/:identityId/client-secrets", requireAuth({ - acceptedAuthModes: [AuthMode.JWT] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), - universalAuthController.getUniversalAuthClientSecrets + universalAuthController.getUniversalAuthClientSecretsDetails ); router.post( "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke", requireAuth({ - acceptedAuthModes: [AuthMode.JWT] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), universalAuthController.revokeUniversalAuthClientSecret ); diff --git a/backend/src/routes/v2/environment.ts b/backend/src/routes/v2/environment.ts index 49e1786b3..e5143e6fe 100644 --- a/backend/src/routes/v2/environment.ts +++ b/backend/src/routes/v2/environment.ts @@ -7,7 +7,7 @@ import { AuthMode } from "../../variables"; router.post( "/:workspaceId/environments", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), environmentController.createWorkspaceEnvironment ); @@ -15,7 +15,7 @@ router.post( router.put( "/:workspaceId/environments", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), environmentController.renameWorkspaceEnvironment ); @@ -23,7 +23,7 @@ router.put( router.patch( "/:workspaceId/environments", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), environmentController.reorderWorkspaceEnvironments ); @@ -31,7 +31,7 @@ router.patch( router.delete( "/:workspaceId/environments", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), environmentController.deleteWorkspaceEnvironment ); diff --git a/backend/src/routes/v2/organizations.ts b/backend/src/routes/v2/organizations.ts index cfe1c4d6e..c66223750 100644 --- a/backend/src/routes/v2/organizations.ts +++ b/backend/src/routes/v2/organizations.ts @@ -9,7 +9,7 @@ import { organizationsController } from "../../controllers/v2"; router.get( "/:organizationId/memberships", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), organizationsController.getOrganizationMemberships ); @@ -17,7 +17,7 @@ router.get( router.patch( "/:organizationId/memberships/:membershipId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), organizationsController.updateOrganizationMembership ); @@ -25,7 +25,7 @@ router.patch( router.delete( "/:organizationId/memberships/:membershipId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), organizationsController.deleteOrganizationMembership ); @@ -33,7 +33,7 @@ router.delete( router.get( "/:organizationId/workspaces", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), organizationsController.getOrganizationWorkspaces ); diff --git a/backend/src/routes/v2/workspace.ts b/backend/src/routes/v2/workspace.ts index 59b7647e1..f45c38f0c 100644 --- a/backend/src/routes/v2/workspace.ts +++ b/backend/src/routes/v2/workspace.ts @@ -62,7 +62,7 @@ router.get( // new - TODO: rewire dashboard to this route "/:workspaceId/memberships", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.getWorkspaceMemberships ); @@ -71,7 +71,7 @@ router.patch( // TODO - rewire dashboard to this route "/:workspaceId/memberships/:membershipId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.updateWorkspaceMembership ); @@ -80,7 +80,7 @@ router.delete( // TODO - rewire dashboard to this route "/:workspaceId/memberships/:membershipId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.deleteWorkspaceMembership ); @@ -96,7 +96,7 @@ router.patch( router.post( "/:workspaceId/identity-memberships/:identityId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.addIdentityToWorkspace ); @@ -104,7 +104,7 @@ router.post( router.patch( "/:workspaceId/identity-memberships/:identityId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.updateIdentityWorkspaceRole ); @@ -112,7 +112,7 @@ router.patch( router.delete( "/:workspaceId/identity-memberships/:identityId", requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.deleteIdentityFromWorkspace ); @@ -120,7 +120,7 @@ router.delete( router.get( "/:workspaceId/identity-memberships", requireAuth({ - acceptedAuthModes: [AuthMode.JWT] + acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] }), workspaceController.getWorkspaceIdentityMemberships ); diff --git a/backend/src/validation/auth.ts b/backend/src/validation/auth.ts index 12121c61f..08ec3ac95 100644 --- a/backend/src/validation/auth.ts +++ b/backend/src/validation/auth.ts @@ -149,7 +149,7 @@ export const UpdateUniversalAuthToIdentityV1 = z.object({ accessTokenNumUsesLimit: z.number().int().min(0).optional(), accessTokenMaxTTL: z.number().int().refine(value => value !== 0, { message: "accessTokenMaxTTL must have a non zero number", - }).default(2592000), + }).optional(), }), }); diff --git a/backend/swagger/index.ts b/backend/swagger/index.ts index 3c7a503d6..6f3a426fb 100644 --- a/backend/swagger/index.ts +++ b/backend/swagger/index.ts @@ -30,7 +30,7 @@ const generateOpenAPISpec = async () => { type: "http", scheme: "bearer", bearerFormat: "JWT", - description: "A service token in Infisical" + description: "An access token in Infisical" }, apiKeyAuth: { type: "apiKey", @@ -52,6 +52,41 @@ const generateOpenAPISpec = async () => { updatedAt: "2023-01-13T14:16:12.210Z", createdAt: "2023-01-13T14:16:12.210Z" }, + Identity: { + _id: "", + name: "Machine 1", + authMethod: "universal-auth" + }, + IdentityUniversalAuth: { + _id: "", + identity: "", + clientId: "...", + clientSecretTrustedIps: [{ + ipAddress: "0.0.0.0", + type: "ipv4", + prefix: "0" + }], + accessTokenTTL: 7200, + accessTokenMaxTTL: 2592000, + accessTokenNumUsesLimit: 0, + accessTokenTrustedIps: [{ + ipAddress: "0.0.0.0", + type: "ipv4", + prefix: "0" + }] + }, + IdentityUniversalAuthClientSecretData: { + _id: "", + identityUniversalAuth: "", + isClientSecretRevoked: false, + description: "", + clientSecretPrefix: "abc", + clientSecretNumUses: 0, + clientSecretNumUsesLimit: 0, + clientSecretTTL: 0, + createdAt: "2023-01-13T14:16:12.210Z", + updatedAt: "2023-01-13T14:16:12.210Z" + }, Membership: { user: { _id: "", @@ -79,6 +114,25 @@ const generateOpenAPISpec = async () => { role: "owner", status: "accepted" }, + IdentityMembership: { + identity: { + _id: "", + name: "Machine 1", + authMethod: "universal-auth" + }, + workspace: "", + role: "member" + }, + IdentityMembershipOrg: { + identity: { + _id: "", + name: "Machine 1", + authMethod: "universal-auth" + }, + organization: "", + role: "member", + status: "accepted" + }, Organization: { _id: "", name: "Acme Corp.", diff --git a/docs/api-reference/endpoints/environments/create.mdx b/docs/api-reference/endpoints/environments/create.mdx index 826dcce3d..2527c613d 100644 --- a/docs/api-reference/endpoints/environments/create.mdx +++ b/docs/api-reference/endpoints/environments/create.mdx @@ -1,4 +1,4 @@ --- title: "Create" -openapi: "POST /api/v1/workspace/{workspaceId}/environments" +openapi: "POST /api/v2/workspace/{workspaceId}/environments" --- diff --git a/docs/api-reference/endpoints/identities/create.mdx b/docs/api-reference/endpoints/identities/create.mdx new file mode 100644 index 000000000..05a11521f --- /dev/null +++ b/docs/api-reference/endpoints/identities/create.mdx @@ -0,0 +1,4 @@ +--- +title: "Create" +openapi: "POST /api/v1/identities/" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/identities/delete.mdx b/docs/api-reference/endpoints/identities/delete.mdx new file mode 100644 index 000000000..07e79dfe9 --- /dev/null +++ b/docs/api-reference/endpoints/identities/delete.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete" +openapi: "DELETE /api/v1/identities/{identityId}" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/identities/update.mdx b/docs/api-reference/endpoints/identities/update.mdx new file mode 100644 index 000000000..c0940467b --- /dev/null +++ b/docs/api-reference/endpoints/identities/update.mdx @@ -0,0 +1,4 @@ +--- +title: "Update" +openapi: "PATCH /api/v1/identities/{identityId}" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/delete-membership.mdx b/docs/api-reference/endpoints/organizations/delete-membership.mdx index d9862e854..ee9ab77d5 100644 --- a/docs/api-reference/endpoints/organizations/delete-membership.mdx +++ b/docs/api-reference/endpoints/organizations/delete-membership.mdx @@ -1,4 +1,4 @@ --- -title: "Delete Membership" +title: "Delete User Membership" openapi: "DELETE /api/v2/organizations/{organizationId}/memberships/{membershipId}" --- diff --git a/docs/api-reference/endpoints/organizations/list-identity-memberships.mdx b/docs/api-reference/endpoints/organizations/list-identity-memberships.mdx new file mode 100644 index 000000000..1929a4b59 --- /dev/null +++ b/docs/api-reference/endpoints/organizations/list-identity-memberships.mdx @@ -0,0 +1,4 @@ +--- +title: "List Identity Memberships" +openapi: "GET /api/v2/organizations/{organizationId}/identity-memberships" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/organizations/memberships.mdx b/docs/api-reference/endpoints/organizations/memberships.mdx index b4b6f3d33..12f60ff7b 100644 --- a/docs/api-reference/endpoints/organizations/memberships.mdx +++ b/docs/api-reference/endpoints/organizations/memberships.mdx @@ -1,4 +1,4 @@ --- -title: "Get Memberships" +title: "Get User Memberships" openapi: "GET /api/v2/organizations/{organizationId}/memberships" --- diff --git a/docs/api-reference/endpoints/organizations/update-membership.mdx b/docs/api-reference/endpoints/organizations/update-membership.mdx index 1654d59c2..bf6af2de8 100644 --- a/docs/api-reference/endpoints/organizations/update-membership.mdx +++ b/docs/api-reference/endpoints/organizations/update-membership.mdx @@ -1,4 +1,4 @@ --- -title: "Update Membership" +title: "Update User Membership" openapi: "PATCH /api/v2/organizations/{organizationId}/memberships/{membershipId}" --- diff --git a/docs/api-reference/endpoints/organizations/workspaces.mdx b/docs/api-reference/endpoints/organizations/workspaces.mdx index 0950a84e0..34671fe73 100644 --- a/docs/api-reference/endpoints/organizations/workspaces.mdx +++ b/docs/api-reference/endpoints/organizations/workspaces.mdx @@ -2,3 +2,9 @@ title: "Get Projects" openapi: "GET /api/v2/organizations/{organizationId}/workspaces" --- + + + This endpoint will be deprecated in the near future in Q1/Q2 2024. + + We recommend switching to using [identities](/documentation/platform/identities/overview). + \ No newline at end of file diff --git a/docs/api-reference/endpoints/service-tokens/get.mdx b/docs/api-reference/endpoints/service-tokens/get.mdx index ad3afbdda..5b2604282 100644 --- a/docs/api-reference/endpoints/service-tokens/get.mdx +++ b/docs/api-reference/endpoints/service-tokens/get.mdx @@ -2,3 +2,9 @@ title: "Get" openapi: "GET /api/v2/service-token/" --- + + + This endpoint will be deprecated in the near future with the removal of service tokens in Q1/Q2 2024. + + We recommend switching to using [identities](/documentation/platform/identities/overview) if your client supports it. + diff --git a/docs/api-reference/endpoints/universal-auth/attach.mdx b/docs/api-reference/endpoints/universal-auth/attach.mdx new file mode 100644 index 000000000..cf2fb6264 --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/attach.mdx @@ -0,0 +1,4 @@ +--- +title: "Attach" +openapi: "POST /api/v1/auth/universal-auth/identities/{identityId}" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/universal-auth/create-client-secret.mdx b/docs/api-reference/endpoints/universal-auth/create-client-secret.mdx new file mode 100644 index 000000000..b93760697 --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/create-client-secret.mdx @@ -0,0 +1,4 @@ +--- +title: "Create Client Secret" +openapi: "POST /api/v1/auth/universal-auth/identities/{identityId}/client-secrets" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/universal-auth/list-client-secrets.mdx b/docs/api-reference/endpoints/universal-auth/list-client-secrets.mdx new file mode 100644 index 000000000..165604d06 --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/list-client-secrets.mdx @@ -0,0 +1,4 @@ +--- +title: "List Client Secrets" +openapi: "GET /api/v1/auth/universal-auth/identities/{identityId}/client-secrets" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/universal-auth/login.mdx b/docs/api-reference/endpoints/universal-auth/login.mdx new file mode 100644 index 000000000..a1617a53c --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/login.mdx @@ -0,0 +1,4 @@ +--- +title: "Login" +openapi: "POST /api/v1/auth/universal-auth/login" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/universal-auth/renew-access-token.mdx b/docs/api-reference/endpoints/universal-auth/renew-access-token.mdx new file mode 100644 index 000000000..3079fc08c --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/renew-access-token.mdx @@ -0,0 +1,4 @@ +--- +title: "Renew Access Token" +openapi: "POST /api/v1/auth/token/renew" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/universal-auth/retrieve.mdx b/docs/api-reference/endpoints/universal-auth/retrieve.mdx new file mode 100644 index 000000000..a26f59706 --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/retrieve.mdx @@ -0,0 +1,4 @@ +--- +title: "Retrieve" +openapi: "GET /api/v1/auth/universal-auth/identities/{identityId}" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/universal-auth/revoke-client-secret.mdx b/docs/api-reference/endpoints/universal-auth/revoke-client-secret.mdx new file mode 100644 index 000000000..5344d796f --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/revoke-client-secret.mdx @@ -0,0 +1,4 @@ +--- +title: "Revoke Client Secret" +openapi: "POST /api/v1/auth/universal-auth/identities/{identityId}/client-secrets/{clientSecretId}/revoke" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/universal-auth/update.mdx b/docs/api-reference/endpoints/universal-auth/update.mdx new file mode 100644 index 000000000..2d33de15a --- /dev/null +++ b/docs/api-reference/endpoints/universal-auth/update.mdx @@ -0,0 +1,4 @@ +--- +title: "Update" +openapi: "PATCH /api/v1/auth/universal-auth/identities/{identityId}" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/users/me.mdx b/docs/api-reference/endpoints/users/me.mdx index 9dab56dfd..4566a032d 100644 --- a/docs/api-reference/endpoints/users/me.mdx +++ b/docs/api-reference/endpoints/users/me.mdx @@ -2,3 +2,9 @@ title: "Get My User" openapi: "GET /api/v2/users/me" --- + + + This endpoint will be deprecated in the near future in Q1/Q2 2024. + + We recommend switching to using [identities](/documentation/platform/identities/overview). + \ No newline at end of file diff --git a/docs/api-reference/endpoints/users/my-organizations.mdx b/docs/api-reference/endpoints/users/my-organizations.mdx index d1ced8124..997fd5c08 100644 --- a/docs/api-reference/endpoints/users/my-organizations.mdx +++ b/docs/api-reference/endpoints/users/my-organizations.mdx @@ -2,3 +2,9 @@ title: "Get My Organizations" openapi: "GET /api/v2/users/me/organizations" --- + + + This endpoint will be deprecated in the near future in Q1/Q2 2024. + + We recommend switching to using [identities](/documentation/platform/identities/overview). + \ No newline at end of file diff --git a/docs/api-reference/endpoints/workspaces/delete-identity-membership.mdx b/docs/api-reference/endpoints/workspaces/delete-identity-membership.mdx new file mode 100644 index 000000000..4621f9b50 --- /dev/null +++ b/docs/api-reference/endpoints/workspaces/delete-identity-membership.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete Identity Membership" +openapi: "DELETE /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/workspaces/delete-membership.mdx b/docs/api-reference/endpoints/workspaces/delete-membership.mdx index 826eafa83..e93b2415b 100644 --- a/docs/api-reference/endpoints/workspaces/delete-membership.mdx +++ b/docs/api-reference/endpoints/workspaces/delete-membership.mdx @@ -1,4 +1,4 @@ --- -title: "Delete Membership" +title: "Delete User Membership" openapi: "DELETE /api/v2/workspace/{workspaceId}/memberships/{membershipId}" --- diff --git a/docs/api-reference/endpoints/workspaces/list-identity-memberships.mdx b/docs/api-reference/endpoints/workspaces/list-identity-memberships.mdx new file mode 100644 index 000000000..45297efff --- /dev/null +++ b/docs/api-reference/endpoints/workspaces/list-identity-memberships.mdx @@ -0,0 +1,4 @@ +--- +title: "List Identity Memberships" +openapi: "GET /api/v2/workspace/{workspaceId}/identity-memberships" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/workspaces/memberships.mdx b/docs/api-reference/endpoints/workspaces/memberships.mdx index 38b86d616..386c8a089 100644 --- a/docs/api-reference/endpoints/workspaces/memberships.mdx +++ b/docs/api-reference/endpoints/workspaces/memberships.mdx @@ -1,4 +1,4 @@ --- -title: "Get Memberships" +title: "Get User Memberships" openapi: "GET /api/v2/workspace/{workspaceId}/memberships" --- diff --git a/docs/api-reference/endpoints/workspaces/update-identity-membership.mdx b/docs/api-reference/endpoints/workspaces/update-identity-membership.mdx new file mode 100644 index 000000000..398c7bc81 --- /dev/null +++ b/docs/api-reference/endpoints/workspaces/update-identity-membership.mdx @@ -0,0 +1,4 @@ +--- +title: "Update Identity Membership" +openapi: "PATCH /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}" +--- \ No newline at end of file diff --git a/docs/api-reference/endpoints/workspaces/update-membership.mdx b/docs/api-reference/endpoints/workspaces/update-membership.mdx index 367793847..f0ef15412 100644 --- a/docs/api-reference/endpoints/workspaces/update-membership.mdx +++ b/docs/api-reference/endpoints/workspaces/update-membership.mdx @@ -1,4 +1,4 @@ --- -title: "Update Membership" +title: "Update User Membership" openapi: "PATCH /api/v2/workspace/{workspaceId}/memberships/{membershipId}" --- diff --git a/docs/api-reference/endpoints/workspaces/workspace-key.mdx b/docs/api-reference/endpoints/workspaces/workspace-key.mdx index 05313eb8f..1138a55f2 100644 --- a/docs/api-reference/endpoints/workspaces/workspace-key.mdx +++ b/docs/api-reference/endpoints/workspaces/workspace-key.mdx @@ -2,3 +2,8 @@ title: "Get Key" openapi: "GET /api/v2/workspace/{workspaceId}/encrypted-key" --- + + This endpoint will be deprecated in the near future in Q1/Q2 2024. + + We recommend using Infisical in non-E2EE mode going forward. + \ No newline at end of file diff --git a/docs/api-reference/overview/authentication.mdx b/docs/api-reference/overview/authentication.mdx index 5fa4fbc30..dcf9719ea 100644 --- a/docs/api-reference/overview/authentication.mdx +++ b/docs/api-reference/overview/authentication.mdx @@ -3,29 +3,34 @@ title: "Authentication" description: "How to authenticate with the Infisical Public API" --- -The Public API accepts multiple modes of authentication being via [Infisical Token](/documentation/platform/token) or API Key. +You can authenticate with the Infisical API using [Identities](/documentation/platform/identities/overview) paired with authentication modes such as [Universal Auth](/documentation/platform/identities/universal-auth). -- [Infisical Token](/documentation/platform/token): Provides short-lived, scoped CRUD access to the secrets of a specific project and environment. -- API Key: Provides full access to all endpoints representing the user without ability to encrypt/decrypt secrets for **E2EE** endpoints. +To interact with the Infisical API, you will need to obtain an access token. Follow the step by [step guide](/documentation/platform/identities/universal-auth) to get an access token via Universal Auth. - - - The Infisical Token mode uses an Infisical Token to authenticate with the API. - To authenticate requests with Infisical using the Infisical Token, you must include your Infisical Token in the `Authorization` header of HTTP requests made to the platform with the value `Bearer `. +**FAQ** - You can obtain an Infisical Token in Project Settings > Service Tokens. + + + The Service Token and API Key authentication modes are being deprecated out in favor of [Identities](/documentation/platform/identity). + We expect to make a deprecation notice in the coming months alongside a larger deprecation initiative planned for Q1/Q2 2024. - ![token add](../../images/project-token-add.png) - - - The API key mode uses an API key to authenticate with the API. - - To authenticate requests with Infisical using the API Key, you must include an API key in the `X-API-KEY` header of HTTP requests made to the platform. - - You can obtain an API key in User Settings > API Keys - - ![API key dashboard](../../images/api-key-dashboard.png) - ![API key in personal settings](../../images/api-key-settings.png) - - \ No newline at end of file + With identities, we're improving significantly over the shortcomings of Service Tokens and API Keys. Amongst many differences, identities provide broader access over the Infisical API, utilizes the same role-based + permission system used by users, and comes with ample more configurable security measures. + + + There are a few reasons for why this might happen: + + - You have insufficient organization permissions to create, read, update, delete identities. + - The identity you are trying to read, update, or delete is more privileged than yourself. + - The role you are trying to create an identity for or update an identity to is more privileged than yours. + + + There are a few reasons for why this might happen: + + - The client secret or access token has expired. + - The identity is insufficently permissioned to interact with the resources you wish to access. + - You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE. + - The client secret/access token is being used from an untrusted IP. + + \ No newline at end of file diff --git a/docs/api-reference/overview/blind-indices.mdx b/docs/api-reference/overview/blind-indices.mdx deleted file mode 100644 index 755ba682f..000000000 --- a/docs/api-reference/overview/blind-indices.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "Blind Indices" ---- - -In April 2023, we added the capability for users to query for secrets by name to improve the user experience of Infisical. Previously, it was only possible to query by id of the secret or fetch all secrets belonging to a project and environment. - -Blind indexing must be enabled for projects created prior to April 2023 to take effect. If your project can be blind indexed, then you'll see a section in your project settings appear as shown below: - -![project enable blind indices](../../images/project-settings-blind-indices.png) - - -It works using virtually irreversible blind indices generated by applying `argon2id` to the name of each secret and a random 128-bit salt assigned to each project on the server. We continue to keep the values of secrets E2EE by default. - -You can read more about it [here](/security/mechanics). - - - As previously mentioned, all projects made after April 2023 are automatically blind indexed. If you created a project before this date, you have to enable it manually in your project settings. - \ No newline at end of file diff --git a/docs/api-reference/overview/examples/e2ee-enabled.mdx b/docs/api-reference/overview/examples/e2ee-enabled.mdx index 0cbeaabcf..1de9c2290 100644 --- a/docs/api-reference/overview/examples/e2ee-enabled.mdx +++ b/docs/api-reference/overview/examples/e2ee-enabled.mdx @@ -2,6 +2,10 @@ title: "E2EE Enabled" --- + + E2EE enabled mode only works with [Service Tokens](/documentation/platform/token) and cannot be used with [Identities](/documentation/platform/identities/overview). + + Using Infisical's API to read/write secrets with E2EE enabled allows you to create, update, and retrieve secrets but requires you to perform client-side encryption/decryption operations. For this reason, we recommend using one of the available SDKs instead. diff --git a/docs/api-reference/overview/introduction.mdx b/docs/api-reference/overview/introduction.mdx index 546e0b9a2..06ee491b5 100644 --- a/docs/api-reference/overview/introduction.mdx +++ b/docs/api-reference/overview/introduction.mdx @@ -6,8 +6,4 @@ Infisical's Public (REST) API provides users an alternative way to programmatica secrets via HTTPS requests. This can be useful for automating tasks, such as rotating credentials, or for integrating secret management into a larger system. -With the Public API, users can create, read, update, and delete secrets, as well as manage access control, query audit logs, and more. - - - In April 2023, we added the capability for users to query for secrets by name to improve the user experience of Infisical. If your project was created prior to April 2023, please read and follow the section on [blind indices](./blind-indices) and how to enable them for better usage of Infisical. - \ No newline at end of file +With the Public API, you can create, read, update, and delete secrets, as well as manage access control, query audit logs, and more. \ No newline at end of file diff --git a/docs/documentation/getting-started/api.mdx b/docs/documentation/getting-started/api.mdx index 279982911..48a6f2ee0 100644 --- a/docs/documentation/getting-started/api.mdx +++ b/docs/documentation/getting-started/api.mdx @@ -2,58 +2,127 @@ title: "REST API" --- -Infisical's Public (REST) API is the most flexible, platform-agnostic way to read/write secrets for your application. +Infisical's REST API is the most flexible way to read/write secrets for your application. -Prerequisites: +In this brief, we'll explore how to fetch a secret back from a project on [Infisical Cloud](https://app.infisical.com) via the REST API. -- Have a project with secrets ready in [Infisical Cloud](https://app.infisical.com). -- Create an [Infisical Token](/documentation/platform/token) scoped to an environment in your project in Infisical. + + + To create a project, head to your Organization Overview and press **Add New Project**; we'll call the project **Demo App**. + ![create project](../../images/getting-started/api/org-create-project-1.png) + + ![create project](../../images/getting-started/api/org-create-project-2.png) + + Next, let's head to the **Development** environment of the project and add a secret `FOO=BAR` to it. + + ![explore project env](../../images/getting-started/api/project-explore-env.png) + + ![create secret](../../images/getting-started/api/project-create-secret.png) + + ![project dashboard](../../images/getting-started/api/project-dashboard.png) -To keep it simple, we're going to fetch secrets from the API with **End-to-End Encryption (E2EE)** disabled. + + For this brief, you'll need to disable end-to-end encryption in your Project Settings + + + + Next, we need to create an identity to represent your application. To create one, head to your Organization Settings > Access Control > Machine Identities and press **Create identity**. - - It's possible to use the API with **E2EE** enabled but this means learning about how encryption works with Infisical and performing client-side encryption/decryption operations yourself. - yourself. - - If **E2EE** is a must for your team, we recommend either using one of the [Infisical SDKs](/documentation/getting-started/sdks) or checking out the [examples for E2EE](/api-reference/overview/examples/e2ee-disabled). - + ![identities organization](../../images/platform/identities/identities-org.png) + + When creating an identity, you specify an organization level [role](/documentation/platform/role-based-access-controls) for it to assume; you can configure roles in Organization Settings > Access Control > Organization Roles. + + ![identities organization create](../../images/platform/identities/identities-org-create.png) + + Once you've created an identity, you'll be prompted to configure the **Universal Auth** authentication method for it. + + ![identities organization create auth method](../../images/platform/identities/identities-org-create-auth-method.png) -## Configuration + + + In order to use the identity, you'll need the non-sensitive **Client ID** + of the identity and a **Client Secret** for it; you can think of these credentials akin to a username + and password used to authenticate with the Infisical API. With that, press on the key icon on the identity to generate a **Client Secret** + for it. + + ![identities client secret create](../../images/platform/identities/identities-org-client-secret.png) + ![identities client secret create](../../images/platform/identities/identities-org-client-secret-create-1.png) + ![identities client secret create](../../images/platform/identities/identities-org-client-secret-create-2.png) + + + To enable the identity to access your project, we need to add it to the project. To do this, head over to the **Demo App** Project Settings > Access Control > Machine Identities and press **Add identity**. -Head to your Project Settings, where you created your service token, and un-check the **E2EE** setting. + Next, select the identity you want to add to the project and the role you want to assign it. -## Retrieve Secret + ![identities project](../../images/platform/identities/identities-project.png) + + ![identities project create](../../images/platform/identities/identities-project-create.png) + + + To access the Infisical API as the identity, you should first perform a login operation + that is to exchange the **Client ID** and **Client Secret** of the identity for an access token + by making a request to the `/api/v1/auth/universal-auth/login` endpoint. + + #### Sample request -Retrieve a secret from the project and environment in Infisical scoped to your service token by making a HTTP request with the following format/details: + ``` + curl --location --request POST 'https://app.infisical.com/api/v1/auth/universal-auth/login' \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + --data-urlencode 'clientSecret=' \ + --data-urlencode 'clientId=' + ``` + + #### Sample response + + ``` + { + "accessToken": "...", + "expiresIn": 7200, + "tokenType": "Bearer" + } + ``` -```bash -curl --location --request GET 'https://app.infisical.com/api/v3/secrets/raw/secretName?workspaceId=workspaceId&environment=environment' \ - --header 'Authorization: Bearer serviceToken' -``` + Next, we can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction) to read/write secrets + + + Each identity access token has a time-to-live (TLL) which you can infer from the response of the login operation; + the default TTL is `7200` seconds which can be adjusted. - - Name of secret to retrieve - - - The ID of the workspace - - - The environment slug - - - Path to secrets in workspace - - - The type of the secret. Valid options are “shared” or “personal” - + If an identity access token expires, it can no longer authenticate with the Infisical API. In this case, + a new access token should be obtained from the aforementioned login operation. + + + + Finally, you can fetch the secret `FOO=BAR` back from **Step 1** by including the access token in the previous step in another request to the `/api/v3/secrets/raw/{secretName}` endpoint. -Depending on your application requirements, you may wish to use Infisical's API in different ways such as by retaining **E2EE** -or fetching multiple secrets at once instead of one at a time. + ### Sample request + + ``` + curl --location --request GET 'http://localhost:8080/api/v3/secrets/raw/FOO?workspaceId=657830d579cfc8415d06ce5b&environment=dev' \ + --header 'Authorization: Bearer ' + ``` + + ### Sample response -Whatever the case, we recommend glossing over the [API Examples](/api-reference/overview/examples/note) -to gain a deeper understanding of how you to best leverage the Infisical API for your use-case. + ``` + { + "secret": { + "_id": "6564234b934d634e1fcd6cdf", + "version": 1, + "workspace": "6564173e934d634e1fcd6950", + "type": "shared", + "environment": "dev", + "secretKey": "FOO2", + "secretValue": "BAR2", + "secretComment": "" + } + } + ``` + + Note that you can fetch a list of secrets back by making a request to the `/api/v3/secrets/raw` endpoint. + + See also: -- Explore the [API Examples](/api-reference/overview/examples/note) - [API Reference](/api-reference/overview/introduction) \ No newline at end of file diff --git a/docs/documentation/platform/identities/overview.mdx b/docs/documentation/platform/identities/overview.mdx new file mode 100644 index 000000000..5197fa411 --- /dev/null +++ b/docs/documentation/platform/identities/overview.mdx @@ -0,0 +1,54 @@ +--- +title: Identities +description: "Programmatically interact with Infisical" +--- + + + Currently, identities can only be used to make authenticated requests to the Infisical API and do not work with any clients such as [Node SDK](https://github.com/Infisical/infisical-node) + , [Python SDK](https://github.com/Infisical/infisical-python), CLI, K8s operator, Terraform Provider, etc. + + We will be releasing compatibility with it across clients in the coming quarter. + + +## Concept + +A (machine) identity is an entity that you can create in an Infisical organization to represent a workload or application that requires access to the Infisical API. This is conceptually similar to an IAM user in AWS or service account in Google Cloud Platform (GCP). + +Each identity must authenticate with the API using a supported authentication method like [Universal Auth](/documentation/platform/identities/universal-auth) to get back a short-lived access token to be used in subsequent requests. + +Key Features: + +- Role Assignment: Identities must be assigned [roles](/documentation/platform/role-based-access-controls). These roles determine the scope of access to resources, either at the organization level or project level. +- Auth/Token Configuration: Identities must be configured with auth methods and access token properties to securely interact with the Infisical API. + +## Workflow + +A typical workflow for using identities consists of four steps: + +1. Creating the identity with a name and [role](/documentation/platform/role-based-access-controls) in Organization Access Control > Machine Identities. +This step also involves configuring an authentication method for it such as [Universal Auth](/documentation/platform/identities/universal-auth). +2. Adding the identity to the project(s) you want it to have access to. +3. Authenticating the identity with the Infisical API based on the configured authentication method on it and receiving a short-lived access token back. +4. Authenticating subsequent requests with the Infisical API using the short-lived access token. + +Check out the following authentication method-specific guides for step-by-step instruction on how to use identities to access Infisical: + +- [Universal Auth](/documentation/platform/identities/universal-auth) + +**FAQ** + + + + A service token is a project-level authentication method that is being phased out in favor of identities. + + Amongst many differences, identities provide broader access over the Infisical API, utilizes the same role-based + permission system used by users, and comes with ample more configurable authentication and security features. + + + There are a few reasons for why this might happen: + + - You have insufficient organization permissions to create, read, update, delete identities. + - The identity you are trying to read, update, or delete is more privileged than yourself. + - The role you are trying to create an identity for or update an identity to is more privileged than yours. + + \ No newline at end of file diff --git a/docs/documentation/platform/identities/universal-auth.mdx b/docs/documentation/platform/identities/universal-auth.mdx new file mode 100644 index 000000000..53cf811a4 --- /dev/null +++ b/docs/documentation/platform/identities/universal-auth.mdx @@ -0,0 +1,140 @@ +--- +title: Universal Auth +description: "Authenticate with Infisical from any platform/environment" +--- + +**Universal Auth** is the most versatile authentication method that can be configured on an identity from any platform/environment to access Infisical. + +In this method, each identity is given a **Client ID** for which you can generate one or more **Client Secret(s)**. Together, a **Client ID** and **Client Secret** can be exchanged for an access token to authenticate with the Infisical API. + +## Properties + +Universal Auth supports many settings that can be beneficial for tightening your workflow security configuration: + +- Support for restrictions on the number of times that the **Client Secret(s)** and access token(s) can be used. +- Support for expiration, so, if specified, the **Client Secret** of the identity will automatically be defunct after a period of time. +- Support for IP allowlisting; this means you can restrict the usage of **Client Secret(s)** and access token to a specific IP or CIDR range. + +## Workflow + +In the following steps, we explore how to create and use identities for your workloads and applications to access the Infisical API +using the Universal Auth authentication method. + + + + To create an identity, head to your Organization Settings > Access Control > Machine Identities and press **Create identity**. + + ![identities organization](../../images/platform/identities/identities-org.png) + + When creating an identity, you specify an organization level [role](/documentation/platform/role-based-access-controls) for it to assume; you can configure roles in Organization Settings > Access Control > Organization Roles. + + ![identities organization create](../../images/platform/identities/identities-org-create.png) + + Now input a few details for your new identity. Here's some guidance for each field: + + - Name (required): A friendly name for the identity. + - Role (required): A role from the **Organization Roles** tab for the identity to assume. The organization role assigned will determine what organization level resources this identity can have access to. + + Once you've created an identity, you'll be prompted to configure the **Universal Auth** authentication method for it. + + ![identities organization create auth method](../../images/platform/identities/identities-org-create-auth-method.png) + + Here's some more guidance on each field: + + - Access Token TTL (default is `2592000` equivalent to 30 days): The lifetime for an acccess token in seconds. This value will be referenced at renewal time. + - Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time. + - Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses. + - Client Secret Trusted IPs: The IPs or CIDR ranges that the **Client Secret** can be used from together with the **Client ID** to get back an access token. By default, **Client Secrets** are given the `0.0.0.0/0`, allowing usage from any network address. + - Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address. + + + Restricting **Client Secret** and access token usage to specific trusted IPs is a paid feature. + + If you’re using Infisical Cloud, then it is available under the Pro Tier. If you’re self-hosting Infisical, then you should contact team@infisical.com to purchase an enterprise license to use it. + + + + + In order to use the identity, you'll need the non-sensitive **Client ID** + of the identity and a **Client Secret** for it; you can think of these credentials akin to a username + and password used to authenticate with the Infisical API. With that, press on the key icon on the identity to generate a **Client Secret** + for it. + + ![identities client secret create](../../images/platform/identities/identities-org-client-secret.png) + ![identities client secret create](../../images/platform/identities/identities-org-client-secret-create-1.png) + ![identities client secret create](../../images/platform/identities/identities-org-client-secret-create-2.png) + + Feel free to input any (optional) details for the **Client Secret** configuration: + + - Description: A description for the **Client Secret**. + - TTL (default is `0`): The time-to-live for the **Client Secret**. By default, the TTL will be set to 0 which implies that the **Client Secret** will never expire; a value of `0` implies an infinite lifetime. + - Max Number of Uses (default is `0`): The maximum number of times that the **Client Secret** can be used together with the **Client ID** to get back an access token; a value of `0` implies infinite number of uses. + + + To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project. + + To do this, head over to the project you want to add the identity to and go to Project Settings > Access Control > Machine Identities and press **Add identity**. + + Next, select the identity you want to add to the project and the project level role you want to allow it to assume. The project role assigned will determine what project level resources this identity can have access to. + + ![identities project](../../images/platform/identities/identities-project.png) + + ![identities project create](../../images/platform/identities/identities-project-create.png) + + + To access the Infisical API as the identity, you should first perform a login operation + that is to exchange the **Client ID** and **Client Secret** of the identity for an access token + by making a request to the `/api/v1/auth/universal-auth/login` endpoint. + + #### Sample request + + ``` + curl --location --request POST 'https://app.infisical.com/api/v1/auth/universal-auth/login' \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + --data-urlencode 'clientSecret=...' \ + --data-urlencode 'clientId=...' + ``` + + #### Sample response + + ``` + { + "accessToken": "...", + "expiresIn": 7200, + "accessTokenMaxTTL": 43244 + "tokenType": "Bearer" + } + ``` + + Next, you can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction) + + + Each identity access token has a time-to-live (TLL) which you can infer from the response of the login operation; + the default TTL is `7200` seconds which can be adjusted. + + If an identity access token expires, it can no longer authenticate with the Infisical API. In this case, + a new access token should be obtained by performing another login operation. + + + + +**FAQ** + + + + There are a few reasons for why this might happen: + + - The client secret or access token has expired. + - The identity is insufficently permissioned to interact with the resources you wish to access. + - You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE. + - The client secret/access token is being used from an untrusted IP. + + + A identity access token can have a time-to-live (TTL) or incremental lifetime afterwhich it expires. + + In certain cases, you may want to extend the lifespan of an access token; to do so, you must set a max TTL parameter. + + A token can be renewed any number of time and each call to renew it will extend the toke life by increments of access token TTL. + Regardless of how frequently an access token is renewed, its lifespan remains bound to the maximum TTL determined at its creation + + \ No newline at end of file diff --git a/docs/documentation/platform/identity.mdx b/docs/documentation/platform/identity.mdx deleted file mode 100644 index c9c85e78c..000000000 --- a/docs/documentation/platform/identity.mdx +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: Identity -description: "Programmatically interact with Infisical" ---- - -A (machine) identity is an entity that you can create in Infisical. -Each identity represents a workload that wishes to access the Infisical API via an authentication method; this is similar to an IAM user in AWS or service account in GCP. - -An identity can be provisioned scoped access to resources at the organization or project-level via [role-based access controls (RBAC)](/documentation/platform/role-based-access-controls). For instance, you may create a identity with scoped access to -fetch secrets back from the `/` path of the `development` environment in some project. - - - The identity feature is in beta. - - Currently, an identity can only be used to make authenticated requests to the Infisical API and does not work with any clients such as [Node SDK](https://github.com/Infisical/infisical-node) - , [Python SDK](https://github.com/Infisical/infisical-python), CLI, K8s operator, Terraform Provider, etc. - - We will be releasing compatibility with it across clients in the coming quarter. - - -Each identity can be configured an authentication method. The only supported method at the moment is **Universal Auth (UA)** -which has the following properties: - -- In UA, each identity is assigned a **Client ID** for which you can generate one or more **Client Secret(s)**. Together, a **Client ID** and **Client Secret** can be exchanged for an access token (i.e. login operation) to authenticate with the Infisical API. -- UA supports restrictions on the number of times that the **Client Secret(s)** and access token(s) can be used. -- UA supports token renewal that is the ability to extend the lifetime of a token by its TTL up to its maximum TTL since its creation. -- UA supports IP allowlisting; this means you can restrict the usage of **Client Secret(s)** and access token to a specific IP or CIDR range. -- UA support expiration, so, if specified, the client secret of the identity will automatically be defunct after a period of time. -- UA tracks most recent usage of their client secrets and access tokens; it also keeps track of each token's usage count. - -## Using identities - -In the following steps, we explore how to create and use identities for your applications to access the Infisical API. - - - - To create an identity, head to your Organization Settings > Access Control > Machine Identities and press **Create identity**. - - ![machine identities organization](../../images/platform/machine-identity/machine-identity-org.png) - - ![machine identities organization create](../../images/platform/machine-identity/machine-identity-org-create.png) - - Now input a few details for your new identity. Here's some guidance for each field: - - - Name (required): A friendly name for the identity. - - Role (required): A role from the **Organization Roles** tab to permit the identity to access certain resources. - - Once you've created an identity, you'll be prompted to configure the **Universal Auth** authentication method for it. - - - Access Token TTL (default is `7200`): The incremental lifetime for an acccess token in seconds; a value of `0` implies an infinite incremental lifetime. - - Access Token Max TTL (default is `7200`): The maximum lifetime for an acccess token in seconds; a value of `0` implies an infinite maximum lifetime. - - Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses. - - Client Secret Trusted IPs: The IPs or CIDR ranges that the **Client Secret** can be used from together with the **Client ID** to get back an access token. By default, **Client Secrets** are given the `0.0.0.0/0` entry representing all possible IPv4 addresses. - - Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0` entry representing all possible IPv4 addresses. - - - Restricting **Client Secret** and access token usage to specific trusted IPs is a paid feature. - - If you’re using Infisical Cloud, then it is available under the Pro Tier. If you’re self-hosting Infisical, then you should contact team@infisical.com to purchase an enterprise license to use it. - - - - - In order to use the identity, you'll need the non-sensitive **Client ID** - of the identity and a **Client Secret** for it; you can think of these credentials akin to a username - and password used to authenticate with the Infisical API. With that, press on the key icon on the identity to generate a **Client Secret** - for it. - - ![machine identities client secret create](../../images/platform/machine-identity/machine-identity-org-client-secret.png) - ![machine identities client secret create](../../images/platform/machine-identity/machine-identity-org-client-secret-create-1.png) - ![machine identities client secret create](../../images/platform/machine-identity/machine-identity-org-client-secret-create-2.png) - - Feel free to input any (optional) details for the **Client Secret** configuration: - - - Description: A description for the **Client Secret**. - - TTL (default is `0`): The time-to-live for the **Client Secret**. By default, the TTL will be set to 0 which implies that the **Client Secret** will never expire; a value of `0` implies an infinite lifetime. - - Max Number of Uses (default is `0`): The maximum number of times that the **Client Secret** can be used together with the **Client ID** to get back an access token; a value of `0` implies infinite number of uses. - - - To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project. - - To do this, head over to the project you want to add the identity to and go to Project Settings > Access Control > Machine Identities and press **Add identity**. - - Next, select the identity you want to add to the project and the role you want to assign it. - - ![machine identities project](../../images/platform/machine-identity/machine-identity-project.png) - - ![machine identities project create](../../images/platform/machine-identity/machine-identity-project-create.png) - - - To access the Infisical API as the identity, you should first perform a login operation - that is to exchange the **Client ID** and **Client Secret** of the MI for an access token - by making a request to the `/api/v1/auth/universal-auth/login` endpoint. - - #### Sample request - - ``` - curl --location --request POST 'https://app.infisical.com/api/v1/auth/universal-auth/login' \ - --header 'Content-Type: application/x-www-form-urlencoded' \ - --data-urlencode 'clientSecret=...' \ - --data-urlencode 'clientId=...' - ``` - - #### Sample response - - ``` - { - "accessToken": "...", - "expiresIn": 7200, - "tokenType": "Bearer" - } - ``` - - Next, you can use the access token to authenticate with the [Infisical API](/api-reference/overview/introduction) - - - Each identity access token has a time-to-live (TLL) which you can infer from the response of the login operation; - the default TTL is `7200` seconds which can be adjusted. - - If an identity access token expires, it can no longer authenticate with the Infisical API. In this case, - a new access token should be obtained from the aforementioned login operation. - - - - -**FAQ** - - - - A service token is a project-level authentication method that is being phased out in favor of identities. - - Amongst many differences, identities provide broader access over the Infisical API, utilizes the same role-based - permission system used by users, and comes with ample more configurable security measures. - - - There are a few reasons for why this might happen: - - - The client secret or access token has expired. - - The identity is insufficently permissioned to interact with the resources you wish to access. - - You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE. - - The client secret/access token is being used from an untrusted IP. - - - A identity access token can have a time-to-live (TTL) or incremental lifetime afterwhich it expires. - - In certain cases, you may want to extend the lifespan of an access token; to do so, you must use the max TTL parameter. - When TTL and max TTL are equal, a token is not renewable; when max TTL is greater than TTL, a token is renewable. - In the latter case, a token still expires at its TTL but its lifetime can be extended/renewed up until its max TLL. - - Note that the max TTL cannot be less than the TTL for an access token. - - - There are a few reasons for why this might happen: - - - You have insufficient organization permissions to create, read, update, delete identities. - - The identity you are trying to read, update, or delete is more privileged than yourself. - - The role you are trying to create an identity for or update an identity to is more privileged than yours. - - - 1. `/**`: This pattern matches all folders at any depth in the directory structure. For example, it would match folders like `/folder1/`, `/folder1/subfolder/`, and so on. - - 2. `/*`: This pattern matches all immediate subfolders in the current directory. It does not match any folders at a deeper level. For example, it would match folders like `/folder1/`, `/folder2/`, but not `/folder1/subfolder/`. - - 3. `/*/*`: This pattern matches all subfolders at a depth of two levels in the current directory. It does not match any folders at a shallower or deeper level. For example, it would match folders like `/folder1/subfolder/`, `/folder2/subfolder/`, but not `/folder1/` or `/folder1/subfolder/subsubfolder/`. - - 4. `/folder1/*`: This pattern matches all immediate subfolders within the `/folder1/` directory. It does not match any folders outside of `/folder1/`, nor does it match any subfolders within those immediate subfolders. For example, it would match folders like `/folder1/subfolder1/`, `/folder1/subfolder2/`, but not `/folder2/subfolder/`. - - \ No newline at end of file diff --git a/docs/images/getting-started/api/org-create-project-1.png b/docs/images/getting-started/api/org-create-project-1.png new file mode 100644 index 000000000..468a264f0 Binary files /dev/null and b/docs/images/getting-started/api/org-create-project-1.png differ diff --git a/docs/images/getting-started/api/org-create-project-2.png b/docs/images/getting-started/api/org-create-project-2.png new file mode 100644 index 000000000..ecf420f66 Binary files /dev/null and b/docs/images/getting-started/api/org-create-project-2.png differ diff --git a/docs/images/getting-started/api/project-create-secret.png b/docs/images/getting-started/api/project-create-secret.png new file mode 100644 index 000000000..6fbe8af62 Binary files /dev/null and b/docs/images/getting-started/api/project-create-secret.png differ diff --git a/docs/images/getting-started/api/project-dashboard.png b/docs/images/getting-started/api/project-dashboard.png new file mode 100644 index 000000000..bf810d807 Binary files /dev/null and b/docs/images/getting-started/api/project-dashboard.png differ diff --git a/docs/images/getting-started/api/project-explore-env.png b/docs/images/getting-started/api/project-explore-env.png new file mode 100644 index 000000000..88a8f105f Binary files /dev/null and b/docs/images/getting-started/api/project-explore-env.png differ diff --git a/docs/images/platform/identities/identities-org-client-secret-create-1.png b/docs/images/platform/identities/identities-org-client-secret-create-1.png new file mode 100644 index 000000000..17b9c0b5d Binary files /dev/null and b/docs/images/platform/identities/identities-org-client-secret-create-1.png differ diff --git a/docs/images/platform/identities/identities-org-client-secret-create-2.png b/docs/images/platform/identities/identities-org-client-secret-create-2.png new file mode 100644 index 000000000..8813631b9 Binary files /dev/null and b/docs/images/platform/identities/identities-org-client-secret-create-2.png differ diff --git a/docs/images/platform/identities/identities-org-client-secret.png b/docs/images/platform/identities/identities-org-client-secret.png new file mode 100644 index 000000000..dd8f579ce Binary files /dev/null and b/docs/images/platform/identities/identities-org-client-secret.png differ diff --git a/docs/images/platform/identities/identities-org-create-auth-method.png b/docs/images/platform/identities/identities-org-create-auth-method.png new file mode 100644 index 000000000..2bf4fd759 Binary files /dev/null and b/docs/images/platform/identities/identities-org-create-auth-method.png differ diff --git a/docs/images/platform/identities/identities-org-create.png b/docs/images/platform/identities/identities-org-create.png new file mode 100644 index 000000000..06a1ef496 Binary files /dev/null and b/docs/images/platform/identities/identities-org-create.png differ diff --git a/docs/images/platform/identities/identities-org.png b/docs/images/platform/identities/identities-org.png new file mode 100644 index 000000000..ad75b3dd1 Binary files /dev/null and b/docs/images/platform/identities/identities-org.png differ diff --git a/docs/images/platform/identities/identities-project-create.png b/docs/images/platform/identities/identities-project-create.png new file mode 100644 index 000000000..d7a2cc5e1 Binary files /dev/null and b/docs/images/platform/identities/identities-project-create.png differ diff --git a/docs/images/platform/identities/identities-project.png b/docs/images/platform/identities/identities-project.png new file mode 100644 index 000000000..b02b7cfca Binary files /dev/null and b/docs/images/platform/identities/identities-project.png differ diff --git a/docs/images/platform/machine-identity/machine-identity-org-client-secret-create-1.png b/docs/images/platform/machine-identity/machine-identity-org-client-secret-create-1.png deleted file mode 100644 index 5e8970dc2..000000000 Binary files a/docs/images/platform/machine-identity/machine-identity-org-client-secret-create-1.png and /dev/null differ diff --git a/docs/images/platform/machine-identity/machine-identity-org-client-secret-create-2.png b/docs/images/platform/machine-identity/machine-identity-org-client-secret-create-2.png deleted file mode 100644 index 5c50589b5..000000000 Binary files a/docs/images/platform/machine-identity/machine-identity-org-client-secret-create-2.png and /dev/null differ diff --git a/docs/images/platform/machine-identity/machine-identity-org-client-secret.png b/docs/images/platform/machine-identity/machine-identity-org-client-secret.png deleted file mode 100644 index 71c6d4334..000000000 Binary files a/docs/images/platform/machine-identity/machine-identity-org-client-secret.png and /dev/null differ diff --git a/docs/images/platform/machine-identity/machine-identity-org-create.png b/docs/images/platform/machine-identity/machine-identity-org-create.png deleted file mode 100644 index 947a0b552..000000000 Binary files a/docs/images/platform/machine-identity/machine-identity-org-create.png and /dev/null differ diff --git a/docs/images/platform/machine-identity/machine-identity-org.png b/docs/images/platform/machine-identity/machine-identity-org.png deleted file mode 100644 index 885e51e1c..000000000 Binary files a/docs/images/platform/machine-identity/machine-identity-org.png and /dev/null differ diff --git a/docs/images/platform/machine-identity/machine-identity-project-create.png b/docs/images/platform/machine-identity/machine-identity-project-create.png deleted file mode 100644 index 084c2b4c4..000000000 Binary files a/docs/images/platform/machine-identity/machine-identity-project-create.png and /dev/null differ diff --git a/docs/images/platform/machine-identity/machine-identity-project.png b/docs/images/platform/machine-identity/machine-identity-project.png deleted file mode 100644 index a35e957cc..000000000 Binary files a/docs/images/platform/machine-identity/machine-identity-project.png and /dev/null differ diff --git a/docs/mint.json b/docs/mint.json index 80f4d8226..2cfacf1bd 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -117,8 +117,14 @@ "documentation/platform/webhooks", "documentation/platform/pit-recovery", "documentation/platform/audit-logs", + { + "group": "Identities", + "pages": [ + "documentation/platform/identities/overview", + "documentation/platform/identities/universal-auth" + ] + }, "documentation/platform/token", - "documentation/platform/identity", "documentation/platform/mfa", "documentation/platform/pr-workflows", "documentation/platform/role-based-access-controls", @@ -317,8 +323,7 @@ "api-reference/overview/examples/e2ee-disabled", "api-reference/overview/examples/e2ee-enabled" ] - }, - "api-reference/overview/blind-indices" + } ] }, { @@ -331,12 +336,34 @@ "api-reference/endpoints/users/my-organizations" ] }, + { + "group": "Identities", + "pages": [ + "api-reference/endpoints/identities/create", + "api-reference/endpoints/identities/update", + "api-reference/endpoints/identities/delete" + ] + }, + { + "group": "Universal Auth", + "pages": [ + "api-reference/endpoints/universal-auth/login", + "api-reference/endpoints/universal-auth/attach", + "api-reference/endpoints/universal-auth/retrieve", + "api-reference/endpoints/universal-auth/update", + "api-reference/endpoints/universal-auth/create-client-secret", + "api-reference/endpoints/universal-auth/list-client-secrets", + "api-reference/endpoints/universal-auth/revoke-client-secret", + "api-reference/endpoints/universal-auth/renew-access-token" + ] + }, { "group": "Organizations", "pages": [ "api-reference/endpoints/organizations/memberships", "api-reference/endpoints/organizations/update-membership", "api-reference/endpoints/organizations/delete-membership", + "api-reference/endpoints/organizations/list-identity-memberships", "api-reference/endpoints/organizations/workspaces" ] }, @@ -346,6 +373,9 @@ "api-reference/endpoints/workspaces/memberships", "api-reference/endpoints/workspaces/update-membership", "api-reference/endpoints/workspaces/delete-membership", + "api-reference/endpoints/workspaces/list-identity-memberships", + "api-reference/endpoints/workspaces/update-identity-membership", + "api-reference/endpoints/workspaces/delete-identity-membership", "api-reference/endpoints/workspaces/workspace-key", "api-reference/endpoints/workspaces/secret-snapshots", "api-reference/endpoints/workspaces/rollback-snapshot" diff --git a/docs/spec.yaml b/docs/spec.yaml index 366c05438..a0a2dc13f 100644 --- a/docs/spec.yaml +++ b/docs/spec.yaml @@ -9,6 +9,106 @@ servers: - url: http://localhost:8080 description: Local server paths: + /api/v1/identities/: + post: + summary: Create identity + description: Create identity + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identity: + $ref: '#/components/schemas/Identity' + description: Details of the created identity + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: Name of entity to create + example: development + organizationId: + type: string + description: ID of organization where to create identity + example: dev-environment + role: + type: string + description: Role to assume for organization membership + example: no-access + required: + - name + - organizationId + - role + /api/v1/identities/{identityId}: + patch: + summary: Update identity + description: Update identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity to update + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identity: + $ref: '#/components/schemas/Identity' + description: Details of the updated identity + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: Name of entity to update to + example: development + role: + type: string + description: Role to update to for organization membership + example: no-access + delete: + summary: Delete identity + description: Delete identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identity: + $ref: '#/components/schemas/Identity' + description: Details of the deleted identity + security: + - bearerAuth: [] /api/v1/secret/{secretId}/secret-versions: get: summary: Return secret versions @@ -110,7 +210,21 @@ paths: required: true schema: type: string - description: ID of project + description: ID of project where to get secret snapshots for + - name: environment + description: Slug of environment where to get secret snapshots for + required: true + in: query + schema: + type: string + - name: directory + description: >- + Path where to get secret snapshots for like / or /foo/bar. Default + is / + required: false + in: query + schema: + type: string - name: offset description: Number of secret snapshots to skip required: false @@ -138,6 +252,7 @@ paths: description: Project secret snapshots security: - apiKeyAuth: [] + bearerAuth: [] /api/v1/workspace/{workspaceId}/secret-snapshots/count: get: description: '' @@ -164,7 +279,7 @@ paths: required: true schema: type: string - description: ID of project + description: ID of project where to roll back responses: '200': description: OK @@ -180,6 +295,7 @@ paths: description: Secrets rolled back to security: - apiKeyAuth: [] + bearerAuth: [] requestBody: required: true content: @@ -187,6 +303,12 @@ paths: schema: type: object properties: + environment: + type: string + description: Slug of environment where to roll back + directory: + type: string + description: Path where to roll back for like / or /foo/bar. Default is / version: type: integer description: Version of secret snapshot to roll back to @@ -604,51 +726,6 @@ paths: responses: '200': description: OK - /api/v3/service-token/me/key: - get: - description: '' - responses: - '200': - description: OK - /api/v3/service-token/me/token: - post: - description: '' - responses: - '200': - description: OK - /api/v3/service-token/: - post: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v3/service-token/{serviceTokenDataId}: - patch: - description: '' - parameters: - - name: serviceTokenDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - delete: - description: '' - parameters: - - name: serviceTokenDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK /api/v1/secret-rotation-providers/{workspaceId}: get: description: '' @@ -749,6 +826,374 @@ paths: responses: '200': description: OK + /api/v1/auth/token/renew: + post: + summary: Renew access token + description: Renew access token + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + accessToken: + type: string + description: (Same) Access token after successful renewal + expiresIn: + type: number + description: TTL of access token in seconds + tokenType: + type: string + description: Type of access token (e.g. Bearer) + description: Access token and its details + requestBody: + content: + application/json: + schema: + type: object + properties: + accessToken: + type: string + description: Access token to renew + example: ... + /api/v1/auth/universal-auth/login: + post: + summary: Login with Universal Auth + description: Login with Universal Auth + parameters: + - name: user-agent + in: header + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + accessToken: + type: string + description: Access token issued after successful login + expiresIn: + type: number + description: TTL of access token in seconds + tokenType: + type: string + description: Type of access token (e.g. Bearer) + description: Access token and its details + requestBody: + content: + application/json: + schema: + type: object + properties: + clientId: + type: string + description: Client ID for identity to login with Universal Auth + example: ... + clientSecret: + type: string + description: Client Secret for identity to login with Universal Auth + example: ... + /api/v1/auth/universal-auth/identities/{identityId}: + post: + summary: Attach Universal Auth configuration onto identity + description: Attach Universal Auth configuration onto identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity to attach Universal Auth onto + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identityUniversalAuth: + $ref: '#/components/schemas/IdentityUniversalAuth' + description: Details of attached Universal Auth + '400': + description: Bad Request + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + clientSecretTrustedIps: + type: array + items: + type: object + properties: + ipAddress: + type: string + description: IP address to trust + default: 0.0.0.0/0 + description: >- + List of IPs or CIDR ranges that the Client Secret can be + used from together with the Client ID to get back an access + token. By default, Client Secrets are given the 0.0.0.0/0 + entry representing all possible IPv4 addresses. + example: ... + default: + - ipAddress: 0.0.0.0/0 + accessTokenTTL: + type: number + description: >- + The incremental lifetime for an acccess token in seconds; a + value of 0 implies an infinite incremental lifetime. + example: ... + default: 100 + accessTokenMaxTTL: + type: number + description: >- + The maximum lifetime for an acccess token in seconds; a + value of 0 implies an infinite maximum lifetime. + example: ... + default: 2592000 + accessTokenNumUsesLimit: + type: number + description: >- + The maximum number of times that an access token can be + used; a value of 0 implies infinite number of uses. + example: ... + default: 0 + accessTokenTrustedIps: + type: array + items: + type: object + properties: + ipAddress: + type: string + description: IP address to trust + default: 0.0.0.0/0 + description: >- + List of IPs or CIDR ranges that access tokens can be used + from. By default, each token is given the 0.0.0.0/0 entry + representing all possible IPv4 addresses. + example: ... + default: + - ipAddress: 0.0.0.0/0 + patch: + summary: Update Universal Auth configuration on identity + description: Update Universal Auth configuration on identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity to update Universal Auth on + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identityUniversalAuth: + $ref: '#/components/schemas/IdentityUniversalAuth' + description: Details of updated Universal Auth + '400': + description: Bad Request + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + clientSecretTrustedIps: + type: array + items: + type: object + properties: + ipAddress: + type: string + description: IP address to trust + description: >- + List of IPs or CIDR ranges that the Client Secret can be + used from together with the Client ID to get back an access + token. By default, Client Secrets are given the 0.0.0.0/0 + entry representing all possible IPv4 addresses. + example: ... + accessTokenTTL: + type: number + description: >- + The incremental lifetime for an acccess token in seconds; a + value of 0 implies an infinite incremental lifetime. + example: ... + accessTokenMaxTTL: + type: number + description: >- + The maximum lifetime for an acccess token in seconds; a + value of 0 implies an infinite maximum lifetime. + example: ... + accessTokenNumUsesLimit: + type: number + description: >- + The maximum number of times that an access token can be + used; a value of 0 implies infinite number of uses. + example: ... + accessTokenTrustedIps: + type: array + items: + type: object + properties: + ipAddress: + type: string + description: IP address to trust + description: >- + List of IPs or CIDR ranges that access tokens can be used + from. By default, each token is given the 0.0.0.0/0 entry + representing all possible IPv4 addresses. + example: ... + get: + summary: Retrieve Universal Auth configuration on identity + description: Retrieve Universal Auth configuration on identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity to retrieve Universal Auth on + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identityUniversalAuth: + $ref: '#/components/schemas/IdentityUniversalAuth' + description: Details of retrieved Universal Auth + security: + - bearerAuth: [] + /api/v1/auth/universal-auth/identities/{identityId}/client-secrets: + post: + summary: Create Universal Auth Client Secret for identity + description: Create Universal Auth Client Secret for identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity to create Universal Auth Client Secret for + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + clientSecret: + type: string + description: The created Client Secret + clientSecretData: + $ref: '#/components/schemas/IdentityUniversalAuthClientSecretData' + description: Details of the created Client Secret + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + description: + type: string + description: A description for the Client Secret to create. + example: ... + ttl: + type: number + description: >- + The time-to-live for the Client Secret to create. By + default, the TTL will be set to 0 which implies that the + Client Secret will never expire; a value of 0 implies an + infinite lifetime. + example: ... + default: 0 + numUsesLimit: + type: number + description: >- + The maximum number of times that the Client Secret can be + used together with the Client ID to get back an access + token; a value of 0 implies infinite number of uses. + example: ... + default: 0 + get: + summary: List Universal Auth Client Secrets for identity + description: List Universal Auth Client Secrets for identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity for which to get Client Secrets for + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + clientSecretData: + type: array + items: + $ref: >- + #/components/schemas/IdentityUniversalAuthClientSecretData + description: Details of the Client Secrets + security: + - bearerAuth: [] + /api/v1/auth/universal-auth/identities/{identityId}/client-secrets/{clientSecretId}/revoke: + post: + summary: Revoke Universal Auth Client Secret for identity + description: Revoke Universal Auth Client Secret for identity + parameters: + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity under which Client Secret was issued for + - name: clientSecretId + in: path + required: true + schema: + type: string + description: ID of Client Secret to revoke + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + clientSecretData: + $ref: '#/components/schemas/IdentityUniversalAuthClientSecretData' + description: Details of the revoked Client Secret + security: + - bearerAuth: [] /api/v1/admin/config: get: description: '' @@ -1106,6 +1551,8 @@ paths: responses: '200': description: OK + '400': + description: Bad Request /api/v1/key/{workspaceId}: post: description: '' @@ -2560,8 +3007,8 @@ paths: description: OK /api/v2/organizations/{organizationId}/memberships: get: - summary: Return organization memberships - description: Return organization memberships + summary: Return organization user memberships + description: Return organization user memberships parameters: - name: organizationId in: path @@ -2584,10 +3031,11 @@ paths: description: Memberships of organization security: - apiKeyAuth: [] + bearerAuth: [] /api/v2/organizations/{organizationId}/memberships/{membershipId}: patch: - summary: Update organization membership - description: Update organization membership + summary: Update organization user membership + description: Update organization user membership parameters: - name: organizationId in: path @@ -2612,8 +3060,11 @@ paths: membership: $ref: '#/components/schemas/MembershipOrg' description: Updated organization membership + '400': + description: Bad Request security: - apiKeyAuth: [] + bearerAuth: [] requestBody: required: true content: @@ -2627,8 +3078,8 @@ paths: Role of organization membership - either owner, admin, or member delete: - summary: Delete organization membership - description: Delete organization membership + summary: Delete organization user membership + description: Delete organization user membership parameters: - name: organizationId in: path @@ -2655,6 +3106,7 @@ paths: description: Deleted organization membership security: - apiKeyAuth: [] + bearerAuth: [] /api/v2/organizations/{organizationId}/workspaces: get: summary: Return projects in organization that user is part of @@ -2699,6 +3151,32 @@ paths: responses: '200': description: OK + /api/v2/organizations/{organizationId}/identity-memberships: + get: + summary: Return organization identity memberships + description: Return organization identity memberships + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + description: ID of organization + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identityMemberships: + type: array + items: + $ref: '#/components/schemas/IdentityMembershipOrg' + description: Identity memberships of organization + security: + - bearerAuth: [] /api/v2/workspace/{workspaceId}/memberships: post: description: '' @@ -2712,8 +3190,8 @@ paths: '200': description: OK get: - summary: Return project memberships - description: Return project memberships + summary: Return project user memberships + description: Return project user memberships parameters: - name: workspaceId in: path @@ -2736,6 +3214,7 @@ paths: description: Memberships of project security: - apiKeyAuth: [] + bearerAuth: [] /api/v2/workspace/{workspaceId}/environments: post: summary: Create environment @@ -3031,8 +3510,8 @@ paths: description: OK /api/v2/workspace/{workspaceId}/memberships/{membershipId}: patch: - summary: Update project membership - description: Update project membership + summary: Update project user membership + description: Update project user membership parameters: - name: workspaceId in: path @@ -3059,6 +3538,7 @@ paths: description: Updated membership security: - apiKeyAuth: [] + bearerAuth: [] requestBody: required: true content: @@ -3068,10 +3548,10 @@ paths: properties: role: type: string - description: Role of membership - either admin or member + description: Role to update to for project membership delete: - summary: Delete project membership - description: Delete project membership + summary: Delete project user membership + description: Delete project user membership parameters: - name: workspaceId in: path @@ -3098,6 +3578,7 @@ paths: description: Deleted membership security: - apiKeyAuth: [] + bearerAuth: [] /api/v2/workspace/{workspaceId}/auto-capitalization: patch: description: '' @@ -3110,6 +3591,117 @@ paths: responses: '200': description: OK + /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: identityId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + patch: + summary: Update project identity membership + description: Update project identity membership + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + description: ID of project + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity whose membership to update in project + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identityMembership: + $ref: '#/components/schemas/IdentityMembership' + description: Updated identity membership + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + role: + type: string + description: Role to update to for identity project membership + delete: + summary: Delete project identity membership + description: Delete project identity membership + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + description: ID of project + - name: identityId + in: path + required: true + schema: + type: string + description: ID of identity whose membership to delete in project + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identityMembership: + $ref: '#/components/schemas/IdentityMembership' + description: Deleted identity membership + security: + - bearerAuth: [] + /api/v2/workspace/{workspaceId}/identity-memberships: + get: + summary: Return project identity memberships + description: Return project identity memberships + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + description: ID of project + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + identityMemberships: + type: array + items: + $ref: '#/components/schemas/IdentityMembership' + description: Identity memberships of project + security: + - bearerAuth: [] /api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}: post: description: '' @@ -3842,18 +4434,6 @@ paths: responses: '200': description: OK - /api/v3/workspaces/{workspaceId}/service-token: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK /api/v3/signup/complete-account/signup: post: description: '' @@ -3920,6 +4500,100 @@ components: createdAt: type: string example: '2023-01-13T14:16:12.210Z' + Identity: + type: object + properties: + _id: + type: string + example: '' + name: + type: string + example: Machine 1 + authMethod: + type: string + example: universal-auth + IdentityUniversalAuth: + type: object + properties: + _id: + type: string + example: '' + identity: + type: string + example: '' + clientId: + type: string + example: ... + clientSecretTrustedIps: + type: array + items: + type: object + properties: + ipAddress: + type: string + example: 0.0.0.0 + type: + type: string + example: ipv4 + prefix: + type: string + example: '0' + accessTokenTTL: + type: number + example: 7200 + accessTokenMaxTTL: + type: number + example: 2592000 + accessTokenNumUsesLimit: + type: number + example: 0 + accessTokenTrustedIps: + type: array + items: + type: object + properties: + ipAddress: + type: string + example: 0.0.0.0 + type: + type: string + example: ipv4 + prefix: + type: string + example: '0' + IdentityUniversalAuthClientSecretData: + type: object + properties: + _id: + type: string + example: '' + identityUniversalAuth: + type: string + example: '' + isClientSecretRevoked: + type: boolean + example: false + description: + type: string + example: '' + clientSecretPrefix: + type: string + example: abc + clientSecretNumUses: + type: number + example: 0 + clientSecretNumUsesLimit: + type: number + example: 0 + clientSecretTTL: + type: number + example: 0 + createdAt: + type: string + example: '2023-01-13T14:16:12.210Z' + updatedAt: + type: string + example: '2023-01-13T14:16:12.210Z' Membership: type: object properties: @@ -3989,6 +4663,51 @@ components: status: type: string example: accepted + IdentityMembership: + type: object + properties: + identity: + type: object + properties: + _id: + type: string + example: '' + name: + type: string + example: Machine 1 + authMethod: + type: string + example: universal-auth + workspace: + type: string + example: '' + role: + type: string + example: member + IdentityMembershipOrg: + type: object + properties: + identity: + type: object + properties: + _id: + type: string + example: '' + name: + type: string + example: Machine 1 + authMethod: + type: string + example: universal-auth + organization: + type: string + example: '' + role: + type: string + example: member + status: + type: string + example: accepted Organization: type: object properties: @@ -4424,7 +5143,7 @@ components: type: http scheme: bearer bearerFormat: JWT - description: A service token in Infisical + description: An access token in Infisical apiKeyAuth: type: apiKey in: header diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx index 99e5b4fea..6261da947 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx @@ -112,7 +112,7 @@ export const IdentityUniversalAuthForm = ({ } = useFieldArray({ control, name: "accessTokenTrustedIps" }); useEffect(() => { - if (data) { // TODO: fix data type + if (data) { reset({ accessTokenTTL: String(data.accessTokenTTL), accessTokenMaxTTL: String(data.accessTokenMaxTTL),