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
2 changes: 2 additions & 0 deletions ceres/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ ring = { workspace = true }
hex = { workspace = true }
sysinfo = { workspace = true }
utoipa = { workspace = true }
base64 = { workspace = true }
http = { workspace = true }
4 changes: 3 additions & 1 deletion ceres/src/model/mr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use mercury::hash::SHA1;

#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
Expand Down
41 changes: 32 additions & 9 deletions ceres/src/pack/monorepo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ pub struct MonoRepo {
pub from_hash: String,
pub to_hash: String,
// current_commit only exists when an unpack operation occurs.
// When only a branch is updated and the pack file is empty, this value will be null.
// When only a branch is updated and the pack file is empty, this value will be None.
pub current_commit: Arc<RwLock<Option<Commit>>>,
pub mr_link: Arc<RwLock<Option<String>>>,
pub bellatrix: Arc<Bellatrix>,
pub username: Option<String>,
}

#[async_trait]
Expand Down Expand Up @@ -311,18 +312,30 @@ impl RepoHandler for MonoRepo {
async fn save_or_update_mr(&self) -> Result<(), MegaError> {
let storage = self.storage.mr_storage();
let path_str = self.path.to_str().unwrap();
let username = self.username();

match storage.get_open_mr_by_path(path_str).await? {
match storage.get_open_mr_by_path(path_str, &username).await? {
Some(mr) => {
self.update_existing_mr(mr).await?;
}
None => {
let link_guard = self.mr_link.read().await;
let mr_link = link_guard.as_ref().unwrap();
let commit_guard = self.current_commit.read().await;
let title = commit_guard.as_ref().unwrap().format_message();
let title = if let Some(commit) = commit_guard.as_ref() {
commit.format_message()
} else {
String::new()
};
storage
.new_mr(path_str, mr_link, &title, &self.from_hash, &self.to_hash)
.new_mr(
path_str,
mr_link,
&title,
&self.from_hash,
&self.to_hash,
&username,
)
.await?;
}
};
Expand Down Expand Up @@ -377,7 +390,11 @@ impl MonoRepo {
async fn fetch_or_new_mr_link(&self) -> Result<String, MegaError> {
let storage = self.storage.mr_storage();
let path_str = self.path.to_str().unwrap();
let mr_link = match storage.get_open_mr_by_path(path_str).await.unwrap() {
let mr_link = match storage
.get_open_mr_by_path(path_str, &self.username())
.await
.unwrap()
{
Some(mr) => mr.link.clone(),
None => {
if self.from_hash == "0".repeat(40) {
Expand All @@ -401,9 +418,10 @@ impl MonoRepo {
comment_stg
.add_conversation(
&mr.link,
"",
&self.username(),
Some(format!(
"Mega updated the mr automatic from {} to {}",
"{} updated the mr automatic from {} to {}",
self.username(),
&mr.to_hash[..6],
&self.to_hash[..6]
)),
Expand All @@ -415,11 +433,12 @@ impl MonoRepo {
tracing::info!("repeat commit with mr: {}, do nothing", mr.id);
}
} else {
// TODO 直接覆盖?

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is in Chinese and appears to be a TODO item asking about direct overwriting. Comments should be in English for consistency and the TODO should be clarified or addressed.

Suggested change
// TODO 直接覆盖?
// TODO: Should we directly overwrite the existing MR in case of a hash conflict? Review and implement appropriate logic if needed.

Copilot uses AI. Check for mistakes.
comment_stg
.add_conversation(
&mr.link,
"",
Some("Mega closed MR due to conflict".to_string()),
&self.username(),
Some(format!("{} closed MR due to conflict", self.username(),)),
ConvTypeEnum::Closed,
)
.await
Expand Down Expand Up @@ -510,6 +529,10 @@ impl MonoRepo {
let to_tree: Tree = mono_stg.get_tree_by_hash(&to_c.tree).await?.unwrap().into();
diff_trees(&to_tree, &from_tree)
}

pub fn username(&self) -> String {
self.username.clone().unwrap_or(String::from("Admin"))

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a hardcoded fallback username 'Admin' could lead to confusion in logs and audit trails. Consider using a more descriptive default like 'system' or 'unknown', or better yet, make username required to avoid ambiguity.

Suggested change
self.username.clone().unwrap_or(String::from("Admin"))
self.username.clone().unwrap_or(String::from("system"))

Copilot uses AI. Check for mistakes.
}
}

type DiffResult = Vec<(PathBuf, Option<SHA1>, Option<SHA1>)>;
Expand Down
49 changes: 48 additions & 1 deletion ceres/src/protocol/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use core::fmt;
use std::{path::PathBuf, str::FromStr, sync::Arc};

use base64::engine::general_purpose;
use base64::prelude::*;
use http::{HeaderMap, HeaderValue};
use tokio::sync::RwLock;

use bellatrix::Bellatrix;
use callisto::sea_orm_active_enums::RefTypeEnum;
use common::{
Expand All @@ -10,7 +15,6 @@ use common::{
use import_refs::RefCommand;
use jupiter::storage::Storage;
use repo::Repo;
use tokio::sync::RwLock;

use crate::pack::{import_repo::ImportRepo, monorepo::MonoRepo, RepoHandler};

Expand All @@ -26,6 +30,7 @@ pub struct SmartProtocol {
pub command_list: Vec<RefCommand>,
pub service_type: Option<ServiceType>,
pub storage: Storage,
pub username: Option<String>,
}

#[derive(Debug, PartialEq, Clone, Copy, Default)]
Expand Down Expand Up @@ -134,6 +139,7 @@ impl SmartProtocol {
command_list: Vec::new(),
service_type: None,
storage,
username: None,
}
}

Expand All @@ -146,6 +152,7 @@ impl SmartProtocol {
command_list: Vec::new(),
service_type: None,
storage,
username: None,
}
}

Expand Down Expand Up @@ -183,6 +190,7 @@ impl SmartProtocol {
current_commit: Arc::new(RwLock::new(None)),
mr_link: Arc::new(RwLock::new(None)),
bellatrix: Arc::new(Bellatrix::new(self.storage.config().build.clone())),
username: self.username.clone(),
};
if let Some(command) = self
.command_list
Expand All @@ -195,6 +203,45 @@ impl SmartProtocol {
Ok(Arc::new(res))
}
}

pub fn enable_http_auth(&self) -> bool {
self.storage.config().enable_http_auth()
}

pub async fn http_auth(&mut self, header: &HeaderMap<HeaderValue>) -> bool {
for (k, v) in header {
if k == http::header::AUTHORIZATION {
let decoded = general_purpose::STANDARD

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The base64 decoding and string parsing operations use .unwrap() which will panic on malformed input. This creates a security vulnerability where malformed Authorization headers can crash the service.

Copilot uses AI. Check for mistakes.
.decode(
v.to_str()
.unwrap()
.strip_prefix("Basic ")
.unwrap()
.as_bytes(),
)
.unwrap();
let credentials = String::from_utf8(decoded).unwrap_or_default();
let mut parts = credentials.splitn(2, ':');
let username = parts.next().unwrap_or("");
self.username = Some(username.to_owned());
let token = parts.next().unwrap_or("");
let auth_config = self.storage.config().authentication.clone();
if auth_config.enable_test_user
&& username == auth_config.test_user_name
&& token == auth_config.test_user_token
{
return true;
}
return self
.storage
.user_storage()
.check_token(username, token)
.await
.unwrap();
}
}
false
}
}

#[cfg(test)]
Expand Down
4 changes: 4 additions & 0 deletions common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ impl Config {
pub fn from_config(config: c::Config) -> Result<Self, ConfigError> {
config.try_deserialize::<Config>()
}

pub fn enable_http_auth(&self) -> bool {
self.authentication.enable_http_auth
}
}

impl Default for Config {
Expand Down
2 changes: 1 addition & 1 deletion config/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ root_dirs = ["third-party", "project", "doc", "release", "model"]
[build]

# enable build system trigger
enable_build = true
enable_build = false

# build system url
orion_server = "https://orion.gitmega.com"
Expand Down
2 changes: 1 addition & 1 deletion jupiter/callisto/src/access_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: i64,
pub user_id: String,
pub username: String,
#[sea_orm(column_type = "Text")]
pub token: String,
pub created_at: DateTime,
Expand Down
2 changes: 1 addition & 1 deletion jupiter/callisto/src/builds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub struct Model {
pub output: String,
pub exit_code: Option<i32>,
pub start_at: DateTime,
pub end_at: DateTime,
pub end_at: Option<DateTime>,
pub repo_name: String,
pub target: String,
}
Expand Down
2 changes: 2 additions & 0 deletions jupiter/callisto/src/entity_ext/mega_mr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ impl mega_mr::Model {
link: String,
from_hash: String,
to_hash: String,
username: String,
) -> Self {
let now = chrono::Utc::now().naive_utc();
Self {
Expand All @@ -71,6 +72,7 @@ impl mega_mr::Model {
path,
from_hash,
to_hash,
username,
}
}
}
1 change: 1 addition & 0 deletions jupiter/callisto/src/mega_mr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct Model {
pub to_hash: String,
pub created_at: DateTime,
pub updated_at: DateTime,
pub username: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
Expand Down
2 changes: 1 addition & 1 deletion jupiter/callisto/src/ssh_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: i64,
pub user_id: String,
pub username: String,
#[sea_orm(column_type = "Text")]
pub title: String,
#[sea_orm(column_type = "Text")]
Expand Down
68 changes: 68 additions & 0 deletions jupiter/src/migration/m20250812_022434_alter_mega_mr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use sea_orm::{DatabaseBackend, Statement};
use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let backend = manager.get_database_backend();

match backend {
DatabaseBackend::Postgres | DatabaseBackend::MySql => {
manager
.alter_table(
Table::alter()
.table(MegaMr::Table)
.add_column_if_not_exists(
ColumnDef::new(Alias::new("username"))
.string()
.not_null()
.default(""),
)
.to_owned(),
)
.await?;

manager
.get_connection()
.execute(Statement::from_string(
manager.get_database_backend(),
"ALTER TABLE access_token RENAME COLUMN user_id TO username".to_owned(),
))
.await?;

manager
.get_connection()
.execute(Statement::from_string(
manager.get_database_backend(),
"ALTER TABLE ssh_keys RENAME COLUMN user_id TO username".to_owned(),
))
.await?;
}
DatabaseBackend::Sqlite => {}

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration skips SQLite implementation entirely. This creates inconsistent behavior across database backends and could cause issues for developers using SQLite in development or testing environments.

Suggested change
DatabaseBackend::Sqlite => {}
DatabaseBackend::Sqlite => {
// Add 'username' column to mega_mr table if it does not exist
manager
.get_connection()
.execute(Statement::from_string(
manager.get_database_backend(),
"ALTER TABLE mega_mr ADD COLUMN username TEXT NOT NULL DEFAULT ''".to_owned(),
))
.await?;
// Rename 'user_id' to 'username' in access_token table
manager
.get_connection()
.execute(Statement::from_string(
manager.get_database_backend(),
"ALTER TABLE access_token RENAME COLUMN user_id TO username".to_owned(),
))
.await?;
// Rename 'user_id' to 'username' in ssh_keys table
manager
.get_connection()
.execute(Statement::from_string(
manager.get_database_backend(),
"ALTER TABLE ssh_keys RENAME COLUMN user_id TO username".to_owned(),
))
.await?;
}

Copilot uses AI. Check for mistakes.
}

Ok(())
}

async fn down(&self, _: &SchemaManager) -> Result<(), DbErr> {
Ok(())
}
}

#[derive(DeriveIden)]
enum MegaMr {
Table,
}

// #[derive(DeriveIden)]
// enum AccessToken {
// Table,
// }

// #[derive(DeriveIden)]
// enum SshKeys {
// Table,
// }
2 changes: 2 additions & 0 deletions jupiter/src/migration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod m20250702_072055_add_item_assignees;
mod m20250710_073119_create_reactions;
mod m20250725_103004_add_note;
mod m20250804_151214_alter_builds_end_at;
mod m20250812_022434_alter_mega_mr;
/// Creates a primary key column definition with big integer type.
///
/// # Arguments
Expand Down Expand Up @@ -79,6 +80,7 @@ impl MigratorTrait for Migrator {
Box::new(m20250710_073119_create_reactions::Migration),
Box::new(m20250725_103004_add_note::Migration),
Box::new(m20250804_151214_alter_builds_end_at::Migration),
Box::new(m20250812_022434_alter_mega_mr::Migration),
]
}
}
Expand Down
4 changes: 4 additions & 0 deletions jupiter/src/storage/mr_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ impl MrStorage {
pub async fn get_open_mr_by_path(
&self,
path: &str,
username: &str,
) -> Result<Option<mega_mr::Model>, MegaError> {
let model = mega_mr::Entity::find()
.filter(mega_mr::Column::Path.eq(path))
.filter(mega_mr::Column::Username.eq(username))
.filter(mega_mr::Column::Status.eq(MergeStatusEnum::Open))
.one(self.get_connection())
.await
Expand Down Expand Up @@ -178,13 +180,15 @@ impl MrStorage {
title: &str,
from_hash: &str,
to_hash: &str,
username: &str,
) -> Result<String, MegaError> {
let model = mega_mr::Model::new(
path.to_owned(),
title.to_owned(),
link.to_owned(),
from_hash.to_owned(),
to_hash.to_owned(),
username.to_owned(),
);
let res = model
.into_active_model()
Expand Down
Loading
Loading