summaryrefslogtreecommitdiff
path: root/src/id.rs
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-09-03 01:25:20 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-09-03 02:09:25 -0400
commitb404344a7c4ab5cb6c7d7b445fab796be79b848f (patch)
treec476b125316b9d4aa7bdece7c9bb8e2f65d2961e /src/id.rs
parent92a7518975c6bc4b2f9b9c6c12c458b24e8cfaf5 (diff)
Allow login creation and authentication.
This is a beefy change, as it adds a TON of smaller pieces needed to make this all function: * A database migration. * A ton of new crates for things like password validation, timekeeping, and HTML generation. * A first cut at a module structure for routes, templates, repositories. * A family of ID types, for identifying various kinds of domain thing. * AppError, which _doesn't_ implement Error but can be sent to clients.
Diffstat (limited to 'src/id.rs')
-rw-r--r--src/id.rs49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/id.rs b/src/id.rs
new file mode 100644
index 0000000..f630107
--- /dev/null
+++ b/src/id.rs
@@ -0,0 +1,49 @@
+use rand::{seq::SliceRandom, thread_rng};
+
+// Make IDs that:
+//
+// * Do not require escaping in URLs
+// * Do not require escaping in hostnames
+// * Are unique up to case conversion
+// * Are relatively unlikely to contain cursewords
+// * Are relatively unlikely to contain visually similar characters in most typefaces
+// * Are not sequential
+//
+// This leaves 23 ASCII characters, or about 4.52 bits of entropy per character
+// if generated with uniform probability.
+pub const ALPHABET: [char; 23] = [
+ '1', '2', '3', '4', '6', '7', '8', '9', 'b', 'c', 'd', 'f', 'h', 'j', 'k', 'n', 'p', 'r', 's',
+ 't', 'w', 'x', 'y',
+];
+
+// Pick enough characters per ID to make accidental collisions "acceptably" unlikely
+// without also making them _too_ unwieldy. This gives a fraction under 68 bits per ID.
+pub const ID_SIZE: usize = 15;
+
+// Intended to be wrapped in a newtype that provides both type-based separation
+// from other identifier types, and a unique prefix to allow the intended type
+// of an ID to be determined by eyeball when debugging.
+//
+// By convention, the prefix should be UPPERCASE - note that the alphabet for this
+// is entirely lowercase.
+#[derive(Debug, Hash, PartialEq, Eq, sqlx::Type)]
+#[sqlx(transparent)]
+pub struct Id(String);
+
+impl Id {
+ pub fn generate<T>(prefix: &str) -> T
+ where
+ T: From<Self>,
+ {
+ let mut rng = thread_rng();
+ let id = prefix
+ .chars()
+ .chain(
+ (0..ID_SIZE)
+ .flat_map(|_| ALPHABET.choose(&mut rng)) /* usize -> &char */
+ .cloned(), /* &char -> char */
+ )
+ .collect::<String>();
+ T::from(Self(id))
+ }
+}