mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 07:02:19 +03:00
Introduce zed-rpc with some shared auth utilities
This commit is contained in:
parent
db3e01a12c
commit
327c20510b
164
Cargo.lock
generated
164
Cargo.lock
generated
@ -650,6 +650,15 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "1.0.5"
|
||||
@ -1109,6 +1118,16 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
@ -1380,6 +1399,9 @@ name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
@ -1415,6 +1437,12 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
@ -1569,6 +1597,35 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"num-integer",
|
||||
"num-traits 0.2.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"libm",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits 0.2.14",
|
||||
"rand 0.8.3",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
@ -1616,6 +1673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1733,6 +1791,17 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"once_cell",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -2224,6 +2293,26 @@ dependencies = [
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ef841a26fc5d040ced0417c6c6a64ee851f42489df11cdf0218e545b6f8d28"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
"lazy_static",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits 0.2.14",
|
||||
"pem",
|
||||
"rand 0.8.3",
|
||||
"simple_asn1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.3"
|
||||
@ -2484,6 +2573,18 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec"
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc31e6cf34ad4321d3a2b8f934949b429e314519f753a77962f16c664dca8e13"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"num-bigint",
|
||||
"num-traits 0.2.14",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplecss"
|
||||
version = "0.2.0"
|
||||
@ -2545,6 +2646,12 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@ -2563,6 +2670,12 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
||||
|
||||
[[package]]
|
||||
name = "svg_fmt"
|
||||
version = "0.4.1"
|
||||
@ -2600,6 +2713,18 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take_mut"
|
||||
version = "0.2.2"
|
||||
@ -2784,6 +2909,12 @@ version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85e00391c1f3d171490a3f8bd79999b0002ae38d3da0d6a3a306c754b053d71b"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
@ -3059,6 +3190,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"postage",
|
||||
"rand 0.8.3",
|
||||
"rsa",
|
||||
"rust-embed",
|
||||
"seahash",
|
||||
"serde 1.0.125",
|
||||
@ -3073,4 +3205,36 @@ dependencies = [
|
||||
"tree-sitter-rust",
|
||||
"unindent",
|
||||
"url",
|
||||
"zed-rpc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed-rpc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"rand 0.8.3",
|
||||
"rsa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["zed", "gpui", "gpui_macros", "fsevent", "scoped_pool"]
|
||||
members = ["zed", "zed-rpc", "gpui", "gpui_macros", "fsevent", "scoped_pool"]
|
||||
|
||||
[patch.crates-io]
|
||||
async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" }
|
||||
|
11
zed-rpc/Cargo.toml
Normal file
11
zed-rpc/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "zed-rpc"
|
||||
description = "Shared logic for communication between the Zed app and the zed.dev server"
|
||||
edition = "2018"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
base64 = "0.13"
|
||||
rsa = "0.4"
|
||||
rand = "0.8"
|
122
zed-rpc/src/auth.rs
Normal file
122
zed-rpc/src/auth.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use rand::{rngs::OsRng, Rng as _};
|
||||
use rsa::{PublicKey as _, PublicKeyEncoding, RSAPrivateKey, RSAPublicKey};
|
||||
|
||||
pub struct PublicKey(RSAPublicKey);
|
||||
|
||||
pub struct PrivateKey(RSAPrivateKey);
|
||||
|
||||
/// Generate a public and private key for asymmetric encryption.
|
||||
pub fn keypair() -> Result<(PublicKey, PrivateKey)> {
|
||||
let mut rng = OsRng;
|
||||
let bits = 1024;
|
||||
let private_key = RSAPrivateKey::new(&mut rng, bits)?;
|
||||
let public_key = RSAPublicKey::from(&private_key);
|
||||
Ok((PublicKey(public_key), PrivateKey(private_key)))
|
||||
}
|
||||
|
||||
/// Generate a random 64-character base64 string.
|
||||
pub fn random_token() -> String {
|
||||
let mut rng = OsRng;
|
||||
let mut token_bytes = [0; 48];
|
||||
for byte in token_bytes.iter_mut() {
|
||||
*byte = rng.gen();
|
||||
}
|
||||
base64::encode(&token_bytes)
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Convert a string to a base64-encoded string that can only be decoded with the corresponding
|
||||
/// private key.
|
||||
pub fn encrypt_string(&self, string: &str) -> Result<String> {
|
||||
let mut rng = OsRng;
|
||||
let bytes = string.as_bytes();
|
||||
let encrypted_bytes = self
|
||||
.0
|
||||
.encrypt(&mut rng, PADDING_SCHEME, bytes)
|
||||
.context("failed to encrypt string with public key")?;
|
||||
let encrypted_string = base64::encode(&encrypted_bytes);
|
||||
Ok(encrypted_string)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
/// Decrypt a base64-encoded string that was encrypted by the correspoding public key.
|
||||
pub fn decrypt_string(&self, encrypted_string: &str) -> Result<String> {
|
||||
let encrypted_bytes =
|
||||
base64::decode(encrypted_string).context("failed to base64-decode encrypted string")?;
|
||||
let bytes = self
|
||||
.0
|
||||
.decrypt(PADDING_SCHEME, &encrypted_bytes)
|
||||
.context("failed to decrypt string with private key")?;
|
||||
let string = String::from_utf8(bytes).context("decrypted content was not valid utf8")?;
|
||||
Ok(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<String> for PublicKey {
|
||||
type Error = anyhow::Error;
|
||||
fn try_into(self) -> Result<String> {
|
||||
let bytes = self
|
||||
.0
|
||||
.to_pkcs1()
|
||||
.context("failed to serialize public key")?;
|
||||
let string = base64::encode(&bytes);
|
||||
Ok(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for PublicKey {
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(value: String) -> Result<Self> {
|
||||
let bytes = base64::decode(&value).context("failed to base64-decode public key string")?;
|
||||
let key = Self(RSAPublicKey::from_pkcs1(&bytes).context("failed to parse public key")?);
|
||||
Ok(key)
|
||||
}
|
||||
}
|
||||
|
||||
const PADDING_SCHEME: rsa::PaddingScheme = rsa::PaddingScheme::PKCS1v15Encrypt;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_generate_encrypt_and_decrypt_token() {
|
||||
// CLIENT:
|
||||
// * generate a keypair for asymmetric encryption
|
||||
// * serialize the public key to send it to the server.
|
||||
let (public, private) = keypair().unwrap();
|
||||
let public_string: String = public.try_into().unwrap();
|
||||
|
||||
// SERVER:
|
||||
// * parse the public key
|
||||
// * generate a random token.
|
||||
// * encrypt the token using the public key.
|
||||
let public: PublicKey = public_string.try_into().unwrap();
|
||||
let token = random_token();
|
||||
let encrypted_token = public.encrypt_string(&token).unwrap();
|
||||
assert_eq!(token.len(), 64);
|
||||
assert_ne!(encrypted_token, token);
|
||||
assert_printable(&token);
|
||||
assert_printable(&encrypted_token);
|
||||
|
||||
// CLIENT:
|
||||
// * decrypt the token using the private key.
|
||||
let decrypted_token = private.decrypt_string(&encrypted_token).unwrap();
|
||||
assert_eq!(decrypted_token, token);
|
||||
}
|
||||
|
||||
fn assert_printable(token: &str) {
|
||||
for c in token.chars() {
|
||||
assert!(
|
||||
c.is_ascii_graphic(),
|
||||
"token {:?} has non-printable char {}",
|
||||
token,
|
||||
c
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
1
zed-rpc/src/lib.rs
Normal file
1
zed-rpc/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod auth;
|
@ -31,6 +31,7 @@ num_cpus = "1.13.0"
|
||||
parking_lot = "0.11.1"
|
||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
rand = "0.8.3"
|
||||
rsa = "0.4"
|
||||
rust-embed = "5.9.0"
|
||||
seahash = "4.1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
@ -42,6 +43,7 @@ toml = "0.5"
|
||||
tree-sitter = "0.19.5"
|
||||
tree-sitter-rust = "0.19.0"
|
||||
url = "2.2"
|
||||
zed-rpc = { path = "../zed-rpc" }
|
||||
|
||||
[dev-dependencies]
|
||||
cargo-bundle = "0.5.0"
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use gpui::MutableAppContext;
|
||||
use smol::io::{AsyncBufReadExt, AsyncWriteExt};
|
||||
@ -33,15 +35,18 @@ fn authenticate(_: &(), cx: &mut MutableAppContext) {
|
||||
let zed_url = std::env::var("ZED_SERVER_URL").unwrap_or("https://zed.dev".to_string());
|
||||
let platform = cx.platform().clone();
|
||||
|
||||
dbg!(&zed_url);
|
||||
|
||||
let task = cx.background_executor().spawn(async move {
|
||||
let listener = smol::net::TcpListener::bind("127.0.0.1:0").await?;
|
||||
let port = listener.local_addr()?.port();
|
||||
|
||||
let (public_key, private_key) =
|
||||
zed_rpc::auth::keypair().expect("failed to generate keypair for auth");
|
||||
|
||||
let public_key_string: String = public_key.try_into().unwrap();
|
||||
|
||||
platform.open_url(&format!(
|
||||
"{}/sign_in?native_app_port={}&native_app_public_key=unused-for-now",
|
||||
zed_url, port,
|
||||
"{}/sign_in?native_app_port={}&native_app_public_key={}",
|
||||
zed_url, port, public_key_string
|
||||
));
|
||||
|
||||
let (mut stream, _) = listener.accept().await?;
|
||||
@ -54,13 +59,13 @@ fn authenticate(_: &(), cx: &mut MutableAppContext) {
|
||||
if let Some(path) = parts.next() {
|
||||
let url = Url::parse(&format!("http://example.com{}", path))
|
||||
.context("failed to parse login notification url")?;
|
||||
let mut user_id = None;
|
||||
let mut access_token = None;
|
||||
let mut public_key = None;
|
||||
for (key, value) in url.query_pairs() {
|
||||
if key == "access_token" {
|
||||
access_token = Some(value);
|
||||
} else if key == "public_key" {
|
||||
public_key = Some(value);
|
||||
} else if key == "user_id" {
|
||||
user_id = Some(value);
|
||||
}
|
||||
}
|
||||
stream
|
||||
@ -69,10 +74,13 @@ fn authenticate(_: &(), cx: &mut MutableAppContext) {
|
||||
.context("failed to write login response")?;
|
||||
stream.flush().await.context("failed to flush tcp stream")?;
|
||||
|
||||
eprintln!(
|
||||
"logged in. access_token: {:?}, public_key: {:?}",
|
||||
access_token, public_key
|
||||
);
|
||||
if let Some((user_id, access_token)) = user_id.zip(access_token) {
|
||||
let access_token = private_key.decrypt_string(&access_token);
|
||||
eprintln!(
|
||||
"logged in. user_id: {}, access_token: {:?}",
|
||||
user_id, access_token
|
||||
);
|
||||
}
|
||||
|
||||
platform.activate(true);
|
||||
return Ok(());
|
||||
|
Loading…
Reference in New Issue
Block a user