Lightning.Credentials (Lightning v2.19.0-pre)
View SourceThe Credentials context.
Summary
Functions
Retrieves basic auth strings for a credential in a specific environment.
Extracts basic auth strings from a credential body map.
Clears a credential's scheduled deletion, putting it back in normal use.
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.
Creates a keychain credential on behalf of an actor.
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 on behalf of an actor.
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.
Updates a keychain credential on behalf of an actor.
Validates a credential transfer request.
Types
Functions
@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 nilenvironment: Environment name (defaults to "main")
Examples
iex> basic_auth_for(credential, "staging")
["dXNlcjpwYXNz"]
Extracts basic auth strings from a credential body map.
Examples
iex> basic_auth_from_body(%{"username" => "user", "password" => "pass"})
["dXNlcjpwYXNz"]
@spec cancel_scheduled_deletion(Ecto.UUID.t(), Lightning.Accounts.User.t()) :: {:ok, Lightning.Credentials.Credential.t()} | {:error, :unauthorized | Ecto.Changeset.t()}
Clears a credential's scheduled deletion, putting it back in normal use.
Takes an id rather than a struct because the screen that calls it has only the id to hand. It loads the credential and asks the same ownership question as every other write here, so the id being caller-supplied does not matter.
Returns an %Ecto.Changeset{} for tracking credential changes.
Examples
iex> change_credential(credential)
%Ecto.Changeset{data: %Credential{}}
Returns an %Ecto.Changeset{} for tracking keychain credential changes.
Examples
iex> change_keychain_credential(keychain_credential)
%Ecto.Changeset{data: %KeychainCredential{}}
@spec confirm_transfer(String.t(), Lightning.Accounts.User.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
ownerto thereceiver. - Records the transfer in the audit log.
- Notifies both parties about the transfer.
The credential and receiver are derived from the verified token, never from caller input, so a confirmation link cannot be re-pointed at a different credential or receiver.
Parameters
token: The signed transfer token minted at initiation.confirming_user: TheUseracting on the confirmation link.
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, expired or revoked.{:error, :not_owner}if the confirming user isn't the token owner, or the token owner doesn't own the credential.{:error, :not_pending}if the transfer isn't in a pending state.{:error, changeset}if there is a validation or update issue.
Example
case confirm_transfer(token, current_user) 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
@spec create_credential(map(), Lightning.Accounts.User.t()) :: {: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
@spec create_keychain_credential( Lightning.Credentials.KeychainCredential.t(), map(), Lightning.Accounts.User.t() ) :: {:ok, Lightning.Credentials.KeychainCredential.t()} | {:error, Ecto.Changeset.t() | :unauthorized}
Creates a keychain credential on behalf of an actor.
The actor is an argument rather than something the caller is trusted to have
checked, so a screen cannot create a keychain by forgetting to ask. Returns
{:error, :unauthorized} when the actor may not create one here.
Examples
iex> create_keychain_credential(keychain, %{name: "My Keychain"}, user)
{:ok, %KeychainCredential{}}
iex> create_keychain_credential(keychain, %{name: nil}, user)
{:error, %Ecto.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<...>
@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<...>
Deletes a credential.
Examples
iex> delete_credential(credential, actor)
{:ok, %{credential: %Credential{}, audit: %Audit{}}}
iex> delete_credential(credential, actor)
{:error, :unauthorized}
@spec delete_keychain_credential( Lightning.Credentials.KeychainCredential.t(), Lightning.Accounts.User.t() ) :: {:ok, Lightning.Credentials.KeychainCredential.t()} | {:error, Ecto.Changeset.t() | :unauthorized}
Deletes a keychain credential on behalf of an actor.
Returns {:error, :unauthorized} when the actor may not delete it.
Examples
iex> delete_keychain_credential(keychain_credential, user)
{:ok, %KeychainCredential{}}
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)
@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
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)
@spec get_schema(String.t()) :: Lightning.Credentials.Schema.t()
Creates a credential schema from credential json schema.
Checks if a given Credential has any associated Step activity.
Parameters
_credential: ACredentialstruct. Only theidfield is used by the function.
Returns
trueif there's at least oneStepassociated with the givenCredential.falseotherwise.
Examples
iex> has_activity_in_projects?(%Credential{id: some_id})
true
iex> has_activity_in_projects?(%Credential{id: another_id})
falseNotes
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.
@spec initiate_credential_transfer( Lightning.Accounts.User.t(), Lightning.Accounts.User.t(), Lightning.Credentials.Credential.t() ) :: :ok | {:error, :not_owner | transfer_error() | Ecto.Changeset.t()}
Initiates a credential transfer from the owner to the receiver.
This function:
- Marks the credential as
pendingfor transfer. - Generates an email token for the credential transfer.
- Sends a transfer confirmation email to the receiver.
Parameters
owner: TheUserwho currently owns the credential.receiver: TheUserwho will receive the credential.credential: TheCredentialto be transferred.
Returns
:okif 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
@spec list_credentials(Lightning.Projects.Project.t()) :: [ Lightning.Credentials.Credential.t() ]
@spec list_credentials(Lightning.Accounts.User.t()) :: [ Lightning.Credentials.Credential.t() ]
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}]
Returns the list of keychain credentials for a project.
Examples
iex> list_keychain_credentials_for_project(%Project{id: 123})
[%KeychainCredential{}, ...]
@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: TheUserstruct whose credentials we want to find.project: TheProjectstruct to check for credential usage.
Returns
- A list of
Credentialstructs 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, ...}]
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}
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}
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, 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.
@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 structenvironment: 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
@spec revoke_transfer(String.t(), Lightning.Accounts.User.t()) :: {:ok, Lightning.Credentials.Credential.t()} | {:error, transfer_error() | Ecto.Changeset.t()}
Revokes a pending credential transfer.
This function:
- Ensures the credential exists.
- Checks that the
owneris the one who initiated the transfer. - Confirms that the credential is still in a
pendingstate. - Resets the transfer status.
Parameters
credential_id: The ID of theCredentialbeing revoked.owner: TheUserwho 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
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: ACredentialstruct that is to be scheduled for deletion.actor: TheUserasking, who must own the credential.
Returns
{:ok, credential}: Returns an:oktuple with the updated credential struct if the update was successful.{:error, changeset}: Returns an:errortuple with the changeset if the update failed.
Examples
iex> schedule_credential_deletion(%Credential{id: some_id}, actor)
{:ok, %Credential{}}
iex> schedule_credential_deletion(%Credential{}, actor)
{:error, %Ecto.Changeset{}}
@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 nilenvironment: Environment name (defaults to "main")
Examples
iex> sensitive_values_for(credential, "production")
["secret123", "api_key_xyz"]
Extracts sensitive values from a credential body map.
Examples
iex> sensitive_values_from_body(%{"password" => "secret123"})
["secret123"]
@spec update_credential( Lightning.Credentials.Credential.t(), map(), Lightning.Accounts.User.t() ) :: {:ok, Lightning.Credentials.Credential.t()} | {:error, any()}
Updates an existing credential and its credential bodies.
Parameters
credential- The credential to updateattrs- 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
@spec update_keychain_credential( Lightning.Credentials.KeychainCredential.t(), map(), Lightning.Accounts.User.t() ) :: {:ok, Lightning.Credentials.KeychainCredential.t()} | {:error, Ecto.Changeset.t() | :unauthorized}
Updates a keychain credential on behalf of an actor.
Returns {:error, :unauthorized} when the actor may not edit it.
Examples
iex> update_keychain_credential(keychain_credential, %{name: "Updated"}, user)
{:ok, %KeychainCredential{}}
@spec validate_credential_transfer( Ecto.Changeset.t(), Lightning.Accounts.User.t(), Lightning.Credentials.Credential.t() ) :: Ecto.Changeset.t()
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: TheEcto.Changesetcontaining the credential transfer details.current_user: The user attempting the credential transfer.credential: The credential being transferred.
Returns
- An updated
Ecto.Changesetwith validation errors if any issues are found.