pub use crate::internal::user_api::{
DeviceAddResult, DeviceId, DeviceName, EncryptedPrivateKey, Jwt, JwtClaims, KeyPair,
UserCreateResult, UserDevice, UserDeviceListResult, UserId, UserResult,
UserUpdatePrivateKeyResult, UserUpdateResult,
};
use crate::{
common::{PublicKey, SdkOperation},
internal::{add_optional_timeout, user_api, OUR_REQUEST},
IronOxide, Result,
};
use async_trait::async_trait;
use recrypt::api::Recrypt;
use std::{collections::HashMap, convert::TryInto};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DeviceCreateOpts {
device_name: Option<DeviceName>,
}
impl DeviceCreateOpts {
pub fn new(device_name: Option<DeviceName>) -> DeviceCreateOpts {
DeviceCreateOpts { device_name }
}
}
impl Default for DeviceCreateOpts {
fn default() -> Self {
DeviceCreateOpts::new(None)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct UserCreateOpts {
needs_rotation: bool,
}
impl UserCreateOpts {
pub fn new(needs_rotation: bool) -> UserCreateOpts {
UserCreateOpts { needs_rotation }
}
}
impl Default for UserCreateOpts {
fn default() -> Self {
UserCreateOpts::new(false)
}
}
#[async_trait]
pub trait UserOps {
async fn user_create(
jwt: &Jwt,
password: &str,
user_create_opts: &UserCreateOpts,
timeout: Option<std::time::Duration>,
) -> Result<UserCreateResult>;
async fn generate_new_device(
jwt: &Jwt,
password: &str,
device_create_options: &DeviceCreateOpts,
timeout: Option<std::time::Duration>,
) -> Result<DeviceAddResult>;
async fn user_verify(
jwt: &Jwt,
timeout: Option<std::time::Duration>,
) -> Result<Option<UserResult>>;
async fn user_list_devices(&self) -> Result<UserDeviceListResult>;
async fn user_get_public_key(&self, users: &[UserId]) -> Result<HashMap<UserId, PublicKey>>;
async fn user_rotate_private_key(&self, password: &str) -> Result<UserUpdatePrivateKeyResult>;
async fn user_delete_device(&self, device_id: Option<&DeviceId>) -> Result<DeviceId>;
async fn user_change_password(
&self,
current_password: &str,
new_password: &str,
) -> Result<UserUpdateResult>;
}
#[async_trait]
impl UserOps for IronOxide {
async fn user_create(
jwt: &Jwt,
password: &str,
user_create_opts: &UserCreateOpts,
timeout: Option<std::time::Duration>,
) -> Result<UserCreateResult> {
let recrypt = Recrypt::new();
add_optional_timeout(
user_api::user_create(
&recrypt,
jwt,
password.try_into()?,
user_create_opts.needs_rotation,
*OUR_REQUEST,
),
timeout,
SdkOperation::UserCreate,
)
.await?
}
async fn generate_new_device(
jwt: &Jwt,
password: &str,
device_create_options: &DeviceCreateOpts,
timeout: Option<std::time::Duration>,
) -> Result<DeviceAddResult> {
let recrypt = Recrypt::new();
let device_create_options = device_create_options.clone();
add_optional_timeout(
user_api::generate_device_key(
&recrypt,
jwt,
password.try_into()?,
device_create_options.device_name,
&std::time::SystemTime::now().into(),
&OUR_REQUEST,
),
timeout,
SdkOperation::GenerateNewDevice,
)
.await?
}
async fn user_verify(
jwt: &Jwt,
timeout: Option<std::time::Duration>,
) -> Result<Option<UserResult>> {
add_optional_timeout(
user_api::user_verify(jwt, *OUR_REQUEST),
timeout,
SdkOperation::UserVerify,
)
.await?
}
async fn user_list_devices(&self) -> Result<UserDeviceListResult> {
add_optional_timeout(
user_api::device_list(self.device.auth()),
self.config.sdk_operation_timeout,
SdkOperation::UserListDevices,
)
.await?
}
async fn user_get_public_key(&self, users: &[UserId]) -> Result<HashMap<UserId, PublicKey>> {
add_optional_timeout(
user_api::user_key_list(self.device.auth(), users),
self.config.sdk_operation_timeout,
SdkOperation::UserGetPublicKey,
)
.await?
}
async fn user_rotate_private_key(&self, password: &str) -> Result<UserUpdatePrivateKeyResult> {
add_optional_timeout(
user_api::user_rotate_private_key(
&self.recrypt,
password.try_into()?,
self.device().auth(),
),
self.config.sdk_operation_timeout,
SdkOperation::UserRotatePrivateKey,
)
.await?
}
async fn user_delete_device(&self, device_id: Option<&DeviceId>) -> Result<DeviceId> {
add_optional_timeout(
user_api::device_delete(self.device.auth(), device_id),
self.config.sdk_operation_timeout,
SdkOperation::UserDeleteDevice,
)
.await?
}
async fn user_change_password(
&self,
current_password: &str,
new_password: &str,
) -> Result<UserUpdateResult> {
add_optional_timeout(
user_api::user_change_password(
current_password.try_into()?,
new_password.try_into()?,
self.device.auth(),
),
self.config.sdk_operation_timeout,
SdkOperation::UserChangePassword,
)
.await?
}
}
#[cfg(test)]
mod tests {
use super::*;
use galvanic_assert::{matchers::*, *};
#[test]
fn user_create_opts_defaults() {
let opts = UserCreateOpts::default();
assert_that!(
&opts,
has_structure!(UserCreateOpts {
needs_rotation: eq(false)
})
)
}
#[test]
fn user_create_opts_new() {
let opts = UserCreateOpts::new(true);
assert_that!(
&opts,
has_structure!(UserCreateOpts {
needs_rotation: eq(true)
})
)
}
}