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
119 changes: 102 additions & 17 deletions client_lib/src/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ use base64::engine::general_purpose::STANDARD as base64_engine;
use base64::{engine::general_purpose, Engine as _};
use reqwest::blocking::{Body, Client, RequestBuilder};
use reqwest::Url;
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::fs::File;
use std::io::{self, BufRead, BufReader, BufWriter, Write, Read};
use std::path::Path;
use std::time::Duration;
use std::env;

// Some of these constants are based on the ones in server/main.rs.
const MAX_MOTION_FILE_SIZE: u64 = 50 * 1024 * 1024; // 50 mebibytes
Expand Down Expand Up @@ -159,6 +161,19 @@ impl HttpClient {
}
}

fn give_hint_to_updater() {
if let Ok(update_hint_path_str) = env::var("UPDATE_HINT_PATH") {
let update_hint_path = Path::new(&update_hint_path_str);

if !update_hint_path.exists() {
if let Err(e) = File::create(update_hint_path) {
eprintln!("Failed to create file: {}", e);
}
println!("Update hint file created: {}", update_hint_path_str);
}
}
}

/// Atomically confirm pairing with app and receive any phone-side notification target metadata.
pub fn send_pairing_token(&self, pairing_token: &str) -> io::Result<PairingStatus> {
let url = format!("{}/pair", self.server_addr);
Expand All @@ -180,6 +195,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::TimedOut, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand Down Expand Up @@ -209,7 +228,11 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::TimedOut, e.to_string()))?;

