From f21d2bbcdc599c4bb77bbb3fcfc37ba29bcd7035 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Tue, 07 Sep 2021 14:07:29 +0100 Subject: [PATCH] SSH authentication using keys from database --- chartered-db/src/schema.rs | 2 +- chartered-db/src/users.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ chartered-git/src/main.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- migrations/2021-08-31-214501_create_crates_table/up.sql | 2 +- 4 files changed, 96 insertions(+), 13 deletions(-) diff --git a/chartered-db/src/schema.rs b/chartered-db/src/schema.rs index fb96e7e..060e617 100644 --- a/chartered-db/src/schema.rs +++ a/chartered-db/src/schema.rs @@ -44,7 +44,7 @@ table! { users (id) { id -> Integer, - username -> Integer, + username -> Text, } } diff --git a/chartered-db/src/users.rs b/chartered-db/src/users.rs index f9c0bc0..16b1925 100644 --- a/chartered-db/src/users.rs +++ a/chartered-db/src/users.rs @@ -1,10 +1,54 @@ -use super::schema::{user_api_keys, user_crate_permissions, user_ssh_keys, users}; -use diesel::{Associations, Identifiable, Queryable}; +use super::{ + schema::{user_api_keys, user_crate_permissions, user_ssh_keys, users}, + ConnectionPool, Result, +}; +use diesel::{prelude::*, Associations, Identifiable, Queryable}; +use std::sync::Arc; #[derive(Identifiable, Queryable, Associations, PartialEq, Eq, Hash, Debug)] pub struct User { id: i32, username: String, +} + +impl User { + pub async fn find_by_api_key( + conn: ConnectionPool, + given_api_key: String, + ) -> Result> { + use crate::schema::user_api_keys::dsl::*; + + tokio::task::spawn_blocking(move || { + let conn = conn.get().unwrap(); + + Ok(crate::schema::user_api_keys::table + .filter(api_key.eq(given_api_key)) + .inner_join(users::table) + .select((users::dsl::id, users::dsl::username)) + .get_result(&conn) + .optional()?) + }) + .await? + } + + pub async fn find_by_ssh_key( + conn: ConnectionPool, + given_ssh_key: Vec, + ) -> Result> { + use crate::schema::user_ssh_keys::dsl::*; + + tokio::task::spawn_blocking(move || { + let conn = conn.get().unwrap(); + + Ok(crate::schema::user_ssh_keys::table + .filter(ssh_key.eq(given_ssh_key)) + .inner_join(users::table) + .select((users::dsl::id, users::dsl::username)) + .get_result(&conn) + .optional()?) + }) + .await? + } } #[derive(Identifiable, Queryable, Associations, PartialEq, Eq, Hash, Debug)] diff --git a/chartered-git/src/main.rs b/chartered-git/src/main.rs index d0fb4ef..8a3dd58 100644 --- a/chartered-git/src/main.rs +++ a/chartered-git/src/main.rs @@ -17,7 +17,7 @@ server::{self, Auth, Session}, ChannelId, CryptoVec, }; -use thrussh_keys::key; +use thrussh_keys::{key, PublicKeyBase64}; use tokio_util::codec::{Decoder, Encoder as TokioEncoder}; #[tokio::main] @@ -25,9 +25,11 @@ async fn main() { env_logger::init(); - let mut config = thrussh::server::Config::default(); - config.keys.push(key::KeyPair::generate_ed25519().unwrap()); - let config = Arc::new(config); + let config = Arc::new(thrussh::server::Config { + methods: thrussh::MethodSet::PUBLICKEY, + keys: vec![key::KeyPair::generate_ed25519().unwrap()], + ..thrussh::server::Config::default() + }); let server = Server { db: chartered_db::init().unwrap(), @@ -52,6 +54,7 @@ input_bytes: BytesMut::default(), output_bytes: BytesMut::default(), db: self.db.clone(), + user: None, } } } @@ -61,6 +64,7 @@ input_bytes: BytesMut, output_bytes: BytesMut, db: chartered_db::ConnectionPool, + user: Option, } impl Handler { @@ -84,13 +88,19 @@ impl server::Handler for Handler { type Error = anyhow::Error; - type FutureAuth = futures::future::Ready>; + type FutureAuth = Pin< + Box< + dyn Future< + Output = Result<(Handler, server::Auth), ::Error>, + > + Send, + >, + >; type FutureUnit = AsyncHandlerFn; type FutureBool = futures::future::Ready>; fn finished_auth(self, auth: Auth) -> Self::FutureAuth { eprintln!("finished auth"); - futures::future::ready(Ok((self, auth))) + Box::pin(futures::future::ready(Ok((self, auth)))) } fn finished_bool(self, b: bool, s: Session) -> Self::FutureBool { @@ -155,9 +165,38 @@ Box::pin(futures::future::ready(Ok((self, session)))) } - fn auth_publickey(self, _: &str, _: &key::PublicKey) -> Self::FutureAuth { - eprintln!("finished auth pubkey"); - self.finished_auth(server::Auth::Accept) + fn auth_publickey(mut self, _username: &str, key: &key::PublicKey) -> Self::FutureAuth { + let public_key = key.public_key_bytes(); + + Box::pin(async move { + let login_user = + match chartered_db::users::User::find_by_ssh_key(self.db.clone(), public_key) + .await? + { + Some(user) => user, + None => return self.finished_auth(server::Auth::Reject).await, + }; + + self.user = Some(login_user); + self.finished_auth(server::Auth::Accept).await + }) + } + + fn auth_keyboard_interactive( + self, + _user: &str, + _submethods: &str, + _response: Option, + ) -> Self::FutureAuth { + self.finished_auth(server::Auth::UnsupportedMethod) + } + + fn auth_none(self, _user: &str) -> Self::FutureAuth { + self.finished_auth(server::Auth::UnsupportedMethod) + } + + fn auth_password(self, _user: &str, _password: &str) -> Self::FutureAuth { + self.finished_auth(server::Auth::UnsupportedMethod) } fn data(mut self, channel: ChannelId, data: &[u8], mut session: Session) -> Self::FutureUnit { diff --git a/migrations/2021-08-31-214501_create_crates_table/up.sql b/migrations/2021-08-31-214501_create_crates_table/up.sql index 4d27fb8..6b56fe8 100644 --- a/migrations/2021-08-31-214501_create_crates_table/up.sql +++ a/migrations/2021-08-31-214501_create_crates_table/up.sql @@ -16,7 +16,7 @@ CREATE TABLE users ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - username INTEGER NOT NULL UNIQUE + username VARCHAR(255) NOT NULL UNIQUE ); CREATE TABLE user_ssh_keys ( -- rgit 0.1.3