Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions ceres/src/lfs/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,10 +492,7 @@ async fn lfs_get_filtered_locks(
cursor: &str,
limit: &str,
) -> Result<(Vec<Lock>, String), GitLFSError> {
let mut locks = match lfs_get_locks(storage, refspec).await {
Ok(locks) => locks,
Err(_) => vec![],
};
let mut locks = (lfs_get_locks(storage, refspec).await).unwrap_or_default();

tracing::debug!("Locks retrieved: {:?}", locks);

Expand Down
6 changes: 3 additions & 3 deletions ceres/src/protocol/smart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl SmartProtocol {
}
};
if !read_first_line {
self.parse_capabilities(&String::from_utf8(dst[46..].to_vec()).unwrap());
self.parse_capabilities(core::str::from_utf8(&dst[46..]).unwrap());
read_first_line = true;
}
}
Expand Down Expand Up @@ -193,7 +193,7 @@ impl SmartProtocol {
let (bytes_take, mut pkt_line) = read_pkt_line(&mut protocol_bytes);
if bytes_take != 0 {
let command = self.parse_ref_command(&mut pkt_line);
self.parse_capabilities(&String::from_utf8(pkt_line.to_vec()).unwrap());
self.parse_capabilities(core::str::from_utf8(&pkt_line).unwrap());
tracing::debug!(
"parse ref_command: {:?}, with caps:{:?}",
command,
Expand Down Expand Up @@ -383,7 +383,7 @@ pub fn read_pkt_line(bytes: &mut Bytes) -> (usize, Bytes) {
return (0, Bytes::new());
}
let pkt_length = bytes.copy_to_bytes(4);
let pkt_length = usize::from_str_radix(&String::from_utf8(pkt_length.to_vec()).unwrap(), 16)
let pkt_length = usize::from_str_radix(core::str::from_utf8(&pkt_length).unwrap(), 16)
.unwrap_or_else(|_| panic!("{:?} is not a valid digit?", pkt_length));
if pkt_length == 0 {
return (0, Bytes::new());
Expand Down
14 changes: 10 additions & 4 deletions jupiter/src/storage/user_storage.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::sync::Arc;

use sea_orm::{
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel, QueryFilter,
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel, ModelTrait,
QueryFilter,
};

use callisto::{ssh_keys, user};
Expand Down Expand Up @@ -67,10 +68,15 @@ impl UserStorage {
Ok(res)
}

pub async fn delete_ssh_key(&self, id: i64) -> Result<(), MegaError> {
ssh_keys::Entity::delete_by_id(id)
.exec(self.get_connection())
pub async fn delete_ssh_key(&self, user_id: i64, id: i64) -> Result<(), MegaError> {
let res = ssh_keys::Entity::find()
.filter(ssh_keys::Column::Id.eq(id))
.filter(ssh_keys::Column::UserId.eq(user_id))
.one(self.get_connection())
.await?;
if let Some(model) = res {
model.delete(self.get_connection()).await?;
}
Ok(())
}

Expand Down
24 changes: 24 additions & 0 deletions mono/src/api/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use axum::response::{IntoResponse, Response};
use http::StatusCode;

#[derive(Debug)]
pub struct ApiError(anyhow::Error);

impl IntoResponse for ApiError {
fn into_response(self) -> Response {
tracing::error!("Application error: {:#}", self.0);

(StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong").into_response()
}
}

// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
// `Result<_, ApiError>`. That way you don't need to do that manually.
impl<E> From<E> for ApiError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}
6 changes: 4 additions & 2 deletions mono/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::path::PathBuf;

use async_session::MemoryStore;
use axum::extract::FromRef;
use oauth2::basic::BasicClient;

use ceres::{
api_service::{
import_api_service::ImportApiService, mono_api_service::MonoApiService, ApiHandler,
Expand All @@ -10,13 +12,13 @@ use ceres::{
};
use common::model::CommonOptions;
use jupiter::{context::Context, storage::user_storage::UserStorage};
use oauth2::basic::BasicClient;

pub mod api_router;
pub mod error;
pub mod lfs;
pub mod mr_router;
pub mod oauth;
pub mod user;
pub mod lfs;

#[derive(Clone)]
pub struct MonoApiServiceState {
Expand Down
57 changes: 21 additions & 36 deletions mono/src/api/oauth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ use axum_extra::{headers, typed_header::TypedHeaderRejectionReason, TypedHeader}
use callisto::user;
use chrono::{Duration, Utc};
use http::{header, request::Parts, StatusCode};
use jupiter::storage::user_storage::UserStorage;
use oauth2::{
basic::BasicClient, reqwest::async_http_client, AuthUrl, AuthorizationCode, ClientId,
ClientSecret, CsrfToken, RedirectUrl, Scope, TokenResponse, TokenUrl,
};

use common::config::OauthConfig;
use jupiter::storage::user_storage::UserStorage;
use model::{GitHubUserJson, LoginUser, OauthCallbackParams};

use crate::api::error::ApiError;
use crate::api::MonoApiServiceState;

pub mod model;
Expand All @@ -47,7 +48,7 @@ async fn login_authorized(
Query(query): Query<OauthCallbackParams>,
State(state): State<MonoApiServiceState>,
State(oauth_client): State<BasicClient>,
) -> Result<impl IntoResponse, OauthError> {
) -> Result<impl IntoResponse, ApiError> {
let store: MemoryStore = MemoryStore::from_ref(&state);
let config = state.context.config.oauth.unwrap();
// Get an auth token
Expand Down Expand Up @@ -77,16 +78,22 @@ async fn login_authorized(
tracing::error!("github:user_info:err {:?}", resp.text().await.unwrap());
}


let user_data: user::Model = github_user.into();
let new_user: user::Model = github_user.into();
let user_storage = state.context.services.user_storage.clone();
let user = user_storage.find_user_by_email(&user_data.email).await.unwrap();
if user.is_none() {
user_storage.save_user(user_data.clone()).await.unwrap();
let user = user_storage
.find_user_by_email(&new_user.email)
.await
.unwrap();

let login_user: LoginUser;
if let Some(user) = user {
// Create a new session filled with user data
login_user = user.into();
} else {
user_storage.save_user(new_user.clone()).await.unwrap();
login_user = new_user.into();
}

// Create a new session filled with user data
let login_user:LoginUser = user_data.into();
let mut session = Session::new();
session
.insert("user", &login_user)
Expand All @@ -99,9 +106,10 @@ async fn login_authorized(
.context("failed to store session")?
.context("unexpected error retrieving cookie value")?;

// Build the cookie
// SameSite=Lax: Allow GET, disable POST cookie send, prevent CSRF
// SameSite=None: allow Post cookie send
let cookie = format!(
"{COOKIE_NAME}={cookie}; Domain={}; SameSite=Lax; Path=/",
"{COOKIE_NAME}={cookie}; Domain={}; SameSite=Lax; Secure; Path=/",
config.cookie_domain
);
// Set cookie
Expand All @@ -117,7 +125,7 @@ async fn login_authorized(
async fn logout(
State(state): State<MonoApiServiceState>,
TypedHeader(cookies): TypedHeader<headers::Cookie>,
) -> Result<impl IntoResponse, OauthError> {
) -> Result<impl IntoResponse, ApiError> {
let store: MemoryStore = MemoryStore::from_ref(&state);
let config = state.context.config.oauth.unwrap();
let cookie = cookies
Expand Down Expand Up @@ -153,7 +161,7 @@ async fn logout(
Ok((headers, Redirect::to(&config.ui_domain)))
}

pub fn oauth_client(oauth_config: OauthConfig) -> Result<BasicClient, OauthError> {
pub fn oauth_client(oauth_config: OauthConfig) -> Result<BasicClient, ApiError> {
let client_id = oauth_config.github_client_id;
let client_secret = oauth_config.github_client_secret;
let ui_domain = oauth_config.ui_domain;
Expand Down Expand Up @@ -219,26 +227,3 @@ where
Ok(user)
}
}

// Use anyhow, define error and enable '?'
// For a simplified example of using anyhow in axum check /examples/anyhow-error-response
#[derive(Debug)]
pub struct OauthError(anyhow::Error);

impl IntoResponse for OauthError {
fn into_response(self) -> Response {
tracing::error!("Application error: {:#}", self.0);
(StatusCode::INTERNAL_SERVER_ERROR, "Login in first").into_response()
}
}

// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
// `Result<_, OauthError>`. That way you don't need to do that manually.
impl<E> From<E> for OauthError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}
5 changes: 5 additions & 0 deletions mono/src/api/user/model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use callisto::ssh_keys;
use serde::{Deserialize, Serialize};
use sea_orm::entity::prelude::*;

#[derive(Debug, Deserialize)]
pub struct AddSSHKey {
Expand All @@ -10,13 +11,17 @@ pub struct AddSSHKey {
pub struct ListSSHKey {
pub id: i64,
pub ssh_key: String,
pub finger: String,
pub created_at: DateTime,
}

impl From<ssh_keys::Model> for ListSSHKey {
fn from(value: ssh_keys::Model) -> Self {
Self {
id: value.id,
ssh_key: value.ssh_key,
finger: value.finger,
created_at: value.created_at,
}
}
}
19 changes: 9 additions & 10 deletions mono/src/api/user/user_router.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
use axum::{
extract::{Path, State},
http::StatusCode,
routing::{get, post},
Json, Router,
};
use russh_keys::parse_public_key_base64;

use common::model::CommonResult;

use crate::api::oauth::model::LoginUser;
use crate::api::{error::ApiError, oauth::model::LoginUser};
use crate::api::user::model::AddSSHKey;
use crate::api::user::model::ListSSHKey;
use crate::api::MonoApiServiceState;

pub fn routers() -> Router<MonoApiServiceState> {
Router::new()
.route("/user", get(user))
.route("/user/ssh", get(list_key))
.route("/user/ssh", post(add_key))
.route("/user/ssh/:key_id/delete", post(remove_key))
.route("/user/ssh/list", get(list_key))
}

async fn user(
user: LoginUser,
_: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<LoginUser>>, (StatusCode, String)> {
) -> Result<Json<CommonResult<LoginUser>>, ApiError> {
Ok(Json(CommonResult::success(Some(user))))
}

async fn add_key(
user: LoginUser,
state: State<MonoApiServiceState>,
Json(json): Json<AddSSHKey>,
) -> Result<Json<CommonResult<String>>, (StatusCode, String)> {
) -> Result<Json<CommonResult<String>>, ApiError> {
let key_data = json
.ssh_key
.split_whitespace()
.nth(1)
.ok_or("Invalid key format")
.unwrap();

let key = parse_public_key_base64(key_data).unwrap();
let key = parse_public_key_base64(key_data)?;

let res = state
.context
Expand All @@ -56,15 +55,15 @@ async fn add_key(
}

async fn remove_key(
_: LoginUser,
user: LoginUser,
state: State<MonoApiServiceState>,
Path(key_id): Path<i64>,
) -> Result<Json<CommonResult<String>>, (StatusCode, String)> {
) -> Result<Json<CommonResult<String>>, ApiError> {
let res = state
.context
.services
.user_storage
.delete_ssh_key(key_id)
.delete_ssh_key(user.user_id, key_id)
.await;
let res = match res {
Ok(_) => CommonResult::success(None),
Expand All @@ -76,7 +75,7 @@ async fn remove_key(
async fn list_key(
user: LoginUser,
state: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<Vec<ListSSHKey>>>, (StatusCode, String)> {
) -> Result<Json<CommonResult<Vec<ListSSHKey>>>, ApiError> {
let res = state
.context
.services
Expand Down
5 changes: 1 addition & 4 deletions mono/src/server/https_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,7 @@ pub async fn app(config: Config, host: String, port: u16, common: CommonOptions)
)
.nest("/auth", oauth::routers().with_state(api_state.clone()))
// Using Regular Expressions for Path Matching in Protocol
.route(
"/*path",
get(get_method_router).post(post_method_router),
)
.route("/*path", get(get_method_router).post(post_method_router))
.layer(
ServiceBuilder::new().layer(CorsLayer::new().allow_origin(Any).allow_headers(vec![
http::header::AUTHORIZATION,
Expand Down
Loading