implement ldap support

This commit is contained in:
SeanOMik 2023-05-28 23:47:22 -04:00
parent d98a8e3790
commit be59462741
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
16 changed files with 697 additions and 125 deletions

339
Cargo.lock generated
View File

@ -100,7 +100,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
"winapi",
]
@ -357,6 +357,16 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
@ -503,12 +513,42 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
name = "figment"
version = "0.10.8"
@ -551,6 +591,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
@ -723,6 +778,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "hex"
version = "0.4.3"
@ -869,6 +930,17 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi 0.3.1",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -930,10 +1002,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.137"
name = "lber"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
checksum = "b5d85f5e00e12cb50c70c3b1c1f0daff6546eb4c608b44d0a990e38a539e0446"
dependencies = [
"bytes",
"nom",
]
[[package]]
name = "ldap3"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5cfbd3c59ca16d6671b002b8b3dd013cd825d9c77a1664a3135194d3270511e"
dependencies = [
"async-trait",
"bytes",
"futures",
"futures-util",
"lazy_static",
"lber",
"log",
"native-tls",
"nom",
"percent-encoding",
"thiserror",
"tokio",
"tokio-native-tls",
"tokio-stream",
"tokio-util",
"url",
]
[[package]]
name = "libc"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "libsqlite3-sys"
@ -955,6 +1061,12 @@ dependencies = [
"cc",
]
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "lock_api"
version = "0.4.9"
@ -1007,7 +1119,25 @@ dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
"windows-sys 0.42.0",
]
[[package]]
name = "native-tls"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
@ -1055,7 +1185,7 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
]
@ -1071,6 +1201,50 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote 1.0.26",
"syn 2.0.15",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "orca-registry"
version = "0.1.0"
@ -1093,6 +1267,7 @@ dependencies = [
"hmac",
"jws",
"jwt",
"ldap3",
"pin-project-lite",
"qstring",
"rand",
@ -1371,6 +1546,20 @@ dependencies = [
"winapi",
]
[[package]]
name = "rustix"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.42.0",
]
[[package]]
name = "rustls"
version = "0.20.8"
@ -1404,6 +1593,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "schannel"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
dependencies = [
"windows-sys 0.42.0",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -1426,6 +1624,29 @@ dependencies = [
"untrusted",
]
[[package]]
name = "security-framework"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "serde"
version = "1.0.147"
@ -1739,6 +1960,19 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"windows-sys 0.42.0",
]
[[package]]
name = "termcolor"
version = "1.1.3"
@ -1819,7 +2053,7 @@ dependencies = [
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys",
"windows-sys 0.42.0",
]
[[package]]
@ -1833,6 +2067,16 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.23.4"
@ -1866,6 +2110,7 @@ dependencies = [
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
@ -2245,13 +2490,37 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.42.0",
"windows_aarch64_msvc 0.42.0",
"windows_i686_gnu 0.42.0",
"windows_i686_msvc 0.42.0",
"windows_x86_64_gnu 0.42.0",
"windows_x86_64_gnullvm 0.42.0",
"windows_x86_64_msvc 0.42.0",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
@ -2260,42 +2529,84 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "yansi"
version = "0.5.1"

View File

@ -51,3 +51,4 @@ sha2 = "0.10.6"
rand = "0.8.5"
bcrypt = "0.14.0"
bitflags = "2.2.1"
ldap3 = "0.11.1"

View File

@ -8,5 +8,7 @@ pkgs.mkShell {
postgresql
sqlite
diesel-cli
openssl
pkg-config
];
}

View File

