Lightning.Credentials (Lightning v2.14.13-pre1)

View Source

The Credentials context.

Summary

Functions

Retrieves basic auth strings for a credential in a specific environment.

Extracts basic auth strings from a credential body map.

Returns an %Ecto.Changeset{} for tracking credential changes.

Returns an %Ecto.Changeset{} for tracking keychain credential changes.

Confirms and executes a credential transfer.

Creates a new credential with its credential bodies.

Returns an empty changeset structure for credential transfers with an email field.

Creates a changeset for transferring credentials, validating the provided email.

Deletes a credential.

Deletes a keychain credential.

Gets a single credential.

Gets a credential body for a specific environment.

Gets a single keychain credential.

Creates a credential schema from credential json schema.

Checks if a given Credential has any associated Step activity.

Initiates a credential transfer from the owner to the receiver.

Retrieves all credentials based on the given context, either a Project or a User.

Returns the list of keychain credentials for a project.

Returns all credentials owned by a specific user that are also being used in a specific project.

Creates a new keychain credential struct with proper associations.

Normalizes OAuth token expiry time to always use expires_at.

Checks if an OAuth token body is expired or expires soon.

Perform, when called with %{"type" => "purge_deleted"} will find credentials that are ready for permanent deletion, set their bodies to null, and try to purge them.

Gets a credential body for an environment and refreshes OAuth tokens if expired.

Revokes a pending credential transfer.

Schedules a given credential for deletion.

Retrieves sensitive values for a credential in a specific environment.

Extracts sensitive values from a credential body map.

Updates an existing credential and its credential bodies.

Validates a credential transfer request.

Types

oauth_refresh_error()

@type oauth_refresh_error() :: :temporary_failure | :reauthorization_required

transfer_error()

@type transfer_error() :: :token_error | :not_found | :not_owner

Functions

basic_auth_for(credential, environment \\ "main")

@spec basic_auth_for(Lightning.Credentials.Credential.t() | nil, String.t()) :: [
  String.t()
]

Retrieves basic auth strings for a credential in a specific environment.

Used primarily for scrubbing historical dataclips where we need to look up the credential body by environment.

Parameters

  • credential: Credential struct or nil
  • environment: Environment name (defaults to "main")

Examples

iex> basic_auth_for(credential, "staging")
["dXNlcjpwYXNz"]

basic_auth_from_body(body)

@spec basic_auth_from_body(map() | nil) :: [String.t()]

Extracts basic auth strings from a credential body map.

Examples

iex> basic_auth_from_body(%{"username" => "user", "password" => "pass"})
["dXNlcjpwYXNz"]

cancel_scheduled_deletion(credential_id)

change_credential(credential, attrs \\ %{})

Returns an %Ecto.Changeset{} for tracking credential changes.

Examples

iex> change_credential(credential)
%Ecto.Changeset{data: %Credential{}}

change_keychain_credential(keychain_credential, attrs \\ %{})

Returns an %Ecto.Changeset{} for tracking keychain credential changes.

Examples

iex> change_keychain_credential(keychain_credential)
%Ecto.Changeset{data: %KeychainCredential{}}

confirm_transfer(credential_id, receiver_id, owner_id, token)

@spec confirm_transfer(String.t(), String.t(), String.t(), String.t()) ::
  {:ok, Lightning.Credentials.Credential.t()}
  | {:error, transfer_error() | Ecto.Changeset.t()}

Confirms and executes a credential transfer.

This function:

  • Verifies the transfer token to ensure the request is valid.
  • Transfers the credential from the owner to the receiver.
  • Records the transfer in the audit log.
  • Deletes all related credential transfer tokens.
  • Notifies both parties about the transfer.

Parameters

  • credential_id: The ID of the Credential being transferred.
  • receiver_id: The ID of the User receiving the credential.
  • owner_id: The ID of the User currently owning the credential.
  • token: The transfer token for verification.

Returns

  • {:ok, credential} on successful transfer.
  • {:error, reason} if the transfer fails.

Errors

  • {:error, :not_found} if the credential or receiver does not exist.
  • {:error, :token_error} if the token is invalid.
  • {:error, :not_owner} if the token does not match the credential owner.
  • {:error, changeset} if there is a validation or update issue.

Example

