Move more of chartered-git configuration to the actual config
Diff
Cargo.lock | 40 ++++++++++++++++++++++------------------
chartered-git/src/config.rs | 22 ++++++++++++++++++++++
chartered-git/src/main.rs | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
book/src/guide/config-reference.md | 38 ++++++++++++++++++++++++++++++++++++++
chartered-git/src/git/packfile/high_level.rs | 6 +++---
5 files changed, 223 insertions(+), 148 deletions(-)
@@ -376,9 +376,9 @@
[[package]]
name = "bumpalo"
version = "3.7.1"
version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]]
name = "byteorder"
@@ -1017,9 +1017,9 @@
[[package]]
name = "h2"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c06815895acec637cd6ed6e9662c935b866d20a106f8361892893a7d9234964"
checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55"
dependencies = [
"bytes",
"fnv",
@@ -1112,9 +1112,9 @@
[[package]]
name = "http-body"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5"
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
dependencies = [
"bytes",
"http",
@@ -1135,9 +1135,9 @@
[[package]]
name = "hyper"
version = "0.14.13"
version = "0.14.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593"
checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b"
dependencies = [
"bytes",
"futures-channel",
@@ -1279,9 +1279,9 @@
[[package]]
name = "libc"
version = "0.2.104"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce"
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
[[package]]
name = "libsodium-sys"
@@ -1731,9 +1731,9 @@
[[package]]
name = "pkg-config"
version = "0.3.20"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
[[package]]
name = "poly1305"
@@ -1748,9 +1748,9 @@
[[package]]
name = "ppv-lite86"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]]
name = "pq-sys"
@@ -1799,9 +1799,9 @@
[[package]]
name = "proc-macro2"
version = "1.0.30"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
dependencies = [
"unicode-xid",
]
@@ -2333,9 +2333,9 @@
[[package]]
name = "syn"
version = "1.0.80"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
dependencies = [
"proc-macro2",
"quote",
@@ -2590,9 +2590,9 @@
[[package]]
name = "tower"
version = "0.4.9"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d15a6b60cdff0cb039d81d3b37f8bc3d7e53dca09069aae3ef2502ca4834fe30"
checksum = "c00e500fff5fa1131c866b246041a6bf96da9c965f8fe4128cb1421f23e93c00"
dependencies = [
"futures-core",
"futures-util",
@@ -1,9 +1,31 @@
use serde::Deserialize;
use std::net::SocketAddr;
use url::Url;
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub bind_address: SocketAddr,
pub database_uri: String,
pub web_base_uri: Url,
#[serde(default)]
pub committer: GitCommitter,
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GitCommitter {
pub name: String,
pub email: String,
pub message: String,
}
impl Default for GitCommitter {
fn default() -> Self {
Self {
name: "chartered".to_string(),
email: "noreply@chart.rs".to_string(),
message: "Update crates".to_string(),
}
}
}
@@ -30,8 +30,15 @@
use thrussh_keys::{key, PublicKeyBase64};
use tokio_util::codec::{Decoder, Encoder as TokioEncoder};
use tracing::{debug, error, info, warn, Instrument};
use url::Url;
const AGENT: &str = concat!(
"agent=",
clap::crate_name!(),
"/",
clap::crate_version!(),
"\n"
);
#[derive(Parser)]
#[clap(version = clap::crate_version!(), author = clap::crate_authors!())]
pub struct Opts {
@@ -65,13 +72,16 @@
..thrussh::server::Config::default()
});
let bind_address = config.bind_address;
let server = Server {
db: chartered_db::init(&config.database_uri)?,
config: Arc::new(config),
};
info!("SSH server listening on {}", config.bind_address);
info!("SSH server listening on {}", bind_address);
thrussh::server::run(trussh_config, &config.bind_address.to_string(), server).await?;
thrussh::server::run(trussh_config, &bind_address.to_string(), server).await?;
Ok(())
}
@@ -79,6 +89,7 @@
#[derive(Clone)]
struct Server {
db: chartered_db::ConnectionPool,
config: Arc<config::Config>,
}
impl server::Server for Server {
@@ -93,6 +104,7 @@
Handler {
ip,
span,
config: self.config.clone(),
codec: GitCodec::default(),
input_bytes: BytesMut::default(),
output_bytes: BytesMut::default(),
@@ -108,6 +120,7 @@
ip: Option<std::net::SocketAddr>,
span: tracing::Span,
codec: GitCodec,
config: Arc<config::Config>,
input_bytes: BytesMut,
output_bytes: BytesMut,
db: chartered_db::ConnectionPool,
@@ -167,117 +180,14 @@
fn finished(self, s: Session) -> Self::FutureUnit {
Box::pin(futures::future::ready(Ok((self, s))))
}
fn env_request(
mut self,
_channel: ChannelId,
name: &str,
value: &str,
session: Session,
) -> Self::FutureUnit {
self.span.in_scope(|| debug!("env set {}={}", name, value));
#[allow(clippy::single_match)]
match (name, value) {
("GIT_PROTOCOL", "version=2") => self.is_git_protocol_v2 = true,
_ => {}
}
Box::pin(futures::future::ready(Ok((self, session))))
}
fn shell_request(mut self, channel: ChannelId, mut session: Session) -> Self::FutureUnit {
let span = self.span.clone();
Box::pin(async move {
error!("Client attempted to open a shell, closing connection");
let username = self.authed()?.user.username.clone();
write!(&mut self.output_bytes, "Hi there, {}! You've successfully authenticated, but chartered does not provide shell access.\r\n", username)?;
self.flush(&mut session, channel);
session.close(channel);
Ok((self, session))
}.instrument(tracing::info_span!(parent: span, "shell request")))
}
fn exec_request(
mut self,
channel: ChannelId,
data: &[u8],
mut session: Session,
) -> Self::FutureUnit {
let span = self.span.clone();
let data = match std::str::from_utf8(data) {
Ok(data) => data,
Err(e) => return Box::pin(futures::future::err(e.into())),
};
let args = shlex::split(data);
Box::pin(async move {
debug!("exec {:?}", args);
if !self.is_git_protocol_v2 {
anyhow::bail!("not git protocol v2");
}
let mut args = args.into_iter().flat_map(Vec::into_iter);
if args.next().as_deref() != Some("git-upload-pack") {
anyhow::bail!("not git-upload-pack");
}
if let Some(org) = args.next().filter(|v| v.as_str() != "/") {
let org = org
.trim_start_matches('/')
.trim_end_matches('/')
.to_string();
self.organisation = Some(org);
} else {
session.extended_data(channel, 1, CryptoVec::from_slice(indoc::indoc! {b"
\r\nNo organisation was given in the path part of the SSH URI. A chartered registry should be defined in your .cargo/config.toml as follows:
[registries]
chartered = {{ index = \"ssh://domain.to.registry.com/my-organisation\" }}\r\n
"}));
session.close(channel);
}
self.write(PktLine::Data(b"version 2\n"))?;
self.write(PktLine::Data(b"agent=chartered/0.1.0\n"))?;
self.write(PktLine::Data(b"ls-refs=unborn\n"))?;
self.write(PktLine::Data(b"fetch=shallow wait-for-done\n"))?;
self.write(PktLine::Data(b"server-option\n"))?;
self.write(PktLine::Data(b"object-info\n"))?;
self.write(PktLine::Flush)?;
self.flush(&mut session, channel);
Ok((self, session))
}.instrument(tracing::info_span!(parent: span, "exec")))
fn auth_none(self, _user: &str) -> Self::FutureAuth {
self.finished_auth(server::Auth::UnsupportedMethod)
}
fn subsystem_request(
self,
_channel: ChannelId,
_data: &str,
session: Session,
) -> Self::FutureUnit {
Box::pin(futures::future::ready(Ok((self, session))))
fn auth_password(self, _user: &str, _password: &str) -> Self::FutureAuth {
self.finished_auth(server::Auth::UnsupportedMethod)
}
@@ -322,17 +232,9 @@
_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 {
let span = self.span.clone();
self.input_bytes.extend_from_slice(data);
@@ -361,11 +263,8 @@
let mut packfile = GitRepository::default();
let config = CargoConfig::new(
&Url::parse("http://127.0.0.1:8888/")?,
&authed.auth_key,
org_name,
);
let config =
CargoConfig::new(&self.config.web_base_uri, &authed.auth_key, org_name);
let config = serde_json::to_vec(&config)?;
packfile.insert(ArrayVec::<_, 0>::new(), "config.json", &config)?;
@@ -377,10 +276,15 @@
Tree::build(self.db.clone(), authed.user.id, org_name.to_string()).await;
tree.write_to_packfile(&mut packfile)?;
let config = self.config.clone();
let (commit_hash, packfile_entries) =
packfile.commit("computer", "john@computer.no", "Update crates")?;
let (commit_hash, packfile_entries) = packfile.commit(
&config.committer.name,
&config.committer.email,
&config.committer.message,
)?;
match frame.command.as_ref() {
b"command=ls-refs" => {
@@ -414,5 +318,116 @@
}
.instrument(tracing::info_span!(parent: span, "data")),
)
}
fn env_request(
mut self,
_channel: ChannelId,
name: &str,
value: &str,
session: Session,
) -> Self::FutureUnit {
self.span.in_scope(|| debug!("env set {}={}", name, value));
#[allow(clippy::single_match)]
match (name, value) {
("GIT_PROTOCOL", "version=2") => self.is_git_protocol_v2 = true,
_ => {}
}
Box::pin(futures::future::ready(Ok((self, session))))
}
fn shell_request(mut self, channel: ChannelId, mut session: Session) -> Self::FutureUnit {
let span = self.span.clone();
Box::pin(async move {
error!("Client attempted to open a shell, closing connection");
let username = self.authed()?.user.username.clone();
write!(&mut self.output_bytes, "Hi there, {}! You've successfully authenticated, but chartered does not provide shell access.\r\n", username)?;
self.flush(&mut session, channel);
session.close(channel);
Ok((self, session))
}.instrument(tracing::info_span!(parent: span, "shell request")))
}
fn exec_request(
mut self,
channel: ChannelId,
data: &[u8],
mut session: Session,
) -> Self::FutureUnit {
let span = self.span.clone();
let data = match std::str::from_utf8(data) {
Ok(data) => data,
Err(e) => return Box::pin(futures::future::err(e.into())),
};
let args = shlex::split(data);
Box::pin(async move {
debug!("exec {:?}", args);
if !self.is_git_protocol_v2 {
anyhow::bail!("not git protocol v2");
}
let mut args = args.into_iter().flat_map(Vec::into_iter);
if args.next().as_deref() != Some("git-upload-pack") {
anyhow::bail!("not git-upload-pack");
}
if let Some(org) = args.next().filter(|v| v.as_str() != "/") {
let org = org
.trim_start_matches('/')
.trim_end_matches('/')
.to_string();
self.organisation = Some(org);
} else {
session.extended_data(channel, 1, CryptoVec::from_slice(indoc::indoc! {b"
\r\nNo organisation was given in the path part of the SSH URI. A chartered registry should be defined in your .cargo/config.toml as follows:
[registries]
chartered = {{ index = \"ssh://domain.to.registry.com/my-organisation\" }}\r\n
"}));
session.close(channel);
}
self.write(PktLine::Data(b"version 2\n"))?;
self.write(PktLine::Data(AGENT.as_bytes()))?;
self.write(PktLine::Data(b"ls-refs=unborn\n"))?;
self.write(PktLine::Data(b"fetch=shallow wait-for-done\n"))?;
self.write(PktLine::Data(b"server-option\n"))?;
self.write(PktLine::Data(b"object-info\n"))?;
self.write(PktLine::Flush)?;
self.flush(&mut session, channel);
Ok((self, session))
}.instrument(tracing::info_span!(parent: span, "exec")))
}
fn subsystem_request(
self,
_channel: ChannelId,
_data: &str,
session: Session,
) -> Self::FutureUnit {
Box::pin(futures::future::ready(Ok((self, session))))
}
}
@@ -12,6 +12,12 @@
```toml
bind_address = "127.0.0.1:2233"
database_uri = "postgres://user:password@localhost/chartered" # can also be `sqlite://`
web_base_uri = "http://localhost:8888/"
[committer]
name = "Chartered"
email = "noreply@chart.rs"
message = "Updated crates!"
```
### Configuration keys
@@ -29,6 +35,38 @@
`sqlite:///path/to/chartered.db` or `sqlite://:memory:`.
[pg-uri]: https://www.postgresql.org/docs/9.4/libpq-connect.html#LIBPQ-CONNSTRING
#### `web_base_uri`
- Type: string
The path at which the Chartered API (`chartered-web`) is running. This should _always_ be HTTPS when
running in production.
#### `committer`
The `committer` table defines the author of the commit that's sent to the
user.
##### `name`
- Type: string
- Default: `chartered`
The name of the committer for any commits being created by `chartered-git`.
##### `email`
- Type: string
- Default: `noreply@chart.rs`
The email address to list for the author of the commit pushed to the user
##### `message`
- Type: string
- Default: `Update crates`
The commit message to use for any commits sent out.
---
@@ -80,9 +80,9 @@
pub fn commit(
&'a mut self,
name: &'static str,
email: &'static str,
message: &'static str,
name: &'a str,
email: &'a str,
message: &'a str,
) -> Result<(HashOutput, Vec<PackFileEntry<'a>>), anyhow::Error> {
let tree_hash = self.tree.to_packfile_entries(&mut self.packfile_entries)?;