@ -15,7 +15,7 @@ use rand::Rng;
use crate::{dto::{scope::Scope, user::{UserAuth, TokenInfo}}, app_state::AppState};
use crate::database::Database;
use crate::auth_storage::{unauthenticated_response, AuthDriver};
use crate::auth::{unauthenticated_response, AuthDriver};
#[derive(Deserialize, Debug)]
pub struct TokenAuthRequest {
@ -167,7 +167,7 @@ pub async fn auth_basic_get(basic_auth: Option<AuthBasic>, state: State<Arc<AppS
if let (Some(account), Some(password)) = (&auth.account, auth.password) {
// Ensure that the password is correct
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.verify_user_login(account.clone(), password).await.unwrap() {
debug!("Authentication failed, incorrect password!");

View File

@ -8,14 +8,14 @@ use axum::response::{IntoResponse, Response};
use tokio_util::io::ReaderStream;
use crate::app_state::AppState;
use crate::auth_storage::{unauthenticated_response, AuthDriver};
use crate::auth::{unauthenticated_response, AuthDriver};
use crate::database::Database;
use crate::dto::RepositoryVisibility;
use crate::dto::user::{Permission, RegistryUserType, UserAuth};
pub async fn digest_exists_head(Path((name, layer_digest)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
// Check if the user has permission to pull, or that the repository is public
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
return unauthenticated_response(&state.config);
}
@ -40,7 +40,7 @@ pub async fn digest_exists_head(Path((name, layer_digest)): Path<(String, String
pub async fn pull_digest_get(Path((name, layer_digest)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
// Check if the user has permission to pull, or that the repository is public
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
return unauthenticated_response(&state.config);
}

View File

@ -7,7 +7,7 @@ use axum::http::{StatusCode, HeaderMap, HeaderName, header};
use tracing::log::warn;
use tracing::{debug, info};
use crate::auth_storage::{unauthenticated_response, AuthDriver};
use crate::auth::{unauthenticated_response, AuthDriver};
use crate::app_state::AppState;
use crate::database::Database;
use crate::dto::RepositoryVisibility;
@ -16,7 +16,7 @@ use crate::dto::manifest::Manifest;
use crate::dto::user::{UserAuth, Permission};
pub async fn upload_manifest_put(Path((name, reference)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>, body: String) -> Response {
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
return unauthenticated_response(&state.config);
}
@ -65,7 +65,7 @@ pub async fn upload_manifest_put(Path((name, reference)): Path<(String, String)>
pub async fn pull_manifest_get(Path((name, reference)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
// Check if the user has permission to pull, or that the repository is public
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
return unauthenticated_response(&state.config);
}
@ -108,7 +108,7 @@ pub async fn pull_manifest_get(Path((name, reference)): Path<(String, String)>,
pub async fn manifest_exists_head(Path((name, reference)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
// Check if the user has permission to pull, or that the repository is public
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
return unauthenticated_response(&state.config);
}
@ -148,7 +148,7 @@ pub async fn manifest_exists_head(Path((name, reference)): Path<(String, String)
}
pub async fn delete_manifest(Path((name, reference)): Path<(String, String)>, headers: HeaderMap, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
return unauthenticated_response(&state.config);
}

View File

@ -14,7 +14,7 @@ pub mod tags;
pub mod catalog;
pub mod auth;
use crate::auth_storage::AuthToken;
use crate::auth::AuthToken;
use crate::dto::user::UserAuth;
/// https://docs.docker.com/registry/spec/api/#api-version-check

View File

@ -12,14 +12,14 @@ use futures::StreamExt;
use tracing::{debug, warn};
use crate::app_state::AppState;
use crate::auth_storage::{unauthenticated_response, AuthDriver};
use crate::auth::{unauthenticated_response, AuthDriver};
use crate::byte_stream::ByteStream;
use crate::database::Database;
use crate::dto::user::{UserAuth, Permission, RegistryUser, RegistryUserType};
/// Starting an upload
pub async fn start_upload_post(Path((name, )): Path<(String, )>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>) -> Response {
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
debug!("Upload requested");
let uuid = uuid::Uuid::new_v4();
@ -39,7 +39,7 @@ pub async fn start_upload_post(Path((name, )): Path<(String, )>, Extension(auth)
}
pub async fn chunked_upload_layer_patch(Path((name, layer_uuid)): Path<(String, String)>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>, mut body: BodyStream) -> Response {
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
return unauthenticated_response(&state.config);
}
@ -98,7 +98,7 @@ pub async fn chunked_upload_layer_patch(Path((name, layer_uuid)): Path<(String,
}
pub async fn finish_chunked_upload_put(Path((name, layer_uuid)): Path<(String, String)>, Query(query): Query<HashMap<String, String>>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>, body: Bytes) -> Response {
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
return unauthenticated_response(&state.config);
}
@ -127,7 +127,7 @@ pub async fn finish_chunked_upload_put(Path((name, layer_uuid)): Path<(String, S
}
pub async fn cancel_upload_delete(Path((name, layer_uuid)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
return unauthenticated_response(&state.config);
}
@ -141,7 +141,7 @@ pub async fn cancel_upload_delete(Path((name, layer_uuid)): Path<(String, String
}
pub async fn check_upload_status_get(Path((name, layer_uuid)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
let auth_driver = state.auth_checker.lock().await;
let mut auth_driver = state.auth_checker.lock().await;
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
return unauthenticated_response(&state.config);
}

View File

@ -1,6 +1,6 @@
use sqlx::{Sqlite, Pool};
use crate::auth_storage::AuthDriver;
use crate::auth::AuthDriver;
use crate::storage::StorageDriver;
use crate::config::Config;

145
src/auth/ldap_driver.rs Normal file
View File

@ -0,0 +1,145 @@
use std::{slice::Iter, iter::Peekable};
use async_trait::async_trait;
use ldap3::{LdapConnAsync, Ldap, Scope, asn1::PL, ResultEntry, SearchEntry};
use sqlx::{Pool, Sqlite};
use tracing::{debug, warn};
use crate::{config::LdapConnectionConfig, dto::{user::{Permission, LoginSource}, RepositoryVisibility}, database::Database};
use super::AuthDriver;
pub struct LdapAuthDriver {
ldap_config: LdapConnectionConfig,
ldap: Ldap,
database: Pool<Sqlite>,
}
impl LdapAuthDriver {
pub async fn new(config: LdapConnectionConfig, database: Pool<Sqlite>) -> anyhow::Result<Self> {
debug!("connecting to ldap");
let (conn, ldap) = LdapConnAsync::new(&config.connection_url).await?;
ldap3::drive!(conn);
debug!("Created ldap connection!");
Ok(Self {
ldap_config: config,
ldap,
database,
})
}
async fn bind(&mut self) -> anyhow::Result<()> {
let res = self.ldap.simple_bind(&self.ldap_config.bind_dn, &self.ldap_config.bind_password).await?;
res.success()?;
Ok(())
}
/* pub async fn verify_login(&mut self, username: &str, password: &str) -> anyhow::Result<bool> {
self.bind().await?;
let filter = self.ldap_config.user_search_filter.replace("%s", &username);
let res = self.ldap.search(&self.ldap_config.user_base_dn, Scope::Subtree, &filter,
vec!["userPassword", "uid", "cn", "mail", "displayName"]).await?;
let (entries, _res) = res.success()?;
let entries: Vec<SearchEntry> = entries
.into_iter()
.map(|e| SearchEntry::construct(e))
.collect();
if entries.is_empty() {
Ok(false)
} else if entries.len() > 1 {
warn!("Got multiple DNs for user ({}), unsure which one to use!!", username);
Ok(false)
} else {
let entry = entries.first().unwrap();
let res = self.ldap.simple_bind(&entry.dn, password).await?;
if res.rc == 0 {
Ok(true)
} else if res.rc == 49 {
warn!("User failed to auth (invalidCredentials, rc=49)!");
Ok(false)
} else {
// this would fail, its just here to propagate the error down
res.success()?;
Ok(false)
}
}
} */
}
#[async_trait]
impl AuthDriver for LdapAuthDriver {
async fn user_has_permission(&mut self, email: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool> {
self.bind().await?;
// Send a request to LDAP to check if the user is an admin
let filter = format!("(&({}={}){})", &self.ldap_config.login_attribute, email, &self.ldap_config.admin_filter);
let res = self.ldap.search(&self.ldap_config.group_base_dn, Scope::Subtree, &filter, vec!["*"]).await?;
let (entries, _res) = res.success()?;
let entries: Vec<SearchEntry> = entries
.into_iter()
.map(|e| SearchEntry::construct(e))
.collect();
if entries.len() > 0 {
Ok(true)
} else {
debug!("LDAP is falling back to database");
// fall back to database auth since this user might be local
self.database.user_has_permission(email, repository, permission, required_visibility).await
}
}
async fn verify_user_login(&mut self, email: String, password: String) -> anyhow::Result<bool> {
self.bind().await?;
let filter = self.ldap_config.user_search_filter.replace("%s", &email);
let res = self.ldap.search(&self.ldap_config.user_base_dn, Scope::Subtree, &filter,
vec!["userPassword", "uid", "cn", "mail", "displayName"]).await?;
let (entries, _res) = res.success()?;
let entries: Vec<SearchEntry> = entries
.into_iter()
.map(|e| SearchEntry::construct(e))
.collect();
if entries.is_empty() {
Ok(false)
} else if entries.len() > 1 {
warn!("Got multiple DNs for user ({}), unsure which one to use!!", email);
Ok(false)
} else {
let entry = entries.first().unwrap();
let res = self.ldap.simple_bind(&entry.dn, &password).await?;
if res.rc == 0 {
// The user was authenticated through ldap!
// Check if the user is stored in the database, if not, add it.
let database = &self.database;
if !database.does_user_exist(email.clone()).await? {
let display_name = entry.attrs.get(&self.ldap_config.display_name_attribute).unwrap().first().unwrap().clone();
database.create_user(email, display_name, LoginSource::LDAP).await?;
}
Ok(true)
} else if res.rc == 49 {
warn!("User failed to auth (invalidCredentials, rc=49)!");
Ok(false)
} else {
// this would fail, its just here to propagate the error down
res.success()?;
Ok(false)
}
}
}
}

View File

@ -1,3 +1,5 @@
pub mod ldap_driver;
use std::{collections::HashSet, ops::Deref, sync::Arc};
use axum::{extract::{State, Path}, http::{StatusCode, HeaderMap, header, HeaderName, Request}, middleware::Next, response::{Response, IntoResponse}};
@ -18,60 +20,51 @@ pub trait AuthDriver: Send + Sync {
/// * `repository`: Name of the repository.
/// * `permissions`: Permission to check for.
/// * `required_visibility`: Specified if there is a specific visibility of the repository that will give the user permission.
async fn user_has_permission(&self, username: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool>;
async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result<bool>;
async fn user_has_permission(&mut self, email: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool>;
async fn verify_user_login(&mut self, email: String, password: String) -> anyhow::Result<bool>;
}
#[async_trait]
impl AuthDriver for Pool<Sqlite> {
async fn user_has_permission(&self, username: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool> {
async fn user_has_permission(&mut self, email: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool> {
let allowed_to = {
match self.get_user_registry_type(username.clone()).await? {
match self.get_user_registry_type(email.clone()).await? {
Some(RegistryUserType::Admin) => true,
_ => {
if let Some(perms) = self.get_user_repo_permissions(username, repository.clone()).await? {
if perms.has_permission(permission) {
return Ok(true);
}
}
if let Some(vis) = required_visibility {
if let Some(repo_vis) = self.get_repository_visibility(&repository).await? {
if vis == repo_vis {
return Ok(true);
}
}
}
false
check_user_permissions(self, email, repository, permission, required_visibility).await?
}
/* match database.get_user_repo_permissions(username, repository).await.unwrap() {
Some(perms) => if perms.has_permission(permission) { true } else { false },
_ => false,
} */
}
};
Ok(allowed_to)
}
async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result<bool> {
Database::verify_user_login(self, username, password).await
async fn verify_user_login(&mut self, email: String, password: String) -> anyhow::Result<bool> {
Database::verify_user_login(self, email, password).await
}
}
/// Temporary struct for storing auth information in memory.
/* pub struct MemoryAuthStorage {
pub valid_tokens: HashSet<String>,
}
impl MemoryAuthStorage {
pub fn new() -> Self {
Self {
valid_tokens: HashSet::new(),
// This ONLY checks permissions, does not check user type
pub async fn check_user_permissions<D>(database: &D, email: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool>
where
D: Database
{
if let Some(perms) = database.get_user_repo_permissions(email, repository.clone()).await? {
if perms.has_permission(permission) {
return Ok(true);
}
}
} */
if let Some(vis) = required_visibility {
if let Some(repo_vis) = database.get_repository_visibility(&repository).await? {
if vis == repo_vis {
return Ok(true);
}
}
}
Ok(false)
}
#[derive(Clone)]
pub struct AuthToken(pub String);

View File

@ -4,11 +4,37 @@ use serde::Deserialize;
use std::env;
#[derive(Deserialize)]
#[derive(Deserialize, Clone)]
pub struct LdapConnectionConfig {
pub connection_url: String,
pub bind_dn: String,
pub bind_password: String,
pub user_base_dn: String,
pub group_base_dn: String,
pub user_search_filter: String,
pub group_search_filter: String,
pub admin_filter: String,
#[serde(default = "default_login_attribute")]
pub login_attribute: String,
#[serde(default = "default_display_name_attribute")]
pub display_name_attribute: String,
}
fn default_login_attribute() -> String {
"mail".to_string()
}
fn default_display_name_attribute() -> String {
"displayName".to_string()
}
#[derive(Deserialize, Clone)]
pub struct Config {
pub listen_address: String,
pub listen_port: String,
pub url: Option<String>,
pub ldap: Option<LdapConnectionConfig>,
}
#[allow(dead_code)]

View File

@ -4,7 +4,7 @@ use tracing::debug;
use chrono::{DateTime, Utc, NaiveDateTime, TimeZone};
use crate::dto::{Tag, user::{User, RepositoryPermissions, RegistryUserType, Permission, UserAuth, TokenInfo}, RepositoryVisibility};
use crate::dto::{Tag, user::{User, RepositoryPermissions, RegistryUserType, Permission, UserAuth, TokenInfo, LoginSource}, RepositoryVisibility};
#[async_trait]
pub trait Database {
@ -50,12 +50,14 @@ pub trait Database {
/// User stuff
async fn create_user(&self, username: String, email: String, password_hash: String, password_salt: String) -> sqlx::Result<User>;
async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result<bool>;
async fn get_user_registry_type(&self, username: String) -> anyhow::Result<Option<RegistryUserType>>;
async fn get_user_repo_permissions(&self, username: String, repository: String) -> anyhow::Result<Option<RepositoryPermissions>>;
async fn get_user_registry_usertype(&self, username: String) -> anyhow::Result<Option<RegistryUserType>>;
async fn store_user_token(&self, token: String, username: String, expiry: DateTime<Utc>, created_at: DateTime<Utc>) -> anyhow::Result<()>;
async fn does_user_exist(&self, email: String) -> sqlx::Result<bool>;
async fn create_user(&self, email: String, username: String, login_source: LoginSource) -> sqlx::Result<User>;
async fn add_user_auth(&self, email: String, password_hash: String, password_salt: String) -> sqlx::Result<()>;
async fn verify_user_login(&self, email: String, password: String) -> anyhow::Result<bool>;
async fn get_user_registry_type(&self, email: String) -> anyhow::Result<Option<RegistryUserType>>;
async fn get_user_repo_permissions(&self, email: String, repository: String) -> anyhow::Result<Option<RepositoryPermissions>>;
async fn get_user_registry_usertype(&self, email: String) -> anyhow::Result<Option<RegistryUserType>>;
async fn store_user_token(&self, token: String, email: String, expiry: DateTime<Utc>, created_at: DateTime<Utc>) -> anyhow::Result<()>;
async fn verify_user_token(&self, token: String) -> anyhow::Result<Option<UserAuth>>;
}
@ -233,8 +235,7 @@ impl Database for Pool<Sqlite> {
}
async fn has_repository(&self, repository: &str) -> sqlx::Result<bool> {
debug!("before query ig");
let row: (u32, ) = match sqlx::query_as("SELECT COUNT(1) repositories WHERE 'name' = ?")
let row: (u32, ) = match sqlx::query_as("SELECT COUNT(1) FROM repositories WHERE \"name\" = ?")
.bind(repository)
.fetch_one(self).await {
Ok(row) => row,
@ -321,33 +322,61 @@ impl Database for Pool<Sqlite> {
Ok(repos)
}
async fn create_user(&self, username: String, email: String, password_hash: String, password_salt: String) -> sqlx::Result<User> {
async fn does_user_exist(&self, email: String) -> sqlx::Result<bool> {
let row: (u32, ) = match sqlx::query_as("SELECT COUNT(1) FROM users WHERE \"email\" = ?")
.bind(email)
.fetch_one(self).await {
Ok(row) => row,
Err(e) => match e {
sqlx::Error::RowNotFound => {
return Ok(false)
},
_ => {
return Err(e);
}
}
};
Ok(row.0 > 0)
}
async fn create_user(&self, email: String, username: String, login_source: LoginSource) -> sqlx::Result<User> {
let username = username.to_lowercase();
let email = email.to_lowercase();
sqlx::query("INSERT INTO users (username, email, password_hash, password_salt) VALUES (?, ?, ?, ?)")
sqlx::query("INSERT INTO users (username, email, login_source) VALUES (?, ?, ?)")
.bind(username.clone())
.bind(email.clone())
.bind(login_source as u32)
.execute(self).await?;
Ok(User::new(username, email, login_source))
}
async fn add_user_auth(&self, email: String, password_hash: String, password_salt: String) -> sqlx::Result<()> {
let email = email.to_lowercase();
sqlx::query("INSERT INTO user_logins (email, password_hash, password_salt) VALUES (?, ?, ?)")
.bind(email.clone())
.bind(password_hash)
.bind(password_salt)
.execute(self).await?;
Ok(User::new(username, email))
Ok(())
}
async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result<bool> {
let username = username.to_lowercase();
let row: (String, ) = sqlx::query_as("SELECT password_hash FROM users WHERE username = ?")
.bind(username)
async fn verify_user_login(&self, email: String, password: String) -> anyhow::Result<bool> {
let email = email.to_lowercase();
let row: (String, ) = sqlx::query_as("SELECT password_hash FROM users WHERE email = ?")
.bind(email)
.fetch_one(self).await?;
Ok(bcrypt::verify(password, &row.0)?)
}
async fn get_user_registry_type(&self, username: String) -> anyhow::Result<Option<RegistryUserType>> {
let username = username.to_lowercase();
async fn get_user_registry_type(&self, email: String) -> anyhow::Result<Option<RegistryUserType>> {
let email = email.to_lowercase();
let row: (u32, ) = match sqlx::query_as("SELECT user_type FROM user_registry_permissions WHERE username = ?")
.bind(username)
let row: (u32, ) = match sqlx::query_as("SELECT user_type FROM user_registry_permissions WHERE email = ?")
.bind(email)
.fetch_one(self).await {
Ok(row) => row,
Err(e) => match e {
@ -363,11 +392,11 @@ impl Database for Pool<Sqlite> {
Ok(Some(RegistryUserType::try_from(row.0)?))
}
async fn get_user_repo_permissions(&self, username: String, repository: String) -> anyhow::Result<Option<RepositoryPermissions>> {
let username = username.to_lowercase();
async fn get_user_repo_permissions(&self, email: String, repository: String) -> anyhow::Result<Option<RepositoryPermissions>> {
let email = email.to_lowercase();
let row: (u32, ) = match sqlx::query_as("SELECT repository_permissions FROM user_repo_permissions WHERE username = ? AND repository_name = ?")
.bind(username.clone())
let row: (u32, ) = match sqlx::query_as("SELECT repository_permissions FROM user_repo_permissions WHERE email = ? AND repository_name = ?")
.bind(email.clone())
.bind(repository.clone())
.fetch_one(self).await {
Ok(row) => row,
@ -384,7 +413,7 @@ impl Database for Pool<Sqlite> {
let vis = self.get_repository_visibility(&repository).await?.unwrap();
// Also get the user type for the registry, if its admin return admin repository permissions
let utype = self.get_user_registry_usertype(username).await?.unwrap(); // unwrap should be safe
let utype = self.get_user_registry_usertype(email).await?.unwrap(); // unwrap should be safe
if utype == RegistryUserType::Admin {
Ok(Some(RepositoryPermissions::new(Permission::ADMIN.bits(), vis)))
} else {
@ -392,22 +421,22 @@ impl Database for Pool<Sqlite> {
}
}
async fn get_user_registry_usertype(&self, username: String) -> anyhow::Result<Option<RegistryUserType>> {
let username = username.to_lowercase();
let row: (u32, ) = sqlx::query_as("SELECT user_type FROM user_registry_permissions WHERE username = ?")
.bind(username)
async fn get_user_registry_usertype(&self, email: String) -> anyhow::Result<Option<RegistryUserType>> {
let email = email.to_lowercase();
let row: (u32, ) = sqlx::query_as("SELECT user_type FROM user_registry_permissions WHERE email = ?")
.bind(email)
.fetch_one(self).await?;
Ok(Some(RegistryUserType::try_from(row.0)?))
}
async fn store_user_token(&self, token: String, username: String, expiry: DateTime<Utc>, created_at: DateTime<Utc>) -> anyhow::Result<()> {
let username = username.to_lowercase();
async fn store_user_token(&self, token: String, email: String, expiry: DateTime<Utc>, created_at: DateTime<Utc>) -> anyhow::Result<()> {
let email = email.to_lowercase();
let expiry = expiry.timestamp();
let created_at = created_at.timestamp();
sqlx::query("INSERT INTO user_tokens (token, username, expiry, created_at) VALUES (?, ?, ?, ?)")
sqlx::query("INSERT INTO user_tokens (token, email, expiry, created_at) VALUES (?, ?, ?, ?)")
.bind(token)
.bind(username)
.bind(email)
.bind(expiry)
.bind(created_at)
.execute(self).await?;
@ -416,18 +445,42 @@ impl Database for Pool<Sqlite> {
}
async fn verify_user_token(&self, token: String) -> anyhow::Result<Option<UserAuth>> {
let token_row: (String, i64, i64, ) = sqlx::query_as("SELECT username, expiry, created_at FROM user_tokens WHERE token = ?")
let token_row: (String, i64, i64,) = match sqlx::query_as("SELECT email, expiry, created_at FROM user_tokens WHERE token = ?")
.bind(token.clone())
.fetch_one(self).await?;
.fetch_one(self).await {
Ok(row) => row,
Err(e) => match e {
sqlx::Error::RowNotFound => {
return Ok(None)
},
_ => {
return Err(anyhow::Error::new(e));
}
}
};
let (username, expiry, created_at) = (token_row.0, token_row.1, token_row.2);
let (email, expiry, created_at) = (token_row.0, token_row.1, token_row.2);
let user_row: (String, ) = sqlx::query_as("SELECT email FROM users WHERE username = ?")
.bind(username.clone())
.fetch_one(self).await?;
let user_row: (String, u32) = match sqlx::query_as("SELECT username, login_source FROM users WHERE email = ?")
.bind(email.clone())
.fetch_one(self).await {
Ok(row) => row,
Err(e) => match e {
sqlx::Error::RowNotFound => {
return Ok(None)
},
_ => {
return Err(anyhow::Error::new(e));
}
}
};
/* let user_row: (String, u32) = sqlx::query_as("SELECT email, login_source FROM users WHERE email = ?")
.bind(email.clone())
.fetch_one(self).await?; */
let (expiry, created_at) = (Utc.timestamp_millis_opt(expiry).unwrap(), Utc.timestamp_millis_opt(created_at).unwrap());
let user = User::new(username, user_row.0);
let user = User::new(email, user_row.0, LoginSource::try_from(user_row.1)?);
let token = TokenInfo::new(token, expiry, created_at);
let auth = UserAuth::new(user, token);

View File

@ -35,8 +35,14 @@ CREATE TABLE IF NOT EXISTS manifest_layers (
);
CREATE TABLE IF NOT EXISTS users (
username TEXT NOT NULL UNIQUE PRIMARY KEY,
email TEXT NOT NULL,
email TEXT NOT NULL UNIQUE PRIMARY KEY,
username TEXT NOT NULL,
-- 0 = local, 1 = ldap
login_source BIGINT NOT NULL
);
CREATE TABLE IF NOT EXISTS user_logins (
email TEXT NOT NULL UNIQUE PRIMARY KEY,
-- bcrypt hashed password
password_hash TEXT NOT NULL,
-- the salt generated along side the password hash
@ -44,13 +50,13 @@ CREATE TABLE IF NOT EXISTS users (
);
CREATE TABLE IF NOT EXISTS user_registry_permissions (
username TEXT NOT NULL UNIQUE PRIMARY KEY,
email TEXT NOT NULL UNIQUE PRIMARY KEY,
-- 0 = regular user, 1 = admin
user_type INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS user_repo_permissions (
username TEXT NOT NULL UNIQUE PRIMARY KEY,
email TEXT NOT NULL UNIQUE PRIMARY KEY,
-- name of repository that this user has these permissions in
repository_name TEXT NOT NULL,
-- bitwised integer storing permissions
@ -59,10 +65,11 @@ CREATE TABLE IF NOT EXISTS user_repo_permissions (
CREATE TABLE IF NOT EXISTS user_tokens (
token TEXT NOT NULL UNIQUE PRIMARY KEY,
username TEXT NOT NULL,
email TEXT NOT NULL,
expiry BIGINT NOT NULL,
created_at BIGINT NOT NULL
);
-- create admin user
INSERT OR IGNORE INTO users (username, email, password_hash, password_salt) VALUES ('admin', 'admin@example.com', '$2b$12$x5ECk0jUmOSfBWxW52wsyOmFxNZkwc2J9FH225if4eBnQYUvYLYYq', 'x5ECk0jUmOSfBWxW52wsyO');
INSERT OR IGNORE INTO users (username, email, login_source) VALUES ('admin', 'admin@example.com', 0);
INSERT OR IGNORE INTO user_logins (email, password_hash, password_salt) VALUES ('admin@example.com', '$2b$12$x5ECk0jUmOSfBWxW52wsyOmFxNZkwc2J9FH225if4eBnQYUvYLYYq', 'x5ECk0jUmOSfBWxW52wsyO');

View File

@ -5,17 +5,37 @@ use chrono::{DateTime, Utc};
use super::RepositoryVisibility;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LoginSource {
Database = 0,
LDAP = 1
}
impl TryFrom<u32> for LoginSource {
type Error = anyhow::Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Database),
1 => Ok(Self::LDAP),
_ => Err(anyhow::anyhow!("Invalid value for LoginSource: `{}`", value)),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct User {
pub username: String,
pub email: String,
pub source: LoginSource,
}
impl User {
pub fn new(username: String, email: String) -> Self {
pub fn new(username: String, email: String, source: LoginSource) -> Self {
Self {
username,
email,
source,
}
}
}

View File

@ -6,13 +6,13 @@ mod storage;
mod byte_stream;
mod config;
mod query;
mod auth_storage;
mod auth;
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
use auth_storage::AuthDriver;
use auth::{AuthDriver, ldap_driver::LdapAuthDriver};
use axum::http::{Request, StatusCode, header, HeaderName};
use axum::middleware::Next;
use axum::response::{Response, IntoResponse};
@ -29,10 +29,11 @@ use tracing::{debug, Level};
use app_state::AppState;
use database::Database;
use crate::dto::user::Permission;
use crate::storage::StorageDriver;
use crate::storage::filesystem::FilesystemDriver;
use crate::config::Config;
use crate::config::{Config, LdapConnectionConfig};
use tower_http::trace::TraceLayer;
@ -60,6 +61,12 @@ async fn change_request_paths<B>(mut request: Request<B>, next: Next<B>) -> Resp
#[tokio::main]
async fn main() -> std::io::Result<()> {
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
.init();
let config = Config::new().expect("Failure to parse config!");
let pool = SqlitePoolOptions::new()
.max_connections(15)
.connect("test.db").await.unwrap();
@ -67,18 +74,25 @@ async fn main() -> std::io::Result<()> {
pool.create_schema().await.unwrap();
let storage_driver: Mutex<Box<dyn StorageDriver>> = Mutex::new(Box::new(FilesystemDriver::new("registry/blobs")));
let auth_driver: Mutex<Box<dyn AuthDriver>> = Mutex::new(Box::new(pool.clone()));
let config = Config::new().expect("Failure to parse config!");
// figure out the auth driver depending on whats specified in the config,
// the fallback is a database auth driver.
let auth_driver: Mutex<Box<dyn AuthDriver>> = match config.ldap.clone() {
Some(ldap) => {
let ldap_driver = LdapAuthDriver::new(ldap, pool.clone()).await.unwrap();
Mutex::new(Box::new(ldap_driver))
},
None => {
Mutex::new(Box::new(pool.clone()))
}
};
let app_addr = SocketAddr::from_str(&format!("{}:{}", config.listen_address, config.listen_port)).unwrap();
let state = Arc::new(AppState::new(pool, storage_driver, config, auth_driver));
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
.init();
let auth_middleware = axum::middleware::from_fn_with_state(state.clone(), auth_storage::require_auth);
let auth_middleware = axum::middleware::from_fn_with_state(state.clone(), auth::require_auth);
let path_middleware = axum::middleware::from_fn(change_request_paths);
let app = Router::new()