case confirm_transfer(credential_id, receiver_id, owner_id, token) do
  {:ok, credential} -> IO.puts("Transfer successful")
  {:error, :not_found} -> IO.puts("Error: Credential or receiver not found")
  {:error, :token_error} -> IO.puts("Error: Invalid transfer token")
  {:error, reason} -> IO.inspect(reason, label: "Transfer failed")
end

create_credential(attrs \\ %{})

@spec create_credential(map()) ::
  {:ok, Lightning.Credentials.Credential.t()} | {:error, any()}

Creates a new credential with its credential bodies.

Parameters

  • attrs - Map of attributes for the credential including:
    • user_id - User ID (required)
    • name - Credential name (required)
    • schema - Schema type (e.g., "oauth", "raw", etc.)
    • oauth_client_id - OAuth client ID (for OAuth credentials)
    • credential_bodies - List of credential body maps, each containing:
      • name - Environment name (e.g., "production", "staging")
      • body - Credential configuration data
    • expected_scopes - List of expected scopes (for OAuth credentials)

Returns

  • {:ok, credential} - Successfully created credential
  • {:error, error} - Error with creation process

create_keychain_credential(keychain_credential, attrs \\ %{})

Creates a keychain credential.

Examples

iex> create_keychain_credential(%{name: "My Keychain", path: "$.user_id"})
{:ok, %KeychainCredential{}}

iex> create_keychain_credential(%{name: nil})
{:error, %Ecto.Changeset{}}

credential_transfer_changeset()

@spec credential_transfer_changeset() :: Ecto.Changeset.t()

Returns an empty changeset structure for credential transfers with an email field.

Returns

An Ecto.Changeset struct with an empty data map and an email field.

Example

iex> credential_transfer_changeset()
#Ecto.Changeset<...>

credential_transfer_changeset(email)

@spec credential_transfer_changeset(String.t()) :: Ecto.Changeset.t()

Creates a changeset for transferring credentials, validating the provided email.

Parameters

  • email: The email address to be included in the changeset.

Returns

An Ecto.Changeset containing the email field.

Example

iex> credential_transfer_changeset("user@example.com")
#Ecto.Changeset<...>

delete_credential(credential)

Deletes a credential.

Examples

iex> delete_credential(credential)
{:ok, %Credential{}}

iex> delete_credential(credential)
{:error, %Ecto.Changeset{}}

delete_keychain_credential(keychain_credential)

Deletes a keychain credential.

Examples

iex> delete_keychain_credential(keychain_credential)
{:ok, %KeychainCredential{}}

iex> delete_keychain_credential(keychain_credential)
{:error, %Ecto.Changeset{}}

get_credential(id)

get_credential!(id)

Gets a single credential.

Raises Ecto.NoResultsError if the Credential does not exist.

Examples

iex> get_credential!(123)
%Credential{}

iex> get_credential!(456)
** (Ecto.NoResultsError)

get_credential_body(credential_id, env_name)

@spec get_credential_body(String.t(), String.t()) ::
  Lightning.Credentials.CredentialBody.t() | nil

Gets a credential body for a specific environment.

Returns nil if no body exists for the given credential and environment combination.

Examples

iex> get_credential_body(credential_id, "production")
%CredentialBody{name: "production", body: %{...}}

iex> get_credential_body(credential_id, "nonexistent")
nil

get_credential_by_project_credential(project_credential_id)

get_keychain_credential(id)

Gets a single keychain credential.

Raises Ecto.NoResultsError if the KeychainCredential does not exist.

Examples

iex> get_keychain_credential(123)
%KeychainCredential{}

iex> get_keychain_credential(456)
** (Ecto.NoResultsError)

get_schema(schema_name)

@spec get_schema(String.t()) :: Lightning.Credentials.Schema.t()

Creates a credential schema from credential json schema.

has_activity_in_projects?(credential)

Checks if a given Credential has any associated Step activity.

Parameters

  • _credential: A Credential struct. Only the id field is used by the function.

Returns

  • true if there's at least one Step associated with the given Credential.
  • false otherwise.

Examples

iex> has_activity_in_projects?(%Credential{id: some_id})
true

iex> has_activity_in_projects?(%Credential{id: another_id})
false

Notes

This function leverages the association between Step and Credential to determine if any steps exist for a given credential. It's a fast check that does not load any records into memory, but simply checks for their existence.

