Add caretta-id
This commit is contained in:
parent
f2e79d4cd0
commit
99fdb12712
18 changed files with 241 additions and 30 deletions
|
|
@ -26,7 +26,7 @@ caretta-sync-macros = { path="macros", optional = true}
|
||||||
caretta-sync-core = {workspace = true, features = ["test"]}
|
caretta-sync-core = {workspace = true, features = ["test"]}
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [ ".", "core", "macros", "cli", "mobile", "examples/*" , "bevy"]
|
members = [ ".", "core", "macros", "cli", "mobile", "examples/*" , "bevy", "id"]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
|
@ -43,6 +43,7 @@ ciborium = "0.2.2"
|
||||||
clap = { version = "4.5.38", features = ["derive"] }
|
clap = { version = "4.5.38", features = ["derive"] }
|
||||||
caretta-sync-core.path = "core"
|
caretta-sync-core.path = "core"
|
||||||
futures = { version = "0.3.31", features = ["executor"] }
|
futures = { version = "0.3.31", features = ["executor"] }
|
||||||
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
tokio = { version = "1.45.0", features = ["macros", "rt", "rt-multi-thread"] }
|
tokio = { version = "1.45.0", features = ["macros", "rt", "rt-multi-thread"] }
|
||||||
|
|
@ -56,6 +57,7 @@ prost-types = "0.14.1"
|
||||||
tonic-prost-build = "0.14.0"
|
tonic-prost-build = "0.14.0"
|
||||||
tonic-prost = "0.14.0"
|
tonic-prost = "0.14.0"
|
||||||
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
use clap::Args;
|
|
||||||
use caretta_sync_core::utils::runnable::Runnable;
|
|
||||||
|
|
||||||
use crate::cli::ConfigArgs;
|
|
||||||
|
|
||||||
use crate::cli::PeerArgs;
|
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
|
||||||
pub struct DeviceAddCommandArgs {
|
|
||||||
#[command(flatten)]
|
|
||||||
peer: PeerArgs,
|
|
||||||
#[arg(short, long)]
|
|
||||||
passcode: Option<String>,
|
|
||||||
#[command(flatten)]
|
|
||||||
config: ConfigArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Runnable for DeviceAddCommandArgs {
|
|
||||||
fn run(self, app_name: &'static str) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
0
cli/src/cli/device/auth/approve.rs
Normal file
0
cli/src/cli/device/auth/approve.rs
Normal file
0
cli/src/cli/device/auth/list.rs
Normal file
0
cli/src/cli/device/auth/list.rs
Normal file
0
cli/src/cli/device/auth/mod.rs
Normal file
0
cli/src/cli/device/auth/mod.rs
Normal file
0
cli/src/cli/device/auth/request.rs
Normal file
0
cli/src/cli/device/auth/request.rs
Normal file
|
|
@ -36,7 +36,7 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
whoami = "1.6.1"
|
whoami = "1.6.1"
|
||||||
rand = "0.8.5"
|
rand.workspace = true
|
||||||
ed25519-dalek = { version = "2.2.0", features = ["signature"] }
|
ed25519-dalek = { version = "2.2.0", features = ["signature"] }
|
||||||
tokio-stream.workspace = true
|
tokio-stream.workspace = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,26 +10,29 @@ use iroh::NodeId;
|
||||||
pub use request::*;
|
pub use request::*;
|
||||||
pub use response::*;
|
pub use response::*;
|
||||||
use rusqlite::{params, types::FromSqlError, Connection};
|
use rusqlite::{params, types::FromSqlError, Connection};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::data::local::RusqliteRecord;
|
use crate::data::local::RusqliteRecord;
|
||||||
|
|
||||||
/// On going authorization
|
/// On going authorization
|
||||||
pub struct Authorization {
|
pub struct Authorization {
|
||||||
|
request_id: Uuid,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
passcode: String,
|
node_info: Option<String>,
|
||||||
|
passcode: Option<String>,
|
||||||
created_at: DateTime<Local>,
|
created_at: DateTime<Local>,
|
||||||
updated_at: DateTime<Local>,
|
updated_at: DateTime<Local>,
|
||||||
}
|
}
|
||||||
static TABLE_NAME: &str = "authorization";
|
static TABLE_NAME: &str = "authorization";
|
||||||
static DEFAULT_COLUMNS: [&str;4] = [
|
static DEFAULT_COLUMNS: [&str;5] = [
|
||||||
|
"request_id",
|
||||||
"node_id",
|
"node_id",
|
||||||
"passcode",
|
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at"
|
"updated_at"
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Authorization {
|
impl Authorization {
|
||||||
pub fn new(node_id: NodeId, passcode: String) -> Self {
|
pub fn new_sent(node_id: NodeId, passcode: String) -> Self {
|
||||||
let timestamp = Local::now();
|
let timestamp = Local::now();
|
||||||
Self {
|
Self {
|
||||||
node_id: node_id,
|
node_id: node_id,
|
||||||
|
|
@ -38,6 +41,7 @@ impl Authorization {
|
||||||
updated_at: timestamp
|
updated_at: timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn new_received(node_id:)
|
||||||
pub fn get_by_node_id(node_id: NodeId, connection: &Connection) -> Result<Self, rusqlite::Error> {
|
pub fn get_by_node_id(node_id: NodeId, connection: &Connection) -> Result<Self, rusqlite::Error> {
|
||||||
connection.query_row(
|
connection.query_row(
|
||||||
"SELECT node_id, passcode, created_at, updated_at FROM authorizaation WHRE node_id=(?1)",
|
"SELECT node_id, passcode, created_at, updated_at FROM authorizaation WHRE node_id=(?1)",
|
||||||
|
|
|
||||||
13
id/Cargo.toml
Normal file
13
id/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "caretta-id"
|
||||||
|
edition.workspace = true
|
||||||
|
version = "0.1.0-alpha"
|
||||||
|
description.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
21
id/README.md
Normal file
21
id/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Carreta ID
|
||||||
|
Random user-friendly id for distubuted system for personal data.
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
- `123` : shortest version
|
||||||
|
- `456-789` : default size, still user freindly and sufficient randomness (for personal data)
|
||||||
|
- `abc-def-ghj` : long version. alphabets except i, l and o are also valid
|
||||||
|
## Specs
|
||||||
|
### Characters
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Perpose
|
||||||
|
When I considering implementing IDs for users(not for internal system) to specify items, such as GitHub commit hashes or issue numbers, the following issues arose.
|
||||||
|
|
||||||
|
- Sequential numbers like Git issues are difficult to implement in distributes systems because collitions are unavoidable.
|
||||||
|
- Random number like UUID is too long for users
|
||||||
|
- Short random number like 7-digit commit hash seems good but is is not standardized specification.
|
||||||
|
|
||||||
|
So I decided to make my own ID specifications.
|
||||||
|
|
||||||
3
id/src/builder.rs
Normal file
3
id/src/builder.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub struct IdBuilder {
|
||||||
|
|
||||||
|
}
|
||||||
174
id/src/chunk.rs
Normal file
174
id/src/chunk.rs
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
|
static CHARACTERS: &[u8;33] = b"0123456789abcdefghjkmnpqrstuvwxyz";
|
||||||
|
|
||||||
|
static BASE: u16 = 33;
|
||||||
|
static SQUARED_BASE: u16 = 1089;
|
||||||
|
static CUBED_BASE: u16 = 35937;
|
||||||
|
|
||||||
|
pub static NIL: IdChunk = IdChunk(0);
|
||||||
|
pub static MAX: IdChunk = IdChunk(CUBED_BASE-1);
|
||||||
|
|
||||||
|
fn char_to_u8(c: char) -> Option<u8> {
|
||||||
|
Some(match c {
|
||||||
|
'0' => 0,
|
||||||
|
'1' => 1,
|
||||||
|
'2' => 2,
|
||||||
|
'3' => 3,
|
||||||
|
'4' => 4,
|
||||||
|
'5' => 5,
|
||||||
|
'6' => 6,
|
||||||
|
'7' => 7,
|
||||||
|
'8' => 8,
|
||||||
|
'9' => 9,
|
||||||
|
'a' => 10,
|
||||||
|
'b' => 11,
|
||||||
|
'c' => 12,
|
||||||
|
'd' => 13,
|
||||||
|
'e' => 14,
|
||||||
|
'f' => 15,
|
||||||
|
'g' => 16,
|
||||||
|
'h' => 17,
|
||||||
|
'i' => 1,
|
||||||
|
'j' => 18,
|
||||||
|
'k' => 19,
|
||||||
|
'l' => 1,
|
||||||
|
'm' => 20,
|
||||||
|
'n' => 21,
|
||||||
|
'o' => 0,
|
||||||
|
'p' => 22,
|
||||||
|
'q' => 23,
|
||||||
|
'r' => 24,
|
||||||
|
's' => 25,
|
||||||
|
't' => 26,
|
||||||
|
'u' => 27,
|
||||||
|
'v' => 28,
|
||||||
|
'w' => 29,
|
||||||
|
'x' => 30,
|
||||||
|
'y' => 31,
|
||||||
|
'z' => 32,
|
||||||
|
'A' => 10,
|
||||||
|
'B' => 11,
|
||||||
|
'C' => 12,
|
||||||
|
'D' => 13,
|
||||||
|
'E' => 14,
|
||||||
|
'F' => 15,
|
||||||
|
'G' => 16,
|
||||||
|
'H' => 17,
|
||||||
|
'I' => 1,
|
||||||
|
'J' => 18,
|
||||||
|
'K' => 19,
|
||||||
|
'L' => 1,
|
||||||
|
'M' => 20,
|
||||||
|
'N' => 21,
|
||||||
|
'O' => 0,
|
||||||
|
'P' => 22,
|
||||||
|
'Q' => 23,
|
||||||
|
'R' => 24,
|
||||||
|
'S' => 25,
|
||||||
|
'T' => 26,
|
||||||
|
'U' => 27,
|
||||||
|
'V' => 28,
|
||||||
|
'W' => 29,
|
||||||
|
'X' => 30,
|
||||||
|
'Y' => 31,
|
||||||
|
'Z' => 32,
|
||||||
|
_ => return None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str_to_u16(s: &str) -> Result<u16, Error> {
|
||||||
|
if s.len() != 3 {
|
||||||
|
return Err(Error::InvalidChunk(format!("Chunk '{}' is not 3 characters", s)))
|
||||||
|
}
|
||||||
|
let mut buf : [u16;3] = [0;3];
|
||||||
|
for (i, c) in s.chars().enumerate() {
|
||||||
|
buf[i] = BASE.pow((2 - i) as u32) * (char_to_u8(c).ok_or(Error::InvalidChunk(format!("Invalid char: {}", c)))? as u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(buf.iter().sum())
|
||||||
|
}
|
||||||
|
fn u16_to_string(int: u16) -> Result<String, Error> {
|
||||||
|
if int >= CUBED_BASE {
|
||||||
|
return Err(Error::OutsideOfRange(int))
|
||||||
|
}
|
||||||
|
let first_char = char::from(CHARACTERS[usize::try_from(int / SQUARED_BASE).unwrap()]);
|
||||||
|
let second_char = char::from(CHARACTERS[usize::try_from((int % SQUARED_BASE)/ BASE).unwrap()]);
|
||||||
|
let third_char = char::from(CHARACTERS[usize::try_from(int % BASE).unwrap()]);
|
||||||
|
Ok(format!("{}{}{}", first_char, second_char, third_char))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct IdChunk(u16);
|
||||||
|
|
||||||
|
impl IdChunk {
|
||||||
|
pub fn new<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
||||||
|
Self(rng.gen_range(0..CUBED_BASE))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IdChunk {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for IdChunk {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u16> for IdChunk {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IdChunk> for u16 {
|
||||||
|
fn from(value: IdChunk) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
fn assert_eq_chunk(s: &str, i: u16) {
|
||||||
|
assert_eq!(s, &u16_to_string(i).unwrap());
|
||||||
|
assert_eq!(i, str_to_u16(s).unwrap())
|
||||||
|
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_nil() {
|
||||||
|
assert_eq_chunk("000", 0);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_max() {
|
||||||
|
assert_eq_chunk("zzz", CUBED_BASE-1);
|
||||||
|
}
|
||||||
|
fn assert_random<R>(rand: &mut R)
|
||||||
|
where
|
||||||
|
R: Rng
|
||||||
|
{
|
||||||
|
let chunk = IdChunk::new(rand);
|
||||||
|
let s = chunk.to_string();
|
||||||
|
assert_eq!(chunk,IdChunk::from_str(&s).unwrap())
|
||||||
|
}
|
||||||
|
fn random_x10() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
for _ in 0..10 {
|
||||||
|
assert_random(&mut rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
4
id/src/config.rs
Normal file
4
id/src/config.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub struct Config {
|
||||||
|
pub enable_nil: bool,
|
||||||
|
pub enable_max: bool,
|
||||||
|
}
|
||||||
0
id/src/double.rs
Normal file
0
id/src/double.rs
Normal file
8
id/src/error.rs
Normal file
8
id/src/error.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Outside of range: {0}")]
|
||||||
|
OutsideOfRange(u16),
|
||||||
|
#[error("Invalid chunk: {0}")]
|
||||||
|
InvalidChunk(String),
|
||||||
|
}
|
||||||
|
|
||||||
6
id/src/lib.rs
Normal file
6
id/src/lib.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
mod chunk;
|
||||||
|
mod double;
|
||||||
|
mod error;
|
||||||
|
mod single;
|
||||||
|
mod triple;
|
||||||
|
|
||||||
0
id/src/single.rs
Normal file
0
id/src/single.rs
Normal file
0
id/src/triple.rs
Normal file
0
id/src/triple.rs
Normal file
Loading…
Add table
Reference in a new issue