Page MenuHomePhabricator

D9448.id31973.diff
No OneTemporary

D9448.id31973.diff

diff --git a/keyserver/addons/rust-node-addon/Cargo.lock b/keyserver/addons/rust-node-addon/Cargo.lock
--- a/keyserver/addons/rust-node-addon/Cargo.lock
+++ b/keyserver/addons/rust-node-addon/Cargo.lock
@@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "anyhow"
version = "1.0.69"
@@ -1013,6 +1022,8 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
+ "aho-corasick",
+ "memchr",
"regex-syntax",
]
@@ -1065,6 +1076,7 @@
"napi",
"napi-build",
"napi-derive",
+ "regex",
"serde",
"serde_json",
"tokio",
diff --git a/keyserver/addons/rust-node-addon/Cargo.toml b/keyserver/addons/rust-node-addon/Cargo.toml
--- a/keyserver/addons/rust-node-addon/Cargo.toml
+++ b/keyserver/addons/rust-node-addon/Cargo.toml
@@ -27,6 +27,7 @@
[build-dependencies]
napi-build = "2.0.1"
+regex = "1"
[profile.release]
lto = true
diff --git a/keyserver/addons/rust-node-addon/build.rs b/keyserver/addons/rust-node-addon/build.rs
--- a/keyserver/addons/rust-node-addon/build.rs
+++ b/keyserver/addons/rust-node-addon/build.rs
@@ -1,5 +1,50 @@
extern crate napi_build;
+use regex::Regex;
+
+use std::env;
+use std::fs;
+use std::path::Path;
+
fn main() {
napi_build::setup();
+
+ const VERSIONS_JS_PATH: &str = "../../../lib/facts/version.js";
+ println!("cargo:rerun-if-changed={}", VERSIONS_JS_PATH);
+ let js_path = Path::new(VERSIONS_JS_PATH);
+
+ let content =
+ fs::read_to_string(&js_path).expect("Failed to read version.js");
+
+ let version_line = content
+ .lines()
+ .find(|line| line.contains("webAndKeyserverCodeVersion"))
+ .expect("Failed to find webAndKeyserverCodeVersion line");
+
+ // Find a sequence in the input string that starts with
+ // 'webAndKeyserverCodeVersion', followed by any number of whitespace
+ // characters, an equals sign, any number of additional whitespace characters,
+ // a series of one or more digits (and capture these digits), and finally a
+ // semicolon.
+ let re = Regex::new(r"webAndKeyserverCodeVersion\s*=\s*(\d+);").unwrap();
+ let version: u64 = re
+ .captures(&version_line)
+ .and_then(|cap| cap.get(1))
+ .map_or_else(
+ || panic!("Failed to capture version number"),
+ |m| {
+ m.as_str()
+ .parse::<u64>()
+ .expect("Failed to parse version number")
+ },
+ );
+
+ let out_dir = env::var("OUT_DIR").unwrap();
+ let rust_path = Path::new(&out_dir).join("version.rs");
+
+ fs::write(
+ &rust_path,
+ format!("pub const CODE_VERSION: u64 = {};", version),
+ )
+ .expect("Failed to write version.rs");
}
diff --git a/keyserver/addons/rust-node-addon/src/identity_client/mod.rs b/keyserver/addons/rust-node-addon/src/identity_client/mod.rs
--- a/keyserver/addons/rust-node-addon/src/identity_client/mod.rs
+++ b/keyserver/addons/rust-node-addon/src/identity_client/mod.rs
@@ -5,24 +5,32 @@
pub mod remove_reserved_usernames;
pub mod upload_one_time_keys;
-use grpc_clients::identity::authenticated::AuthLayer;
-use grpc_clients::identity::protos::unauthenticated as client_proto;
-use grpc_clients::identity::protos::authenticated::identity_client_service_client::IdentityClientServiceClient as AuthClient;
use client_proto::identity_client_service_client::IdentityClientServiceClient;
use client_proto::{
- AddReservedUsernamesRequest, DeviceKeyUpload, IdentityKeyInfo, PreKey,
- RegistrationFinishRequest, RegistrationStartRequest, DeviceType,
- RemoveReservedUsernameRequest, InboundKeyInfo, UploadOneTimeKeysRequest
+ AddReservedUsernamesRequest, DeviceKeyUpload, DeviceType, IdentityKeyInfo,
+ InboundKeyInfo, PreKey, RegistrationFinishRequest, RegistrationStartRequest,
+ RemoveReservedUsernameRequest, UploadOneTimeKeysRequest,
};
+use grpc_clients::identity::authenticated::ChainedInterceptedAuthClient;
+use grpc_clients::identity::protos::unauthenticated as client_proto;
+use grpc_clients::identity::shared::CodeVersionLayer;
use lazy_static::lazy_static;
use napi::bindgen_prelude::*;
use serde::{Deserialize, Serialize};
-use tonic::codegen::InterceptedService;
use std::env::var;
+use tonic::codegen::InterceptedService;
use tonic::{transport::Channel, Request};
use tracing::{self, info, instrument, warn, Level};
use tracing_subscriber::EnvFilter;
+mod generated {
+ // We get the CODE_VERSION from this generated file
+ include!(concat!(env!("OUT_DIR"), "/version.rs"));
+}
+
+pub use generated::CODE_VERSION;
+pub const DEVICE_TYPE: &str = "keyserver";
+
lazy_static! {
static ref IDENTITY_SERVICE_CONFIG: IdentityServiceConfig = {
let filter = EnvFilter::builder()
@@ -58,12 +66,15 @@
}
}
-async fn get_identity_client_service_channel(
-) -> Result<IdentityClientServiceClient<Channel>> {
+async fn get_identity_client_service_channel() -> Result<
+ IdentityClientServiceClient<InterceptedService<Channel, CodeVersionLayer>>,
+> {
info!("Connecting to identity service");
grpc_clients::identity::get_unauthenticated_client(
&IDENTITY_SERVICE_CONFIG.identity_socket_addr,
+ CODE_VERSION,
+ DEVICE_TYPE.to_string(),
)
.await
.map_err(|_| {
@@ -78,7 +89,7 @@
user_id: String,
device_id: String,
access_token: String,
-) -> Result<AuthClient<InterceptedService<Channel, AuthLayer>>> {
+) -> Result<ChainedInterceptedAuthClient> {
info!("Connecting to identity service");
grpc_clients::identity::get_auth_client(
@@ -86,6 +97,8 @@
user_id,
device_id,
access_token,
+ CODE_VERSION,
+ DEVICE_TYPE.to_string(),
)
.await
.map_err(|_| {
@@ -167,3 +180,13 @@
warn!("Received error: {}", error.message());
Error::new(Status::GenericFailure, error.message())
}
+
+#[cfg(test)]
+mod tests {
+ use super::CODE_VERSION;
+
+ #[test]
+ fn test_code_version_exists() {
+ assert!(CODE_VERSION > 0);
+ }
+}
diff --git a/keyserver/addons/rust-node-addon/src/identity_client/upload_one_time_keys.rs b/keyserver/addons/rust-node-addon/src/identity_client/upload_one_time_keys.rs
--- a/keyserver/addons/rust-node-addon/src/identity_client/upload_one_time_keys.rs
+++ b/keyserver/addons/rust-node-addon/src/identity_client/upload_one_time_keys.rs
@@ -23,7 +23,10 @@
};
debug!("Sending one time keys to Identity service");
- let result = identity_client.upload_one_time_keys(upload_request).await;
+ identity_client
+ .upload_one_time_keys(upload_request)
+ .await
+ .map_err(handle_grpc_error)?;
Ok(true)
}
diff --git a/services/comm-services-lib/src/auth/service.rs b/services/comm-services-lib/src/auth/service.rs
--- a/services/comm-services-lib/src/auth/service.rs
+++ b/services/comm-services-lib/src/auth/service.rs
@@ -13,6 +13,12 @@
const AWSCURRENT: &str = "AWSCURRENT";
const AWSPREVIOUS: &str = "AWSPREVIOUS";
+// Identity service gRPC clients require a code version and device type.
+// We can supply some placeholder values for services for the time being, since
+// this metadata is only relevant for devices.
+const PLACEHOLDER_CODE_VERSION: u64 = 0;
+const DEVICE_TYPE: &str = "service";
+
#[derive(
Debug, derive_more::Display, derive_more::Error, derive_more::From,
)]
@@ -81,6 +87,8 @@
user_id,
device_id,
access_token,
+ PLACEHOLDER_CODE_VERSION,
+ DEVICE_TYPE.to_string(),
)
.await
.map_err(AuthServiceError::from)
diff --git a/services/commtest/tests/grpc_client_test.rs b/services/commtest/tests/grpc_client_test.rs
--- a/services/commtest/tests/grpc_client_test.rs
+++ b/services/commtest/tests/grpc_client_test.rs
@@ -4,12 +4,16 @@
async fn verify_access_token() {
use grpc_clients::identity::unauthenticated::client::verify_user_access_token;
let device_info = create_device(None).await;
+ let code_version = 100;
+ let device_type = "android";
let token_valid = verify_user_access_token(
"http://127.0.0.1:50054",
&device_info.user_id,
&device_info.device_id,
&device_info.access_token,
+ code_version,
+ device_type.to_string(),
)
.await
.expect("Failed to call identity's verify_user_access_token endpoint");
@@ -22,6 +26,8 @@
&device_info.user_id,
&device_info.device_id,
"garbage",
+ code_version,
+ device_type.to_string(),
)
.await
.expect("Failed to call identity's verify_user_access_token endpoint");
diff --git a/services/tunnelbroker/src/identity/mod.rs b/services/tunnelbroker/src/identity/mod.rs
--- a/services/tunnelbroker/src/identity/mod.rs
+++ b/services/tunnelbroker/src/identity/mod.rs
@@ -7,14 +7,24 @@
use crate::config::CONFIG;
use crate::error::Error;
+// Identity service gRPC clients require a code version and device type.
+// We can supply some placeholder values for services for the time being, since
+// this metadata is only relevant for devices.
+const PLACEHOLDER_CODE_VERSION: u64 = 0;
+const DEVICE_TYPE: &str = "service";
+
/// Returns true if access token is valid
pub async fn verify_user_access_token(
user_id: &str,
device_id: &str,
access_token: &str,
) -> Result<bool, Error> {
- let mut grpc_client =
- get_unauthenticated_client(&CONFIG.identity_endpoint).await?;
+ let mut grpc_client = get_unauthenticated_client(
+ &CONFIG.identity_endpoint,
+ PLACEHOLDER_CODE_VERSION,
+ DEVICE_TYPE.to_string(),
+ )
+ .await?;
let message = VerifyUserAccessTokenRequest {
user_id: user_id.to_string(),
signing_public_key: device_id.to_string(),
diff --git a/shared/grpc_clients/src/identity/authenticated.rs b/shared/grpc_clients/src/identity/authenticated.rs
--- a/shared/grpc_clients/src/identity/authenticated.rs
+++ b/shared/grpc_clients/src/identity/authenticated.rs
@@ -7,7 +7,8 @@
Request, Status,
};
-use crate::error::Error;
+use crate::identity::shared::{ChainedInterceptor, ToMetadataValueAscii};
+use crate::{error::Error, identity::shared::CodeVersionLayer};
pub struct AuthLayer {
user_id: String,
@@ -15,10 +16,6 @@
access_token: String,
}
-trait ToMetadataValueAscii {
- fn parse_to_ascii(&self) -> Result<MetadataValue<Ascii>, Status>;
-}
-
impl ToMetadataValueAscii for str {
fn parse_to_ascii(&self) -> Result<MetadataValue<Ascii>, Status> {
self.parse().map_err(|e: InvalidMetadataValue| {
@@ -40,21 +37,38 @@
Ok(request)
}
}
+
+pub type ChainedInterceptedAuthClient = AuthClient<
+ InterceptedService<Channel, ChainedInterceptor<AuthLayer, CodeVersionLayer>>,
+>;
+
pub async fn get_auth_client(
url: &str,
user_id: String,
device_id: String,
access_token: String,
-) -> Result<AuthClient<InterceptedService<Channel, AuthLayer>>, Error> {
+ code_version: u64,
+ device_type: String,
+) -> Result<ChainedInterceptedAuthClient, Error> {
use crate::get_grpc_service_channel;
let channel = get_grpc_service_channel(url).await?;
- let interceptor = AuthLayer {
+ let auth_interceptor = AuthLayer {
user_id,
device_id,
access_token,
};
- Ok(AuthClient::with_interceptor(channel, interceptor))
+ let version_interceptor = CodeVersionLayer {
+ version: code_version,
+ device_type,
+ };
+
+ let chained = ChainedInterceptor {
+ first: auth_interceptor,
+ second: version_interceptor,
+ };
+
+ Ok(AuthClient::with_interceptor(channel, chained))
}
diff --git a/shared/grpc_clients/src/identity/mod.rs b/shared/grpc_clients/src/identity/mod.rs
--- a/shared/grpc_clients/src/identity/mod.rs
+++ b/shared/grpc_clients/src/identity/mod.rs
@@ -1,5 +1,6 @@
pub mod authenticated;
pub mod device;
+pub mod shared;
pub mod unauthenticated;
pub mod protos {
diff --git a/shared/grpc_clients/src/identity/shared.rs b/shared/grpc_clients/src/identity/shared.rs
new file mode 100644
--- /dev/null
+++ b/shared/grpc_clients/src/identity/shared.rs
@@ -0,0 +1,57 @@
+use tonic::{
+ metadata::{errors::InvalidMetadataValue, Ascii, MetadataValue},
+ service::Interceptor,
+ Request, Status,
+};
+
+pub struct CodeVersionLayer {
+ pub(crate) version: u64,
+ pub(crate) device_type: String,
+}
+
+impl Interceptor for CodeVersionLayer {
+ fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
+ let metadata = request.metadata_mut();
+ metadata.insert("code_version", self.version.parse_to_ascii()?);
+ metadata.insert("device_type", self.device_type.parse_to_ascii()?);
+
+ Ok(request)
+ }
+}
+
+pub trait ToMetadataValueAscii {
+ fn parse_to_ascii(&self) -> Result<MetadataValue<Ascii>, Status>;
+}
+
+impl ToMetadataValueAscii for u64 {
+ fn parse_to_ascii(&self) -> Result<MetadataValue<Ascii>, Status> {
+ let ascii_string = self.to_string();
+
+ ascii_string.parse().map_err(|e: InvalidMetadataValue| {
+ Status::invalid_argument(format!(
+ "Non-Ascii character present in metadata value: {}",
+ e
+ ))
+ })
+ }
+}
+
+pub struct ChainedInterceptor<A, B>
+where
+ A: Interceptor + Send + Sync + 'static,
+ B: Interceptor + Send + Sync + 'static,
+{
+ pub(crate) first: A,
+ pub(crate) second: B,
+}
+
+impl<A, B> Interceptor for ChainedInterceptor<A, B>
+where
+ A: Interceptor + Send + Sync + 'static,
+ B: Interceptor + Send + Sync + 'static,
+{
+ fn call(&mut self, request: Request<()>) -> Result<Request<()>, Status> {
+ let request = self.first.call(request)?;
+ self.second.call(request)
+ }
+}
diff --git a/shared/grpc_clients/src/identity/unauthenticated/client.rs b/shared/grpc_clients/src/identity/unauthenticated/client.rs
--- a/shared/grpc_clients/src/identity/unauthenticated/client.rs
+++ b/shared/grpc_clients/src/identity/unauthenticated/client.rs
@@ -12,8 +12,11 @@
user_id: &str,
device_id: &str,
access_token: &str,
+ code_version: u64,
+ device_type: String,
) -> Result<bool, Error> {
- let mut grpc_client = get_unauthenticated_client(identity_url).await?;
+ let mut grpc_client =
+ get_unauthenticated_client(identity_url, code_version, device_type).await?;
let message = VerifyUserAccessTokenRequest {
user_id: user_id.to_string(),
@@ -23,5 +26,5 @@
let request = Request::new(message);
let response = grpc_client.verify_user_access_token(request).await?;
- return Ok(response.into_inner().token_valid);
+ Ok(response.into_inner().token_valid)
}
diff --git a/shared/grpc_clients/src/identity/unauthenticated/mod.rs b/shared/grpc_clients/src/identity/unauthenticated/mod.rs
--- a/shared/grpc_clients/src/identity/unauthenticated/mod.rs
+++ b/shared/grpc_clients/src/identity/unauthenticated/mod.rs
@@ -1,13 +1,29 @@
pub mod client;
+use tonic::codegen::InterceptedService;
use tonic::transport::Channel;
-use super::protos::client::identity_client_service_client::IdentityClientServiceClient;
+use super::{
+ protos::client::identity_client_service_client::IdentityClientServiceClient,
+ shared::CodeVersionLayer,
+};
use crate::error::Error;
pub async fn get_unauthenticated_client(
url: &str,
-) -> Result<IdentityClientServiceClient<Channel>, Error> {
+ code_version: u64,
+ device_type: String,
+) -> Result<
+ IdentityClientServiceClient<InterceptedService<Channel, CodeVersionLayer>>,
+ Error,
+> {
let channel = crate::get_grpc_service_channel(url).await?;
- Ok(IdentityClientServiceClient::new(channel))
+ let version_interceptor = CodeVersionLayer {
+ version: code_version,
+ device_type,
+ };
+ Ok(IdentityClientServiceClient::with_interceptor(
+ channel,
+ version_interceptor,
+ ))
}
diff --git a/shared/grpc_clients/src/lib.rs b/shared/grpc_clients/src/lib.rs
--- a/shared/grpc_clients/src/lib.rs
+++ b/shared/grpc_clients/src/lib.rs
@@ -10,7 +10,7 @@
use tonic::transport::{Certificate, Channel, ClientTlsConfig};
use tracing::info;
-const CERT_PATHS: &'static [&'static str] = &[
+const CERT_PATHS: &[&str] = &[
// MacOS and newer Ubuntu
"/etc/ssl/cert.pem",
// Common CA cert paths

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 6, 6:34 AM (21 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2428404
Default Alt Text
D9448.id31973.diff (15 KB)

Event Timeline