Skip to content

Commit

Permalink
Link secondary devices as primary device
Browse files Browse the repository at this point in the history
  • Loading branch information
Schmiddiii committed Oct 11, 2023
1 parent 6125534 commit 7fb2d40
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 8 deletions.
4 changes: 3 additions & 1 deletion presage-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,9 @@ async fn run<C: Store + 'static>(subcommand: Cmd, config_store: C) -> anyhow::Re
async move {
match provisioning_link_rx.await {
Ok(url) => {
qr2term::print_qr(url.to_string()).expect("failed to render qrcode")
println!("Please scan in the QR code:");
qr2term::print_qr(url.to_string()).expect("failed to render qrcode");
println!("Alternatively, use the URL: {}", url);
}
Err(e) => log::error!("Error linking device: {e}"),
}
Expand Down
11 changes: 8 additions & 3 deletions presage-store-sled/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,12 @@ impl SledStore {

#[cfg(feature = "encryption")]
let cipher = passphrase
.as_ref()
.map(|p| Self::get_or_create_store_cipher(&database, p.as_ref()))
.transpose()?;

dbg!(cipher.is_some());

#[cfg(not(feature = "encryption"))]
if passphrase.is_some() {
panic!("A passphrase was supplied but the encryption feature flag is not enabled")
Expand Down Expand Up @@ -299,7 +302,7 @@ fn migrate(

let run_migrations = move || {
let mut store = SledStore::new(db_path, passphrase)?;
let schema_version = store.schema_version();
let schema_version = dbg!(store.schema_version());
for step in schema_version.steps() {
match &step {
SchemaVersion::V1 => {
Expand Down Expand Up @@ -374,11 +377,11 @@ impl Store for SledStore {
/// State

fn load_state(&self) -> Result<Option<Registered>, SledStoreError> {
self.get(SLED_TREE_STATE, SLED_KEY_REGISTRATION)
dbg!(self.get(SLED_TREE_STATE, SLED_KEY_REGISTRATION))
}

fn save_state(&mut self, state: &Registered) -> Result<(), SledStoreError> {
self.insert(SLED_TREE_STATE, SLED_KEY_REGISTRATION, state)?;
self.insert(SLED_TREE_STATE, SLED_KEY_REGISTRATION, dbg!(state))?;
Ok(())
}

Expand Down Expand Up @@ -656,8 +659,10 @@ impl SledContactsIter {
#[cfg(feature = "encryption")]
fn decrypt_value<T: DeserializeOwned>(&self, value: &[u8]) -> Result<T, SledStoreError> {
if let Some(cipher) = self.cipher.as_ref() {
log::trace!("Decrypt with cipher");
Ok(cipher.decrypt_value(value)?)
} else {
log::trace!("Decrypt no cipher");
Ok(serde_json::from_slice(value)?)
}
}
Expand Down
4 changes: 2 additions & 2 deletions presage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ authors = ["Gabriel Féron <[email protected]>"]
edition = "2021"

[dependencies]
libsignal-service = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "454d234" }
libsignal-service-hyper = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "454d234" }
libsignal-service = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "aa84243" }
libsignal-service-hyper = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "aa84243" }

base64 = "0.12"
futures = "0.3"
Expand Down
4 changes: 4 additions & 0 deletions presage/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ pub enum Error<S: std::error::Error> {
RequestingCodeForbidden(libsignal_service::push_service::RegistrationSessionMetadataResponse),
#[error("Unverified registration session (i.e. wrong verification code)")]
UnverifiedRegistrationSession,
#[error("Failed to link secondary device")]
ServiceLinkError(#[from] libsignal_service::LinkError),
#[error("An operation was requested that requires the registration to be primary, but it was only secondary")]
NotPrimaryDevice,
}

impl<S: StoreError> From<S> for Error<S> {
Expand Down
4 changes: 3 additions & 1 deletion presage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ mod serde;
mod store;

pub use errors::Error;
pub use manager::{Confirmation, Linking, Manager, Registered, Registration, RegistrationOptions};
pub use manager::{
Confirmation, Linking, Manager, Registered, Registration, RegistrationOptions, RegistrationType,
};
pub use store::{ContentTimestamp, Store, StoreError, Thread};

#[deprecated(note = "Please help use improve the prelude module instead")]
Expand Down
55 changes: 54 additions & 1 deletion presage/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize};
use url::Url;

use libsignal_service::proto::EditMessage;
use libsignal_service::push_service::{RegistrationMethod, VerificationTransport};
use libsignal_service::push_service::{DeviceInfo, RegistrationMethod, VerificationTransport};
use libsignal_service::{
attachment_cipher::decrypt_in_place,
cipher,
Expand Down Expand Up @@ -74,6 +74,12 @@ impl<Store, State: fmt::Debug> fmt::Debug for Manager<Store, State> {
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RegistrationType {
Primary,
Secondary,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct RegistrationOptions<'a> {
pub signal_servers: SignalServers,
Expand Down Expand Up @@ -1287,6 +1293,53 @@ impl<C: Store> Manager<C, Registered> {
}
}

/// Returns how this client was registered, either as a primary or secondary device.
pub fn registration_type(&self) -> RegistrationType {
if self.state.device_name.is_some() {
RegistrationType::Secondary
} else {
RegistrationType::Primary
}
}

/// As a primary device, link a secondary device.
pub async fn link_secondary(&self, secondary: Url) -> Result<(), Error<C::Error>> {
// XXX: What happens if secondary device? Possible to use static typing to make this method call impossible in that case?
if self.registration_type() != RegistrationType::Primary {
return Err(Error::NotPrimaryDevice);
}

let credentials = self.credentials()?.ok_or(Error::NotYetRegisteredError)?;
let mut account_manager =
AccountManager::new(self.push_service()?, Some(self.state.profile_key));
let store = &self.config_store;

account_manager
.link_device(secondary, store, credentials)
.await?;
Ok(())
}

/// As a primary device, unlink a secondary device.
pub async fn unlink_secondary(&self, device_id: i64) -> Result<(), Error<C::Error>> {
// XXX: What happens if secondary device? Possible to use static typing to make this method call impossible in that case?
if self.registration_type() != RegistrationType::Primary {
return Err(Error::NotPrimaryDevice);
}
self.push_service()?.unlink_device(device_id).await?;
Ok(())
}

/// As a primary device, list all the devices.
// XXX: Also shows the current device?
pub async fn linked_devices(&self) -> Result<Vec<DeviceInfo>, Error<C::Error>> {
// XXX: What happens if secondary device? Possible to use static typing to make this method call impossible in that case?
if self.registration_type() != RegistrationType::Primary {
return Err(Error::NotPrimaryDevice);
}
Ok(self.push_service()?.devices().await?)
}

#[deprecated = "use Manager::contact_by_id"]
pub fn get_contacts(
&self,
Expand Down

0 comments on commit 7fb2d40

Please sign in to comment.