initiate_credential_transfer(owner, receiver, credential)

@spec initiate_credential_transfer(
  Lightning.Accounts.User.t(),
  Lightning.Accounts.User.t(),
  Lightning.Credentials.Credential.t()
) :: :ok | {:error, transfer_error() | Ecto.Changeset.t()}

Initiates a credential transfer from the owner to the receiver.

This function:

  • Marks the credential as pending for transfer.
  • Generates an email token for the credential transfer.
  • Sends a transfer confirmation email to the receiver.

Parameters

  • owner: The User who currently owns the credential.
  • receiver: The User who will receive the credential.
  • credential: The Credential to be transferred.

Returns

  • :ok if the transfer process is successfully initiated.
  • {:error, reason} if any validation or transaction step fails.

Example

case initiate_credential_transfer(owner, receiver, credential) do
  :ok -> IO.puts("Transfer initiated successfully")
  {:error, error} -> IO.inspect(error, label: "Transfer failed")
end

list_credentials(project)

Retrieves all credentials based on the given context, either a Project or a User.

Parameters

  • context: The Project or User struct to retrieve credentials for.

Returns

  • A list of credentials associated with the given Project or created by the given User.

Examples

When given a Project:

iex> list_credentials(%Project{id: 1})
[%Credential{project_id: 1}, %Credential{project_id: 1}]

When given a User:

iex> list_credentials(%User{id: 123})
[%Credential{user_id: 123}, %Credential{user_id: 123}]

list_keychain_credentials_for_project(project)

Returns the list of keychain credentials for a project.

Examples

iex> list_keychain_credentials_for_project(%Project{id: 123})
[%KeychainCredential{}, ...]

list_user_credentials_in_project(user, project)

@spec list_user_credentials_in_project(
  Lightning.Accounts.User.t(),
  Lightning.Projects.Project.t()
) :: [
  Lightning.Credentials.Credential.t()
]

Returns all credentials owned by a specific user that are also being used in a specific project.

Parameters

  • user: The User struct whose credentials we want to find.
  • project: The Project struct to check for credential usage.

Returns

  • A list of Credential structs that are owned by the user and used in the project.

Examples

iex> list_user_credentials_in_project(%User{id: 123}, %Project{id: 456})
[%Credential{user_id: 123, ...}, %Credential{user_id: 123, ...}]

new_keychain_credential(user, project)

Creates a new keychain credential struct with proper associations.

This function ensures that the created_by and project associations are properly set and cannot be tampered with via browser params.

Examples

iex> new_keychain_credential(user, project)
%KeychainCredential{created_by: user, project: project}

normalize_token_expiry(token)

@spec normalize_token_expiry(map()) :: map()

Normalizes OAuth token expiry time to always use expires_at.

Converts relative expires_in to absolute expires_at timestamp based on current time. If the token already has expires_at, returns it unchanged.

Examples

iex> normalize_token_expiry(%{"expires_in" => 3600})
%{"expires_in" => 3600, "expires_at" => 1234567890}

iex> normalize_token_expiry(%{"expires_at" => 1234567890})
%{"expires_at" => 1234567890}

oauth_token_expired?(body)

@spec oauth_token_expired?(map()) :: boolean()

Checks if an OAuth token body is expired or expires soon.

Returns true if the token expires within the next 5 minutes (300 seconds buffer). If expiry cannot be determined, conservatively returns true to trigger refresh.

Examples

iex> oauth_token_expired?(%{"expires_at" => future_timestamp})
false

iex> oauth_token_expired?(%{"expires_at" => soon_timestamp})
true

perform(job)

Perform, when called with %{"type" => "purge_deleted"} will find credentials that are ready for permanent deletion, set their bodies to null, and try to purge them.

resolve_credential_body(credential, environment)

@spec resolve_credential_body(Lightning.Credentials.Credential.t(), String.t()) ::
  {:ok, map()} | {:error, :environment_not_found | oauth_refresh_error()}

Gets a credential body for an environment and refreshes OAuth tokens if expired.

This is the primary function for credential resolution during workflow execution. It handles the full flow: fetch body → check expiration → refresh if needed → return final body.

Parameters

  • credential: The credential struct
  • environment: Environment name (e.g., "production", "staging")

