Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ repository = "https://github.com/pando85/passless"
passless-core = { path = "./passless-core", version = "0.6.3" }
passless-config-doc = { path = "./passless-config-doc", version = "0.6.3" }

soft-fido2 = "0.7.0"
soft-fido2-ctap = "0.7.0"
soft-fido2-transport = "0.7.0"
soft-fido2 = "0.8.0"
soft-fido2-ctap = "0.8.0"
soft-fido2-transport = "0.8.0"

thiserror = "2.0"
clap = { version = "4.5", features = ["std", "color", "derive", "cargo", "env"] }
Expand Down
28 changes: 11 additions & 17 deletions cmd/passless/src/authenticator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,29 +104,23 @@ impl<S: CredentialStorage> AuthenticatorCallbacks for PasslessCallbacks<S> {
Ok(UvResult::Accepted)
}

fn write_credential(
&self,
cred_id: &[u8],
rp_id: &str,
credential: &CredentialRef,
) -> Result<()> {
info!("Storing credential for RP: {}", rp_id);
debug!("Credential ID: {}", bytes_to_hex(cred_id));
fn write_credential(&self, credential: &CredentialRef) -> Result<()> {
info!("Storing credential for RP: {}", credential.rp_id);
debug!("Credential ID: {}", bytes_to_hex(credential.id));
let mut storage = self.storage.lock().unwrap();
storage.write(cred_id, rp_id, *credential)?;
info!("Credential persisted successfully for RP: {}", rp_id);
storage.write(*credential)?;
info!(
"Credential persisted successfully for RP: {}",
credential.rp_id
);
Ok(())
}

fn read_credential(&self, cred_id: &[u8], rp_id: &str) -> Result<Option<Credential>> {
debug!(
"Reading credential: rp={}, id={}",
rp_id,
bytes_to_hex(cred_id)
);
fn read_credential(&self, cred_id: &[u8]) -> Result<Option<Credential>> {
debug!("Reading credential: id={}", bytes_to_hex(cred_id));
let mut storage = self.storage.lock().unwrap();

match storage.read(cred_id, rp_id) {
match storage.read(cred_id) {
Ok(cred) => {
debug!("Credential found");
Ok(Some(cred))
Expand Down
84 changes: 2 additions & 82 deletions cmd/passless/src/storage/local/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl CredentialStorage for LocalStorageAdapter {
self.find_next()
}

fn read(&mut self, id: &[u8], _rp: &str) -> Result<soft_fido2::Credential> {
fn read(&mut self, id: &[u8]) -> Result<soft_fido2::Credential> {
debug!("read called for credential ID");

// Use index for direct path lookup
Expand All @@ -203,7 +203,7 @@ impl CredentialStorage for LocalStorageAdapter {
self.load_credential_from_path(&path)
}

fn write(&mut self, _id: &[u8], _rp: &str, cred_ref: soft_fido2::CredentialRef) -> Result<()> {
fn write(&mut self, cred_ref: soft_fido2::CredentialRef) -> Result<()> {
// Just save the credential as provided by soft-fido2
// This is called both during registration (new credential) and
// during authentication (updating sign counter)
Expand Down Expand Up @@ -234,89 +234,9 @@ impl CredentialStorage for LocalStorageAdapter {
Ok(())
}

fn update_user_info(
&mut self,
id: &[u8],
user_name: Option<&str>,
display_name: Option<&str>,
) -> Result<()> {
debug!("update_user_info called for credential ID");

// Use index for direct path lookup
let path_info = self
.indexes
.id
.get(id)
.ok_or(soft_fido2::Error::DoesNotExist)?;

let path = path_info.to_path(&self.storage_dir);

// Load the credential
let mut cred = self.load_credential_from_path(&path)?;

// Update user information
if let Some(name) = user_name {
cred.user.name = Some(name.to_string());
}
if let Some(display) = display_name {
cred.user.display_name = Some(display.to_string());
}

// Save the updated credential
self.save_credential(&cred)?;

debug!("Updated user info for credential on RP: {}", cred.rp.id);
Ok(())
}

fn select_users(&self, rp_id: &str) -> Vec<String> {
debug!("select_users called for RP: {}", rp_id);

// Use index to get credential IDs for this RP
let cred_ids = match self.indexes.rp.get(rp_id) {
Some(ids) => ids,
None => {
debug!("No credentials found for RP: {}", rp_id);
return Vec::new();
}
};

// Load only credentials for this RP
let users: Vec<String> = cred_ids
.iter()
.filter_map(|cred_id| {
let path_info = self.indexes.id.get(cred_id)?;
let path = path_info.to_path(&self.storage_dir);
let cred = self.load_credential_from_path(&path).ok()?;
Some(String::from_utf8_lossy(&cred.user.id).to_string())
})
.collect();

debug!("Found {} users for RP: {}", users.len(), rp_id);
users
}

fn count_credentials(&self) -> usize {
let count = self.indexes.id.len();
debug!("count_credentials: {}", count);
count
}

fn get_relying_parties(&self) -> Result<Vec<soft_fido2_ctap::types::RelyingParty>> {
debug!("get_relying_parties called");

// Extract RP info directly from path structure - no file loading needed!
let rp_list: Vec<soft_fido2_ctap::types::RelyingParty> = self
.indexes
.rp
.keys()
.map(|rp_id| soft_fido2_ctap::types::RelyingParty {
id: rp_id.clone(),
name: None, // Name not available in path structure
})
.collect();

debug!("Found {} relying parties", rp_list.len());
Ok(rp_list)
}
}
86 changes: 2 additions & 84 deletions cmd/passless/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,105 +37,23 @@ pub enum CredentialFilter {
/// Any storage backend must implement this trait.
pub trait CredentialStorage: Send + Sync {
/// Start a new iteration and return the first matching credential
///
/// # Arguments
///
/// * `filter` - Filter criteria to apply
///
/// # Returns
///
/// The first matching credential or an error if none found
fn read_first(&mut self, filter: CredentialFilter) -> Result<soft_fido2::Credential>;

/// Continue the current iteration and return the next credential
///
/// # Returns
///
/// The next matching credential or an error if no more credentials
fn read_next(&mut self) -> Result<soft_fido2::Credential>;

/// Read a specific credential by ID and RP
///
/// # Arguments
///
/// * `id` - The credential ID
/// * `rp` - The relying party ID
///
/// # Returns
///
/// The credential or an error
fn read(&mut self, id: &[u8], rp: &str) -> Result<soft_fido2::Credential>;
fn read(&mut self, id: &[u8]) -> Result<soft_fido2::Credential>;

/// Store a new credential
///
/// # Arguments
///
/// * `id` - The credential ID
/// * `rp` - The relying party ID
/// * `cred` - The credential reference to store
///
/// # Returns
///
/// Ok(()) on success or an error
fn write(&mut self, id: &[u8], rp: &str, cred: soft_fido2::CredentialRef) -> Result<()>;
fn write(&mut self, cred: soft_fido2::CredentialRef) -> Result<()>;

/// Delete a credential by ID
///
/// # Arguments
///
/// * `id` - The credential ID to delete
///
/// # Returns
///
/// Ok(()) on success or an error
fn delete(&mut self, id: &[u8]) -> Result<()>;

/// Update user information for a credential
///
/// # Arguments
///
/// * `id` - The credential ID to update
/// * `user_name` - Optional new user name (login identifier)
/// * `display_name` - Optional new display name (friendly name)
///
/// # Returns
///
/// Ok(()) on success or an error
#[allow(dead_code)]
fn update_user_info(
&mut self,
id: &[u8],
user_name: Option<&str>,
display_name: Option<&str>,
) -> Result<()>;

/// Get all user names for a given relying party
///
/// # Arguments
///
/// * `rp_id` - The relying party ID
///
/// # Returns
///
/// Vector of user names
#[allow(dead_code)]
fn select_users(&self, rp_id: &str) -> Vec<String>;

/// Count total number of stored credentials
///
/// # Returns
///
/// The number of credentials in storage
fn count_credentials(&self) -> usize;

/// Get all relying parties that have credentials
///
/// # Returns
///
/// # Vector of relying party IDs
#[allow(dead_code)]
fn get_relying_parties(&self) -> Result<Vec<soft_fido2_ctap::types::RelyingParty>>;

/// Check if user verification should be disabled for this backend
///
/// Some backends (like pass) don't support user verification.
Expand Down
Loading
Loading