MappingBackendUserIds

Trait MappingBackendUserIds 

Source
pub trait MappingBackendUserIds {
    // Required methods
    fn backend_user_ids(&self, filter: BackendUserIdFilter) -> Vec<String>;
    fn backend_user_with_passphrase(
        &self,
        name: &str,
        passphrase: Passphrase,
    ) -> Result<Box<dyn UserWithPassphrase>, Error>;
    fn backend_users_with_new_passphrase(
        &self,
        filter: BackendUserIdFilter,
    ) -> Vec<Box<dyn UserWithPassphrase>>;
}
Expand description

An interface for returning a list of backend users based on a filter.

§Example

use signstar_config::{
    Error,
    SystemUserId,
    config::{BackendUserIdFilter, BackendUserIdKind, MappingBackendUserIds},
};
use signstar_crypto::{passphrase::Passphrase, traits::UserWithPassphrase};

#[derive(Debug)]
struct ExampleCreds {
    pub id: u8,
    pub passphrase: Passphrase,
}

impl UserWithPassphrase for ExampleCreds {
    fn user(&self) -> String {
        self.id.to_string()
    }

    fn passphrase(&self) -> &Passphrase {
        &self.passphrase
    }
}

#[derive(Debug)]
enum ExampleUserMapping {
    Admin {
        backend_id: u8,
    },
    Backup {
        backend_id: u8,
        system_user: SystemUserId,
    },
    Metrics {
        backend_id: u8,
        system_user: SystemUserId,
    },
    Signer {
        backend_id: u8,
        system_user: SystemUserId,
    },
}

impl ExampleUserMapping {
    pub fn backend_user_id(&self) -> u8 {
        match self {
            Self::Admin { backend_id }
            | Self::Backup { backend_id, .. }
            | Self::Metrics { backend_id, .. }
            | Self::Signer { backend_id, .. } => *backend_id,
        }
    }
}

impl MappingBackendUserIds for ExampleUserMapping {
    fn backend_user_ids(&self, filter: BackendUserIdFilter) -> Vec<String> {
        match self {
            Self::Admin { backend_id, .. } => {
                if [BackendUserIdKind::Admin, BackendUserIdKind::Any]
                    .contains(&filter.backend_user_id_kind)
                {
                    return vec![backend_id.to_string()];
                }
            }
            Self::Backup { backend_id, .. } => {
                if [
                    BackendUserIdKind::Any,
                    BackendUserIdKind::Backup,
                    BackendUserIdKind::NonAdmin,
                ]
                .contains(&filter.backend_user_id_kind)
                {
                    return vec![backend_id.to_string()];
                }
            }
            Self::Metrics { backend_id, .. } => {
                if [
                    BackendUserIdKind::Any,
                    BackendUserIdKind::Metrics,
                    BackendUserIdKind::NonAdmin,
                ]
                .contains(&filter.backend_user_id_kind)
                {
                    return vec![backend_id.to_string()];
                }
            }
            Self::Signer { backend_id, .. } => {
                if [
                    BackendUserIdKind::Any,
                    BackendUserIdKind::Signing,
                    BackendUserIdKind::NonAdmin,
                ]
                .contains(&filter.backend_user_id_kind)
                {
                    return vec![backend_id.to_string()];
                }
            }
        }

        Vec::new()
    }

    fn backend_user_with_passphrase(
        &self,
        name: &str,
        passphrase: Passphrase,
    ) -> Result<Box<dyn UserWithPassphrase>, Error> {
        let backend_user_id = self.backend_user_id();
        if backend_user_id.to_string() != name {
            return Err(
                signstar_config::config::TraitsError::BackendUserIdMismatch {
                    expected: name.to_string(),
                    actual: backend_user_id.to_string(),
                }
                .into(),
            );
        }

        Ok(Box::new(ExampleCreds {
            id: backend_user_id,
            passphrase,
        }))
    }