Returns

  • {:ok, body} - The credential body (with fresh tokens if OAuth was refreshed)
  • {:error, :environment_not_found} - No body exists for this environment
  • {:error, oauth_refresh_error()} - OAuth refresh failed

revoke_transfer(credential_id, owner)

Revokes a pending credential transfer.

This function:

  • Ensures the credential exists.
  • Checks that the owner is the one who initiated the transfer.
  • Confirms that the credential is still in a pending state.
  • Resets the transfer status and deletes related credential transfer tokens.

Parameters

  • credential_id: The ID of the Credential being revoked.
  • owner: The User who owns the credential and is revoking the transfer.

Returns

  • {:ok, credential} if the transfer is successfully revoked.
  • {:error, :not_found} if the credential does not exist.
  • {:error, :not_owner} if the user does not own the credential.
  • {:error, :not_pending} if the transfer is not in a pending state.
  • {:error, changeset} if there is a validation or update issue.

Example

case revoke_transfer(credential_id, owner) do
  {:ok, credential} -> IO.puts("Transfer revoked for credential")
  {:error, :not_found} -> IO.puts("Error: Credential not found")
  {:error, :not_owner} -> IO.puts("Error: You do not own this credential")
  {:error, :not_pending} -> IO.puts("Error: Transfer is not pending")
  {:error, reason} -> IO.inspect(reason, label: "Revoke failed")
end

schedule_credential_deletion(credential)

Schedules a given credential for deletion.

The deletion date is determined based on the :purge_deleted_after_days configuration in the application environment. If this configuration is absent, the credential is scheduled for immediate deletion.

The function will also perform necessary side effects such as:

  • Removing associations of the credential.
  • Revoking OAuth tokens if the credential is an OAuth credential.
  • Notifying the owner of the credential about the scheduled deletion.

Parameters

  • credential: A Credential struct that is to be scheduled for deletion.

Returns

  • {:ok, credential}: Returns an :ok tuple with the updated credential struct if the update was successful.
  • {:error, changeset}: Returns an :error tuple with the changeset if the update failed.

Examples

iex> schedule_credential_deletion(%Credential{id: some_id})
{:ok, %Credential{}}

iex> schedule_credential_deletion(%Credential{})
{:error, %Ecto.Changeset{}}

sensitive_values_for(id_or_credential, environment \\ "main")

@spec sensitive_values_for(
  Ecto.UUID.t() | Lightning.Credentials.Credential.t() | nil,
  String.t()
) :: [
  any()
]

Retrieves sensitive values for a credential in a specific environment.

Used primarily for scrubbing historical dataclips where we need to look up the credential body by environment.

Parameters

  • id_or_credential: Credential ID, Credential struct, or nil
  • environment: Environment name (defaults to "main")

Examples

iex> sensitive_values_for(credential, "production")
["secret123", "api_key_xyz"]

sensitive_values_from_body(body)

@spec sensitive_values_from_body(map() | nil) :: [any()]

Extracts sensitive values from a credential body map.

Examples

iex> sensitive_values_from_body(%{"password" => "secret123"})
["secret123"]

update_credential(credential, attrs)

@spec update_credential(Lightning.Credentials.Credential.t(), map()) ::
  {:ok, Lightning.Credentials.Credential.t()} | {:error, any()}

Updates an existing credential and its credential bodies.

Parameters

  • credential - The credential to update
  • attrs - Map of attributes to update, including:
    • credential_bodies - List of credential body maps to create/update

Returns

  • {:ok, credential} - Successfully updated credential
  • {:error, error} - Error with update process

update_keychain_credential(keychain_credential, attrs)

Updates a keychain credential.

Examples

iex> update_keychain_credential(keychain_credential, %{name: "Updated"})
{:ok, %KeychainCredential{}}

iex> update_keychain_credential(keychain_credential, %{name: nil})
{:error, %Ecto.Changeset{}}

validate_credential_transfer(changeset, sender, credential)

Validates a credential transfer request.

This function ensures:

  • The email format is correct.
  • The email does not already exist in the system.
  • The credential is not transferred to the same user.
  • The recipient has access to the necessary projects.

If the changeset is valid, additional validation checks are applied.

Parameters

  • changeset: The Ecto.Changeset containing the credential transfer details.
  • current_user: The user attempting the credential transfer.
  • credential: The credential being transferred.

Returns

  • An updated Ecto.Changeset with validation errors if any issues are found.