if response.status() == reqwest::StatusCode::NOT_FOUND {
if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if response.status() == StatusCode::NOT_FOUND {
return Ok(None);
}

Expand Down Expand Up @@ -277,6 +300,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
let status = response.status();
let content_type = response
Expand Down Expand Up @@ -358,6 +385,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand Down Expand Up @@ -402,10 +433,12 @@ impl HttpClient {
let response = self.authorized_headers(client
.get(&server_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -431,10 +464,12 @@ impl HttpClient {
let del_response = self.authorized_headers(client
.delete(&server_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if del_response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !del_response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -452,10 +487,12 @@ impl HttpClient {
let response = self.authorized_headers(client
.delete(&server_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -477,6 +514,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -498,6 +539,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -522,10 +567,19 @@ impl HttpClient {
let response = self.authorized_headers(client
.get(&server_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Server error: {}", response.status()),
));
}

let mut buf = Vec::new();
let mut limited = response.take(max_size);
limited.read_to_end(&mut buf)?;
Expand Down Expand Up @@ -576,6 +630,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand Down Expand Up @@ -615,10 +673,12 @@ impl HttpClient {
let response = self.authorized_headers(client
.get(&server_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -638,10 +698,12 @@ impl HttpClient {
let del_response = self.authorized_headers(client
.delete(&server_del_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if del_response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !del_response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -664,6 +726,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -686,6 +752,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand All @@ -712,10 +782,19 @@ impl HttpClient {
let response = self.authorized_headers(client
.get(&server_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Server error: {}", response.status()),
));
}

let mut buf = Vec::new();
let mut limited = response.take(max_size);
limited.read_to_end(&mut buf)?;
Expand Down Expand Up @@ -757,6 +836,10 @@ impl HttpClient {
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand Down Expand Up @@ -784,10 +867,12 @@ impl HttpClient {
let response = self.authorized_headers(client
.get(&server_url))
.send()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?
.error_for_status()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;

if response.status() == StatusCode::CONFLICT {
Self::give_hint_to_updater();
}

if !response.status().is_success() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand Down
5 changes: 4 additions & 1 deletion deploy/src-tauri/assets/pi_hub/build_image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ ExecStartPre=/usr/bin/test -r /var/lib/secluso/wifi_password
ExecStart=${SECLUSO_INSTALL_DIR}/bin/secluso-raspberry-camera-hub
Environment="RUST_LOG=info"
Environment="LD_LIBRARY_PATH=/usr/local/lib/aarch64-linux-gnu/:/usr/local/lib:${LD_LIBRARY_PATH:-}"
Environment="UPDATE_HINT_PATH=/var/lib/secluso/update_hint"
Restart=always
RestartSec=1

Expand Down Expand Up @@ -414,6 +415,8 @@ EOF

if [[ -x "$ROOT${SECLUSO_INSTALL_DIR}/bin/$updater_name" ]]; then
UPDATE_INTERVAL_SECS="1800"
HINT_CHECK_INTERVAL_SECS="60"
UPDATE_HINT_PATH="/var/lib/secluso/update_hint"
cat > "$ROOT/etc/systemd/system/secluso-updater.service" <<EOF
[Unit]
Description=Secluso Updater
Expand All @@ -422,7 +425,7 @@ Wants=network-online.target

[Service]
Type=simple
ExecStart=${SECLUSO_INSTALL_DIR}/bin/${updater_name} --component raspberry_camera_hub --interval-secs ${UPDATE_INTERVAL_SECS} --github-timeout-secs 20 --restart-unit secluso_camera_hub.service --github-repo ${SECLUSO_REPO}${SIG_ARGS}
ExecStart=${SECLUSO_INSTALL_DIR}/bin/${updater_name} --component raspberry_camera_hub --interval-secs ${UPDATE_INTERVAL_SECS} --github-timeout-secs 20 --restart-unit secluso_camera_hub.service --github-repo ${SECLUSO_REPO}${SIG_ARGS} --update-hint-path ${UPDATE_HINT_PATH} --hint-check-interval-secs ${HINT_CHECK_INTERVAL_SECS}
Restart=always
RestartSec=2

Expand Down
4 changes: 3 additions & 1 deletion deploy/src-tauri/assets/server/provision_server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ STATE_DIR="${STATE_DIR:-/var/lib/secluso}"
SERVICE_USER="${SERVICE_USER:-secluso}"
RELEASE_TAG="${RELEASE_TAG:-unknown}"
UPDATE_INTERVAL_SECS="${UPDATE_INTERVAL_SECS:-1800}"
HINT_CHECK_INTERVAL_SECS="${HINT_CHECK_INTERVAL_SECS:-60}"
STAGING_DIR="${STAGING_DIR:-}"
SIG_ARGS=""
if [[ -n "${SIG_KEYS:-}" ]]; then
Expand Down Expand Up @@ -133,6 +134,7 @@ Restart=always
RestartSec=1
Environment=RUST_LOG=info
Environment=SECLUSO_USER_CREDENTIALS_DIR=$STATE_DIR/user_credentials
Environment=UPDATE_HINT_PATH=$STATE_DIR/update_hint
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
Expand All @@ -152,7 +154,7 @@ Wants=network-online.target

[Service]
Type=simple
ExecStart=$INSTALL_PREFIX/bin/secluso-update --component server --interval-secs $UPDATE_INTERVAL_SECS --github-timeout-secs 20 --restart-unit $SERVER_UNIT --github-repo $OWNER_REPO$SIG_ARGS
ExecStart=$INSTALL_PREFIX/bin/secluso-update --component server --interval-secs $UPDATE_INTERVAL_SECS --github-timeout-secs 20 --restart-unit $SERVER_UNIT --github-repo $OWNER_REPO$SIG_ARGS --update-hint-path $STATE_DIR/update_hint --hint-check-interval-secs $HINT_CHECK_INTERVAL_SECS
Restart=always
RestartSec=2
${GITHUB_TOKEN:+Environment=GITHUB_TOKEN=$GITHUB_TOKEN}
Expand Down
20 changes: 20 additions & 0 deletions server/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ use std::collections::HashMap;
use std::collections::VecDeque;
use std::ffi::OsString;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::str;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use std::time::Instant;
use std::env;
use subtle::{Choice, ConstantTimeEq};

const DUMMY_PASSWORD: [u8; NUM_PASSWORD_CHARS] = [0u8; NUM_PASSWORD_CHARS];
Expand Down Expand Up @@ -103,6 +105,20 @@ fn to_fixed_bytes(s: &str) -> [u8; NUM_PASSWORD_CHARS] {
out
}

fn give_hint_to_updater() {
if let Ok(update_hint_path_str) = env::var("UPDATE_HINT_PATH") {
let update_hint_path = Path::new(&update_hint_path_str);

if !update_hint_path.exists() {
if let Err(e) = File::create(update_hint_path) {
eprintln!("Failed to create file: {}", e);
}
println!("Update hint file created: {}", update_hint_path_str);
}
}
}


#[rocket::async_trait]
impl<'r> FromRequest<'r> for &'r BasicAuth {
type Error = ();
Expand All @@ -128,9 +144,13 @@ impl<'r> FromRequest<'r> for &'r BasicAuth {
// We run a check on client-provided version header to ensure a match.
// If it fails, we return a 409 (Conflict).
// It must be provided for all, or we cannot guarantee version-compatibility.
// When there's a version mismatch, we also give a hint to the updater so
// that it can check the Github latest release. The version mismatch might
// be because the server is running an old version.
if let Some(client_version) = client_header {
let version = env!("CARGO_PKG_VERSION");
if client_version != version {
give_hint_to_updater();
return Outcome::Error((Status::Conflict, ()));
}
} else {
Expand Down
Loading
Loading