    fn backend_users_with_new_passphrase(
        &self,
        filter: BackendUserIdFilter,
    ) -> Vec<Box<dyn UserWithPassphrase>> {
        if let Some(backend_id) = match self {
            Self::Admin { backend_id, .. } => {
                if [BackendUserIdKind::Admin, BackendUserIdKind::Any]
                    .contains(&filter.backend_user_id_kind)
                {
                    Some(*backend_id)
                } else {
                    None
                }
            }
            Self::Backup { backend_id, .. } => {
                if [
                    BackendUserIdKind::Any,
                    BackendUserIdKind::Backup,
                    BackendUserIdKind::NonAdmin,
                ]
                .contains(&filter.backend_user_id_kind)
                {
                    Some(*backend_id)
                } else {
                    None
                }
            }
            Self::Metrics { backend_id, .. } => {
                if [
                    BackendUserIdKind::Any,
                    BackendUserIdKind::Metrics,
                    BackendUserIdKind::NonAdmin,
                ]
                .contains(&filter.backend_user_id_kind)
                {
                    Some(*backend_id)
                } else {
                    None
                }
            }
            Self::Signer { backend_id, .. } => {
                if [
                    BackendUserIdKind::Any,
                    BackendUserIdKind::Signing,
                    BackendUserIdKind::NonAdmin,
                ]
                .contains(&filter.backend_user_id_kind)
                {
                    Some(*backend_id)
                } else {
                    None
                }
            }
        } {
            vec![Box::new(ExampleCreds {
                id: backend_id,
                passphrase: Passphrase::generate(None),
            })]
        } else {
            Vec::new()
        }
    }
}

let backend_id = 1;
let mapping = ExampleUserMapping::Backup {
    backend_id,
    system_user: "backup".parse()?,
};

// Find backend user IDs based on the kind of user.
assert!(
    mapping
        .backend_user_ids(BackendUserIdFilter {
            backend_user_id_kind: BackendUserIdKind::Backup
        })
        .first()
        .is_some_and(|id| *id == backend_id.to_string())
);
// This returns an empty list if there are no matches.
assert!(
    mapping
        .backend_user_ids(BackendUserIdFilter {
            backend_user_id_kind: BackendUserIdKind::Admin
        })
        .is_empty()
);

// Create credentials based on a user and a passphrase.
let passphrase = Passphrase::generate(None);
let creds = mapping.backend_user_with_passphrase("1", passphrase.clone())?;
assert_eq!(creds.user(), backend_id.to_string());
assert_eq!(
    creds.passphrase().expose_borrowed(),
    passphrase.expose_borrowed()
);
// The user ID has to match when creating credentials!
assert!(
    mapping
        .backend_user_with_passphrase("foo", passphrase.clone())
        .is_err()
);

// Create new credentials with new passphrase based on backend user ID.
assert!(
    mapping
        .backend_users_with_new_passphrase(BackendUserIdFilter {
            backend_user_id_kind: BackendUserIdKind::Backup
        })
        .first()
        .is_some_and(|creds| creds.user() == backend_id.to_string())
);

Required Methods§

Source

fn backend_user_ids(&self, filter: BackendUserIdFilter) -> Vec<String>

Returns a list of Strings representing backend User IDs according to a filter.

Source

fn backend_user_with_passphrase( &self, name: &str, passphrase: Passphrase, ) -> Result<Box<dyn UserWithPassphrase>, Error>

Returns a specific UserWithPassphrase implementation for a backend user.

§Errors

Returns an error if user matches no backend user of the user mapping. Note, that implementations may use Error::BackendUserIdMismatch for this, if they do not wish to create their own error variant for this purpose.

Source

fn backend_users_with_new_passphrase( &self, filter: BackendUserIdFilter, ) -> Vec<Box<dyn UserWithPassphrase>>

Returns a list of UserWithPassphrase implementations according to a filter.

For each returned backend user a new Passphrase is generated using the default settings of Passphrase::generate.

With an implementation of BackendUserIdFilter it is possible to target specific kinds of backend users.

Implementors§