🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-09-07 14:07:29.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-09-07 14:07:29.0 +01:00:00
commit
f21d2bbcdc599c4bb77bbb3fcfc37ba29bcd7035 [patch]
tree
f1d23ee7e712c0a9760c4bd27b4e1781d192bea4
parent
df2c19ea2fcc0efddbda43cb08e6fbd0cf288ffd
download
f21d2bbcdc599c4bb77bbb3fcfc37ba29bcd7035.tar.gz

SSH authentication using keys from database



Diff

 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<Option<User>> {
        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<u8>,
    ) -> Result<Option<User>> {
        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<chartered_db::users::User>,
}

impl Handler {
@@ -84,13 +88,19 @@

impl server::Handler for Handler {
    type Error = anyhow::Error;
    type FutureAuth = futures::future::Ready<Result<(Self, server::Auth), anyhow::Error>>;
    type FutureAuth = Pin<
        Box<
            dyn Future<
                    Output = Result<(Handler, server::Auth), <Handler as server::Handler>::Error>,
                > + Send,
        >,
    >;
    type FutureUnit = AsyncHandlerFn;
    type FutureBool = futures::future::Ready<Result<(Self, Session, bool), anyhow::Error>>;

    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<server::Response>,
    ) -> 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 (