Keyring storage

This commit is contained in:
ChristianVisintin
2021-01-16 16:07:11 +01:00
parent eee08bd623
commit 0e4caaecfd
5 changed files with 371 additions and 23 deletions

View File

@@ -23,8 +23,9 @@
*
*/
// Local
use super::{KeyStorage, KeyStorageError};
// Ext
use std::fs::{OpenOptions, Permissions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
@@ -75,10 +76,10 @@ impl KeyStorage for FileStorage {
let mut key: String = String::new();
match file.read_to_string(&mut key) {
Ok(_) => Ok(key),
Err(_) => Err(KeyStorageError::Io),
Err(_) => Err(KeyStorageError::ProviderError),
}
}
Err(_) => Err(KeyStorageError::Io),
Err(_) => Err(KeyStorageError::ProviderError),
}
}
@@ -97,7 +98,7 @@ impl KeyStorage for FileStorage {
Ok(mut file) => {
// Write key to file
if let Err(_) = file.write_all(key.as_bytes()) {
return Err(KeyStorageError::Io);
return Err(KeyStorageError::ProviderError);
}
// Set file to readonly
let mut permissions: Permissions = file.metadata().unwrap().permissions();
@@ -105,7 +106,7 @@ impl KeyStorage for FileStorage {
let _ = file.set_permissions(permissions);
Ok(())
}
Err(_) => Err(KeyStorageError::Io),
Err(_) => Err(KeyStorageError::ProviderError),
}
}

View File

@@ -0,0 +1,135 @@
//! ## KeyringStorage
//!
//! `keyringstorage` provides an implementation of the `KeyStorage` trait using the OS keyring
/*
*
* Copyright (C) 2020-2021 Christian Visintin - christian.visintin1997@gmail.com
*
* This file is part of "TermSCP"
*
* TermSCP is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TermSCP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TermSCP. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Deps
extern crate keyring;
// Local
use super::{KeyStorage, KeyStorageError};
// Ext
use keyring::{Keyring, KeyringError};
/// ## KeyringStorage
///
/// provides a `KeyStorage` implementation using the keyring crate
pub struct KeyringStorage {
username: String,
}
impl KeyringStorage {
/// ### new
///
/// Instantiates a new KeyringStorage
pub fn new(username: &str) -> Self {
KeyringStorage {
username: username.to_string(),
}
}
}
impl KeyStorage for KeyringStorage {
/// ### get_key
///
/// Retrieve key from the key storage.
/// The key might be acccess through an identifier, which identifies
/// the key in the storage
fn get_key(&self, storage_id: &str) -> Result<String, KeyStorageError> {
let storage: Keyring = Keyring::new(storage_id, self.username.as_str());
match storage.get_password() {
Ok(s) => Ok(s),
Err(e) => match e {
KeyringError::NoPasswordFound => Err(KeyStorageError::NoSuchKey),
#[cfg(target_os = "windows")]
KeyringError::WindowsVaultError => Err(KeyStorageError::NoSuchKey),
#[cfg(target_os = "macos")]
KeyringError::MacOsKeychainError(_) => Err(KeyStorageError::NoSuchKey),
_ => panic!("{}", e),
},
}
}
/// ### set_key
///
/// Set the key into the key storage
fn set_key(&self, storage_id: &str, key: &str) -> Result<(), KeyStorageError> {
let storage: Keyring = Keyring::new(storage_id, self.username.as_str());
match storage.set_password(key) {
Ok(_) => Ok(()),
Err(_) => Err(KeyStorageError::ProviderError),
}
}
/// is_supported
///
/// Returns whether the key storage is supported on the host system
fn is_supported(&self) -> bool {
let dummy: String = String::from("dummy-service");
let storage: Keyring = Keyring::new(dummy.as_str(), self.username.as_str());
// Check what kind of error is returned
match storage.get_password() {
Ok(_) => true,
Err(err) => match err {
KeyringError::NoBackendFound => false,
//#[cfg(target_os = "macos")]
//KeyringError::MacOsKeychainError(_) => false,
//#[cfg(target_os = "windows")]
//KeyringError::WindowsVaultError => false,
_ => true,
},
}
}
}
#[cfg(test)]
mod tests {
extern crate whoami;
use super::*;
use whoami::username;
#[test]
fn test_system_keys_keyringstorage() {
let username: String = username();
let storage: KeyringStorage = KeyringStorage::new(username.as_str());
let app_name: &str = "termscp";
let secret: &str = "Th15-15/My-Супер-Секрет";
let kring: Keyring = Keyring::new(app_name, username.as_str());
let _ = kring.delete_password();
drop(kring);
// Secret should not exist
assert_eq!(
storage.get_key(app_name).err().unwrap(),
KeyStorageError::NoSuchKey
);
// Write secret
assert!(storage.set_key(app_name, secret).is_ok());
// Get secret
assert_eq!(storage.get_key(app_name).ok().unwrap().as_str(), secret);
// Delete the key manually...
let kring: Keyring = Keyring::new(app_name, username.as_str());
assert!(kring.delete_password().is_ok());
}
}

View File

@@ -25,22 +25,24 @@
// Storages
pub mod filestorage;
#[cfg(any(target_os = "windows", target_os = "macos"))]
pub mod keyringstorage;
/// ## KeyStorageError
///
///
/// defines the error type for the `KeyStorage`
#[derive(PartialEq, std::fmt::Debug)]
pub enum KeyStorageError {
BadKey,
Io,
//BadKey,
ProviderError,
NoSuchKey,
}
impl std::fmt::Display for KeyStorageError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let err: String = String::from(match &self {
KeyStorageError::BadKey => "Bad key syntax",
KeyStorageError::Io => "Input/Output error",
//KeyStorageError::BadKey => "Bad key syntax",
KeyStorageError::ProviderError => "Provider service error",
KeyStorageError::NoSuchKey => "No such key",
});
write!(f, "{}", err)
@@ -48,25 +50,41 @@ impl std::fmt::Display for KeyStorageError {
}
/// ## KeyStorage
///
///
/// this traits provides the methods to communicate and interact with the key storage.
pub trait KeyStorage {
/// ### get_key
///
///
/// Retrieve key from the key storage.
/// The key might be acccess through an identifier, which identifies
/// the key in the storage
fn get_key(&self, storage_id: &str) -> Result<String, KeyStorageError>;
/// ### set_key
///
///
/// Set the key into the key storage
fn set_key(&self, storage_id: &str, key: &str) -> Result<(), KeyStorageError>;
/// is_supported
///
///
/// Returns whether the key storage is supported on the host system
fn is_supported(&self) -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_system_keys_mod_errors() {
assert_eq!(
format!("{}", KeyStorageError::ProviderError),
String::from("Provider service error")
);
assert_eq!(
format!("{}", KeyStorageError::NoSuchKey),
String::from("No such key")
);
}
}