mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
Update opaque and implement it without DB
This commit is contained in:
parent
f12abb35d3
commit
8b73de0df7
65
Cargo.lock
generated
65
Cargo.lock
generated
@ -728,6 +728,22 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-mac"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ct-codecs"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "3.1.0"
|
version = "3.1.0"
|
||||||
@ -737,6 +753,7 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"digest",
|
"digest",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
|
"serde",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@ -1056,6 +1073,7 @@ version = "0.14.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"serde",
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
@ -1210,12 +1228,12 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hkdf"
|
name = "hkdf"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
|
checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
"hmac",
|
"hmac 0.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1224,7 +1242,17 @@ version = "0.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
|
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-mac",
|
"crypto-mac 0.10.0",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-mac 0.11.0",
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1324,9 +1352,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "86e46349d67dc03bdbdb28da0337a355a53ca1d5156452722c36fe21d0e6389b"
|
checksum = "86e46349d67dc03bdbdb28da0337a355a53ca1d5156452722c36fe21d0e6389b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"crypto-mac",
|
"crypto-mac 0.10.0",
|
||||||
"digest",
|
"digest",
|
||||||
"hmac",
|
"hmac 0.10.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
@ -1419,6 +1447,8 @@ dependencies = [
|
|||||||
"actix-web-httpauth",
|
"actix-web-httpauth",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"base64",
|
||||||
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"cron",
|
"cron",
|
||||||
@ -1426,7 +1456,7 @@ dependencies = [
|
|||||||
"figment",
|
"figment",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hmac",
|
"hmac 0.10.1",
|
||||||
"http",
|
"http",
|
||||||
"jwt",
|
"jwt",
|
||||||
"ldap3_server",
|
"ldap3_server",
|
||||||
@ -1434,6 +1464,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"mockall",
|
"mockall",
|
||||||
"opaque-ke",
|
"opaque-ke",
|
||||||
|
"orion",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"sea-query",
|
"sea-query",
|
||||||
"serde",
|
"serde",
|
||||||
@ -1804,8 +1835,8 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-ke"
|
name = "opaque-ke"
|
||||||
version = "0.5.1-pre.1"
|
version = "0.6.0-pre.1"
|
||||||
source = "git+https://github.com/novifinancial/opaque-ke?rev=98f1821897cd2800e5bffb2a70541056145e99cc#98f1821897cd2800e5bffb2a70541056145e99cc"
|
source = "git+https://github.com/novifinancial/opaque-ke?rev=eb59676a940b15f77871aefe1e46d7b5bf85f40a#eb59676a940b15f77871aefe1e46d7b5bf85f40a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
@ -1814,7 +1845,7 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
"generic-bytes",
|
"generic-bytes",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
"hmac",
|
"hmac 0.11.0",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"serde",
|
"serde",
|
||||||
"subtle",
|
"subtle",
|
||||||
@ -1871,6 +1902,18 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orion"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "118f94b4ca56d1eb99466a26a216b87fad822a51af8b308264c88a9337eb0a15"
|
||||||
|
dependencies = [
|
||||||
|
"ct-codecs",
|
||||||
|
"getrandom 0.2.3",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
@ -2525,7 +2568,7 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac 0.10.1",
|
||||||
"itoa",
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
|
@ -16,6 +16,8 @@ actix-service = "2.0.0"
|
|||||||
actix-web = "4.0.0-beta.3"
|
actix-web = "4.0.0-beta.3"
|
||||||
anyhow = "*"
|
anyhow = "*"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
base64 = "0.13"
|
||||||
|
bincode = "1.3"
|
||||||
chrono = { version = "*", features = [ "serde" ]}
|
chrono = { version = "*", features = [ "serde" ]}
|
||||||
clap = "3.0.0-beta.2"
|
clap = "3.0.0-beta.2"
|
||||||
cron = "*"
|
cron = "*"
|
||||||
@ -28,6 +30,7 @@ jwt = "0.13"
|
|||||||
ldap3_server = "*"
|
ldap3_server = "*"
|
||||||
lldap_model = { path = "model" }
|
lldap_model = { path = "model" }
|
||||||
log = "*"
|
log = "*"
|
||||||
|
orion = "0.16"
|
||||||
serde = "*"
|
serde = "*"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
@ -45,7 +48,7 @@ rand = { version = "0.8", features = ["small_rng", "getrandom"] }
|
|||||||
# TODO: update to 0.6 when out.
|
# TODO: update to 0.6 when out.
|
||||||
[dependencies.opaque-ke]
|
[dependencies.opaque-ke]
|
||||||
git = "https://github.com/novifinancial/opaque-ke"
|
git = "https://github.com/novifinancial/opaque-ke"
|
||||||
rev = "98f1821897cd2800e5bffb2a70541056145e99cc"
|
rev = "eb59676a940b15f77871aefe1e46d7b5bf85f40a"
|
||||||
|
|
||||||
[dependencies.sqlx]
|
[dependencies.sqlx]
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -92,7 +92,7 @@ impl LoginForm {
|
|||||||
Ok(l) => l,
|
Ok(l) => l,
|
||||||
};
|
};
|
||||||
let req = login::ClientLoginFinishRequest {
|
let req = login::ClientLoginFinishRequest {
|
||||||
login_key: res.login_key,
|
server_data: res.server_data,
|
||||||
credential_finalization: login_finish.message,
|
credential_finalization: login_finish.message,
|
||||||
};
|
};
|
||||||
self.call_backend(
|
self.call_backend(
|
||||||
|
@ -23,7 +23,7 @@ thiserror = "*"
|
|||||||
# TODO: update to 0.6 when out.
|
# TODO: update to 0.6 when out.
|
||||||
[dependencies.opaque-ke]
|
[dependencies.opaque-ke]
|
||||||
git = "https://github.com/novifinancial/opaque-ke"
|
git = "https://github.com/novifinancial/opaque-ke"
|
||||||
rev = "98f1821897cd2800e5bffb2a70541056145e99cc"
|
rev = "eb59676a940b15f77871aefe1e46d7b5bf85f40a"
|
||||||
|
|
||||||
[dependencies.chrono]
|
[dependencies.chrono]
|
||||||
version = "*"
|
version = "*"
|
||||||
|
@ -14,6 +14,12 @@ pub struct BindRequest {
|
|||||||
pub mod login {
|
pub mod login {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ServerData {
|
||||||
|
pub username: String,
|
||||||
|
pub server_login: opaque::server::login::ServerLogin,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ClientLoginStartRequest {
|
pub struct ClientLoginStartRequest {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
@ -22,15 +28,15 @@ pub mod login {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ServerLoginStartResponse {
|
pub struct ServerLoginStartResponse {
|
||||||
/// A randomly-generated temporary key that corresponds to this login attempt.
|
/// Base64, encrypted ServerData to be passed back to the server.
|
||||||
pub login_key: String,
|
pub server_data: String,
|
||||||
pub credential_response: opaque::client::login::CredentialResponse,
|
pub credential_response: opaque::client::login::CredentialResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ClientLoginFinishRequest {
|
pub struct ClientLoginFinishRequest {
|
||||||
/// The key returned by the server in the previous step.
|
/// Encrypted ServerData from the previous step.
|
||||||
pub login_key: String,
|
pub server_data: String,
|
||||||
pub credential_finalization: opaque::client::login::CredentialFinalization,
|
pub credential_finalization: opaque::client::login::CredentialFinalization,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,6 +45,11 @@ pub mod login {
|
|||||||
pub mod registration {
|
pub mod registration {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ServerData {
|
||||||
|
pub username: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ClientRegistrationStartRequest {
|
pub struct ClientRegistrationStartRequest {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
@ -47,15 +58,15 @@ pub mod registration {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ServerRegistrationStartResponse {
|
pub struct ServerRegistrationStartResponse {
|
||||||
/// A randomly-generated temporary key that corresponds to this registration attempt.
|
/// Base64, encrypted ServerData to be passed back to the server.
|
||||||
pub registration_key: String,
|
pub server_data: String,
|
||||||
pub registration_response: opaque::client::registration::RegistrationResponse,
|
pub registration_response: opaque::client::registration::RegistrationResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ClientRegistrationFinishRequest {
|
pub struct ClientRegistrationFinishRequest {
|
||||||
/// The key returned by the server in the previous step.
|
/// Encrypted ServerData from the previous step.
|
||||||
pub registration_key: String,
|
pub server_data: String,
|
||||||
pub registration_upload: opaque::server::registration::RegistrationUpload,
|
pub registration_upload: opaque::server::registration::RegistrationUpload,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,18 +100,14 @@ pub mod client {
|
|||||||
pub type ClientLoginStartResult = opaque_ke::ClientLoginStartResult<DefaultSuite>;
|
pub type ClientLoginStartResult = opaque_ke::ClientLoginStartResult<DefaultSuite>;
|
||||||
pub type CredentialResponse = opaque_ke::CredentialResponse<DefaultSuite>;
|
pub type CredentialResponse = opaque_ke::CredentialResponse<DefaultSuite>;
|
||||||
pub type CredentialFinalization = opaque_ke::CredentialFinalization<DefaultSuite>;
|
pub type CredentialFinalization = opaque_ke::CredentialFinalization<DefaultSuite>;
|
||||||
pub use opaque_ke::{ClientLoginFinishParameters, ClientLoginStartParameters};
|
pub use opaque_ke::ClientLoginFinishParameters;
|
||||||
|
|
||||||
/// Initiate the login negotiation.
|
/// Initiate the login negotiation.
|
||||||
pub fn start_login<R: RngCore + CryptoRng>(
|
pub fn start_login<R: RngCore + CryptoRng>(
|
||||||
password: &str,
|
password: &str,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> AuthenticationResult<ClientLoginStartResult> {
|
) -> AuthenticationResult<ClientLoginStartResult> {
|
||||||
Ok(ClientLogin::start(
|
Ok(ClientLogin::start(rng, password.as_bytes())?)
|
||||||
rng,
|
|
||||||
password.as_bytes(),
|
|
||||||
ClientLoginStartParameters::default(),
|
|
||||||
)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalize the client login negotiation.
|
/// Finalize the client login negotiation.
|
||||||
@ -130,6 +126,7 @@ pub mod client {
|
|||||||
pub mod server {
|
pub mod server {
|
||||||
pub use super::*;
|
pub use super::*;
|
||||||
pub type ServerRegistration = opaque_ke::ServerRegistration<DefaultSuite>;
|
pub type ServerRegistration = opaque_ke::ServerRegistration<DefaultSuite>;
|
||||||
|
pub type ServerSetup = opaque_ke::ServerSetup<DefaultSuite>;
|
||||||
/// Methods to register a new user, from the server side.
|
/// Methods to register a new user, from the server side.
|
||||||
pub mod registration {
|
pub mod registration {
|
||||||
pub use super::*;
|
pub use super::*;
|
||||||
@ -140,24 +137,21 @@ pub mod server {
|
|||||||
/// Start a registration process, from a request sent by the client.
|
/// Start a registration process, from a request sent by the client.
|
||||||
///
|
///
|
||||||
/// The result must be kept for the next step.
|
/// The result must be kept for the next step.
|
||||||
pub fn start_registration<R: RngCore + CryptoRng>(
|
pub fn start_registration(
|
||||||
rng: &mut R,
|
server_setup: &ServerSetup,
|
||||||
registration_request: RegistrationRequest,
|
registration_request: RegistrationRequest,
|
||||||
server_public_key: &PublicKey,
|
username: &str,
|
||||||
) -> AuthenticationResult<ServerRegistrationStartResult> {
|
) -> AuthenticationResult<ServerRegistrationStartResult> {
|
||||||
Ok(ServerRegistration::start(
|
Ok(ServerRegistration::start(
|
||||||
rng,
|
server_setup,
|
||||||
registration_request,
|
registration_request,
|
||||||
server_public_key,
|
username.as_bytes(),
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish to register a new user, and get the data to store in the database.
|
/// Finish to register a new user, and get the data to store in the database.
|
||||||
pub fn get_password_file(
|
pub fn get_password_file(registration_upload: RegistrationUpload) -> ServerRegistration {
|
||||||
registration_start: ServerRegistration,
|
ServerRegistration::finish(registration_upload)
|
||||||
registration_upload: RegistrationUpload,
|
|
||||||
) -> AuthenticationResult<ServerRegistration> {
|
|
||||||
Ok(registration_start.finish(registration_upload)?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,15 +170,17 @@ pub mod server {
|
|||||||
/// The result must be kept for the next step.
|
/// The result must be kept for the next step.
|
||||||
pub fn start_login<R: RngCore + CryptoRng>(
|
pub fn start_login<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
password_file: ServerRegistration,
|
server_setup: &ServerSetup,
|
||||||
server_private_key: &PrivateKey,
|
password_file: Option<ServerRegistration>,
|
||||||
credential_request: CredentialRequest,
|
credential_request: CredentialRequest,
|
||||||
|
username: &str,
|
||||||
) -> AuthenticationResult<ServerLoginStartResult> {
|
) -> AuthenticationResult<ServerLoginStartResult> {
|
||||||
Ok(ServerLogin::start(
|
Ok(ServerLogin::start(
|
||||||
rng,
|
rng,
|
||||||
|
server_setup,
|
||||||
password_file,
|
password_file,
|
||||||
server_private_key,
|
|
||||||
credential_request,
|
credential_request,
|
||||||
|
username.as_bytes(),
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginStartParameters::default(),
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,12 @@ pub enum DomainError {
|
|||||||
DatabaseError(#[from] sqlx::Error),
|
DatabaseError(#[from] sqlx::Error),
|
||||||
#[error("Authentication protocol error for `{0}`")]
|
#[error("Authentication protocol error for `{0}`")]
|
||||||
AuthenticationProtocolError(#[from] lldap_model::opaque::AuthenticationError),
|
AuthenticationProtocolError(#[from] lldap_model::opaque::AuthenticationError),
|
||||||
|
#[error("Unknown crypto error: `{0}`")]
|
||||||
|
UnknownCryptoError(#[from] orion::errors::UnknownCryptoError),
|
||||||
|
#[error("Binary serialization error: `{0}`")]
|
||||||
|
BinarySerializationError(#[from] bincode::Error),
|
||||||
|
#[error("Invalid base64: `{0}`")]
|
||||||
|
Base64DecodeError(#[from] base64::DecodeError),
|
||||||
#[error("Internal error: `{0}`")]
|
#[error("Internal error: `{0}`")]
|
||||||
InternalError(String),
|
InternalError(String),
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,8 @@ impl SqlBackendHandler {
|
|||||||
|
|
||||||
pub fn get_password_file(
|
pub fn get_password_file(
|
||||||
clear_password: &str,
|
clear_password: &str,
|
||||||
server_public_key: &opaque::PublicKey,
|
server_setup: &opaque::server::ServerSetup,
|
||||||
|
username: &str,
|
||||||
) -> Result<opaque::server::ServerRegistration> {
|
) -> Result<opaque::server::ServerRegistration> {
|
||||||
use opaque::{client, server};
|
use opaque::{client, server};
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
@ -30,9 +31,9 @@ pub fn get_password_file(
|
|||||||
client::registration::start_registration(clear_password, &mut rng)?;
|
client::registration::start_registration(clear_password, &mut rng)?;
|
||||||
|
|
||||||
let server_register_start_result = server::registration::start_registration(
|
let server_register_start_result = server::registration::start_registration(
|
||||||
&mut rng,
|
server_setup,
|
||||||
client_register_start_result.message,
|
client_register_start_result.message,
|
||||||
server_public_key,
|
username,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let client_registration_result = client::registration::finish_registration(
|
let client_registration_result = client::registration::finish_registration(
|
||||||
@ -42,9 +43,8 @@ pub fn get_password_file(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(server::registration::get_password_file(
|
Ok(server::registration::get_password_file(
|
||||||
server_register_start_result.state,
|
|
||||||
client_registration_result.message,
|
client_registration_result.message,
|
||||||
)?)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_filter_expr(filter: RequestFilter) -> SimpleExpr {
|
fn get_filter_expr(filter: RequestFilter) -> SimpleExpr {
|
||||||
@ -187,7 +187,7 @@ impl BackendHandler for SqlBackendHandler {
|
|||||||
Users::CreationDate,
|
Users::CreationDate,
|
||||||
];
|
];
|
||||||
let mut values = vec![
|
let mut values = vec![
|
||||||
request.user_id.into(),
|
request.user_id.clone().into(),
|
||||||
request.email.into(),
|
request.email.into(),
|
||||||
request.display_name.map(Into::into).unwrap_or(Value::Null),
|
request.display_name.map(Into::into).unwrap_or(Value::Null),
|
||||||
request.first_name.map(Into::into).unwrap_or(Value::Null),
|
request.first_name.map(Into::into).unwrap_or(Value::Null),
|
||||||
@ -197,7 +197,7 @@ impl BackendHandler for SqlBackendHandler {
|
|||||||
if let Some(pass) = request.password {
|
if let Some(pass) = request.password {
|
||||||
columns.push(Users::PasswordHash);
|
columns.push(Users::PasswordHash);
|
||||||
values.push(
|
values.push(
|
||||||
get_password_file(&pass, self.config.get_server_keys().public())?
|
get_password_file(&pass, self.config.get_server_setup(), &request.user_id)?
|
||||||
.serialize()
|
.serialize()
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
@ -5,25 +5,16 @@ use super::{
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use lldap_model::{opaque, BindRequest};
|
use lldap_model::{opaque, BindRequest};
|
||||||
use log::*;
|
use log::*;
|
||||||
use rand::{CryptoRng, RngCore};
|
|
||||||
use sea_query::{Expr, Iden, Query};
|
use sea_query::{Expr, Iden, Query};
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
type SqlOpaqueHandler = SqlBackendHandler;
|
type SqlOpaqueHandler = SqlBackendHandler;
|
||||||
|
|
||||||
fn generate_random_id<R: RngCore + CryptoRng>(rng: &mut R) -> String {
|
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
|
||||||
std::iter::repeat(())
|
|
||||||
.map(|()| rng.sample(Alphanumeric))
|
|
||||||
.map(char::from)
|
|
||||||
.take(32)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn passwords_match(
|
fn passwords_match(
|
||||||
password_file_bytes: &[u8],
|
password_file_bytes: &[u8],
|
||||||
clear_password: &str,
|
clear_password: &str,
|
||||||
server_private_key: &opaque::PrivateKey,
|
server_setup: &opaque::server::ServerSetup,
|
||||||
|
username: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use opaque::{client, server};
|
use opaque::{client, server};
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
@ -33,9 +24,10 @@ fn passwords_match(
|
|||||||
.map_err(opaque::AuthenticationError::ProtocolError)?;
|
.map_err(opaque::AuthenticationError::ProtocolError)?;
|
||||||
let server_login_start_result = server::login::start_login(
|
let server_login_start_result = server::login::start_login(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
password_file,
|
server_setup,
|
||||||
server_private_key,
|
Some(password_file),
|
||||||
client_login_start_result.message,
|
client_login_start_result.message,
|
||||||
|
username,
|
||||||
)?;
|
)?;
|
||||||
client::login::finish_login(
|
client::login::finish_login(
|
||||||
client_login_start_result.state,
|
client_login_start_result.state,
|
||||||
@ -44,6 +36,40 @@ fn passwords_match(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SqlBackendHandler {
|
||||||
|
fn get_orion_secret_key(&self) -> Result<orion::aead::SecretKey> {
|
||||||
|
Ok(orion::aead::SecretKey::from_slice(
|
||||||
|
self.config.get_server_keys().private(),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_password_file_for_user(
|
||||||
|
&self,
|
||||||
|
username: &str,
|
||||||
|
) -> Result<Option<opaque::server::ServerRegistration>> {
|
||||||
|
// Fetch the previously registered password file from the DB.
|
||||||
|
let password_file_bytes = {
|
||||||
|
let query = Query::select()
|
||||||
|
.column(Users::PasswordHash)
|
||||||
|
.from(Users::Table)
|
||||||
|
.and_where(Expr::col(Users::UserId).eq(username))
|
||||||
|
.to_string(DbQueryBuilder {});
|
||||||
|
if let Some(row) = sqlx::query(&query).fetch_optional(&self.sql_pool).await? {
|
||||||
|
row.get::<Option<Vec<u8>>, _>(&*Users::PasswordHash.to_string())
|
||||||
|
// If no password, always fail.
|
||||||
|
.ok_or_else(|| DomainError::AuthenticationError(username.to_string()))?
|
||||||
|
} else {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
opaque::server::ServerRegistration::deserialize(&password_file_bytes)
|
||||||
|
.map(Option::Some)
|
||||||
|
.map_err(|_| {
|
||||||
|
DomainError::InternalError(format!("Corrupted password file for {}", username))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl LoginHandler for SqlBackendHandler {
|
impl LoginHandler for SqlBackendHandler {
|
||||||
async fn bind(&self, request: BindRequest) -> Result<()> {
|
async fn bind(&self, request: BindRequest) -> Result<()> {
|
||||||
@ -67,7 +93,8 @@ impl LoginHandler for SqlBackendHandler {
|
|||||||
if let Err(e) = passwords_match(
|
if let Err(e) = passwords_match(
|
||||||
&password_hash,
|
&password_hash,
|
||||||
&request.password,
|
&request.password,
|
||||||
self.config.get_server_keys().private(),
|
self.config.get_server_setup(),
|
||||||
|
&request.name,
|
||||||
) {
|
) {
|
||||||
debug!(r#"Invalid password for "{}": {}"#, request.name, e);
|
debug!(r#"Invalid password for "{}": {}"#, request.name, e);
|
||||||
} else {
|
} else {
|
||||||
@ -89,99 +116,44 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
|||||||
&self,
|
&self,
|
||||||
request: login::ClientLoginStartRequest,
|
request: login::ClientLoginStartRequest,
|
||||||
) -> Result<login::ServerLoginStartResponse> {
|
) -> Result<login::ServerLoginStartResponse> {
|
||||||
// Fetch the previously registered password file from the DB.
|
let maybe_password_file = self.get_password_file_for_user(&request.username).await?;
|
||||||
let password_file_bytes = {
|
|
||||||
let query = Query::select()
|
|
||||||
.column(Users::PasswordHash)
|
|
||||||
.from(Users::Table)
|
|
||||||
.and_where(Expr::col(Users::UserId).eq(request.username.as_str()))
|
|
||||||
.to_string(DbQueryBuilder {});
|
|
||||||
sqlx::query(&query)
|
|
||||||
.fetch_one(&self.sql_pool)
|
|
||||||
.await?
|
|
||||||
.get::<Option<Vec<u8>>, _>(&*Users::PasswordHash.to_string())
|
|
||||||
// If no password, always fail.
|
|
||||||
.ok_or_else(|| DomainError::AuthenticationError(request.username.clone()))?
|
|
||||||
};
|
|
||||||
let password_file = opaque::server::ServerRegistration::deserialize(&password_file_bytes)
|
|
||||||
.map_err(|_| {
|
|
||||||
DomainError::InternalError(format!("Corrupted password file for {}", request.username))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let start_response = opaque::server::login::start_login(
|
let start_response = opaque::server::login::start_login(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
password_file,
|
self.config.get_server_setup(),
|
||||||
self.config.get_server_keys().private(),
|
maybe_password_file,
|
||||||
request.login_start_request,
|
request.login_start_request,
|
||||||
|
&request.username,
|
||||||
)?;
|
)?;
|
||||||
let login_attempt_id = generate_random_id(&mut rng);
|
let secret_key = self.get_orion_secret_key()?;
|
||||||
|
let server_data = login::ServerData {
|
||||||
{
|
username: request.username,
|
||||||
// Insert the current login attempt in the DB.
|
server_login: start_response.state,
|
||||||
let query = Query::insert()
|
};
|
||||||
.into_table(LoginAttempts::Table)
|
let encrypted_state = orion::aead::seal(&secret_key, &bincode::serialize(&server_data)?)?;
|
||||||
.columns(vec![
|
|
||||||
LoginAttempts::RandomId,
|
|
||||||
LoginAttempts::UserId,
|
|
||||||
LoginAttempts::ServerLoginData,
|
|
||||||
LoginAttempts::Timestamp,
|
|
||||||
])
|
|
||||||
.values_panic(vec![
|
|
||||||
login_attempt_id.as_str().into(),
|
|
||||||
request.username.as_str().into(),
|
|
||||||
start_response.state.serialize().into(),
|
|
||||||
chrono::Utc::now().naive_utc().into(),
|
|
||||||
])
|
|
||||||
.to_string(DbQueryBuilder {});
|
|
||||||
sqlx::query(&query).execute(&self.sql_pool).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(login::ServerLoginStartResponse {
|
Ok(login::ServerLoginStartResponse {
|
||||||
login_key: login_attempt_id,
|
server_data: base64::encode(&encrypted_state),
|
||||||
credential_response: start_response.message,
|
credential_response: start_response.message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn login_finish(&self, request: login::ClientLoginFinishRequest) -> Result<String> {
|
async fn login_finish(&self, request: login::ClientLoginFinishRequest) -> Result<String> {
|
||||||
// Fetch the previous data from this login attempt.
|
let secret_key = self.get_orion_secret_key()?;
|
||||||
let row = {
|
let login::ServerData {
|
||||||
let query = Query::select()
|
username,
|
||||||
.column(LoginAttempts::UserId)
|
server_login,
|
||||||
.column(LoginAttempts::ServerLoginData)
|
} = bincode::deserialize(&orion::aead::open(
|
||||||
.from(LoginAttempts::Table)
|
&secret_key,
|
||||||
.and_where(Expr::col(LoginAttempts::RandomId).eq(request.login_key.as_str()))
|
&base64::decode(&request.server_data)?,
|
||||||
.and_where(
|
)?)?;
|
||||||
Expr::col(LoginAttempts::Timestamp)
|
|
||||||
.gt(chrono::Utc::now().naive_utc() - chrono::Duration::minutes(5)),
|
|
||||||
)
|
|
||||||
.to_string(DbQueryBuilder {});
|
|
||||||
sqlx::query(&query).fetch_one(&self.sql_pool).await?
|
|
||||||
};
|
|
||||||
let username = row.get::<String, _>(&*LoginAttempts::UserId.to_string());
|
|
||||||
let login_data = opaque::server::login::ServerLogin::deserialize(
|
|
||||||
&row.get::<Vec<u8>, _>(&*LoginAttempts::ServerLoginData.to_string()),
|
|
||||||
)
|
|
||||||
.map_err(|_| {
|
|
||||||
DomainError::InternalError(format!(
|
|
||||||
"Corrupted login data for user `{}` [id `{}`]",
|
|
||||||
username, request.login_key
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
// Finish the login: this makes sure the client data is correct, and gives a session key we
|
// Finish the login: this makes sure the client data is correct, and gives a session key we
|
||||||
// don't need.
|
// don't need.
|
||||||
let _session_key =
|
let _session_key =
|
||||||
opaque::server::login::finish_login(login_data, request.credential_finalization)?
|
opaque::server::login::finish_login(server_login, request.credential_finalization)?
|
||||||
.session_key;
|
.session_key;
|
||||||
|
|
||||||
{
|
|
||||||
// Login was successful, we can delete the login attempt from the table.
|
|
||||||
let delete_query = Query::delete()
|
|
||||||
.from_table(LoginAttempts::Table)
|
|
||||||
.and_where(Expr::col(LoginAttempts::RandomId).eq(request.login_key))
|
|
||||||
.to_string(DbQueryBuilder {});
|
|
||||||
sqlx::query(&delete_query).execute(&self.sql_pool).await?;
|
|
||||||
}
|
|
||||||
Ok(username)
|
Ok(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,36 +161,19 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
|||||||
&self,
|
&self,
|
||||||
request: registration::ClientRegistrationStartRequest,
|
request: registration::ClientRegistrationStartRequest,
|
||||||
) -> Result<registration::ServerRegistrationStartResponse> {
|
) -> Result<registration::ServerRegistrationStartResponse> {
|
||||||
let mut rng = rand::rngs::OsRng;
|
|
||||||
// Generate the server-side key and derive the data to send back.
|
// Generate the server-side key and derive the data to send back.
|
||||||
let start_response = opaque::server::registration::start_registration(
|
let start_response = opaque::server::registration::start_registration(
|
||||||
&mut rng,
|
self.config.get_server_setup(),
|
||||||
request.registration_start_request,
|
request.registration_start_request,
|
||||||
self.config.get_server_keys().public(),
|
&request.username,
|
||||||
)?;
|
)?;
|
||||||
// Unique ID to identify the registration attempt.
|
let secret_key = self.get_orion_secret_key()?;
|
||||||
let registration_attempt_id = generate_random_id(&mut rng);
|
let server_data = registration::ServerData {
|
||||||
{
|
username: request.username,
|
||||||
// Write the registration attempt to the DB for the later turn.
|
};
|
||||||
let query = Query::insert()
|
let encrypted_state = orion::aead::seal(&secret_key, &bincode::serialize(&server_data)?)?;
|
||||||
.into_table(RegistrationAttempts::Table)
|
|
||||||
.columns(vec![
|
|
||||||
RegistrationAttempts::RandomId,
|
|
||||||
RegistrationAttempts::UserId,
|
|
||||||
RegistrationAttempts::ServerRegistrationData,
|
|
||||||
RegistrationAttempts::Timestamp,
|
|
||||||
])
|
|
||||||
.values_panic(vec![
|
|
||||||
registration_attempt_id.as_str().into(),
|
|
||||||
request.username.as_str().into(),
|
|
||||||
start_response.state.serialize().into(),
|
|
||||||
chrono::Utc::now().naive_utc().into(),
|
|
||||||
])
|
|
||||||
.to_string(DbQueryBuilder {});
|
|
||||||
sqlx::query(&query).execute(&self.sql_pool).await?;
|
|
||||||
}
|
|
||||||
Ok(registration::ServerRegistrationStartResponse {
|
Ok(registration::ServerRegistrationStartResponse {
|
||||||
registration_key: registration_attempt_id,
|
server_data: base64::encode(encrypted_state),
|
||||||
registration_response: start_response.message,
|
registration_response: start_response.message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -227,37 +182,14 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
|||||||
&self,
|
&self,
|
||||||
request: registration::ClientRegistrationFinishRequest,
|
request: registration::ClientRegistrationFinishRequest,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Fetch the previous state.
|
let secret_key = self.get_orion_secret_key()?;
|
||||||
let row = {
|
let registration::ServerData { username } = bincode::deserialize(&orion::aead::open(
|
||||||
let query = Query::select()
|
&secret_key,
|
||||||
.column(RegistrationAttempts::UserId)
|
&base64::decode(&request.server_data)?,
|
||||||
.column(RegistrationAttempts::ServerRegistrationData)
|
)?)?;
|
||||||
.from(RegistrationAttempts::Table)
|
|
||||||
.and_where(
|
|
||||||
Expr::col(RegistrationAttempts::RandomId).eq(request.registration_key.as_str()),
|
|
||||||
)
|
|
||||||
.and_where(
|
|
||||||
Expr::col(RegistrationAttempts::Timestamp)
|
|
||||||
.gt(chrono::Utc::now().naive_utc() - chrono::Duration::minutes(5)),
|
|
||||||
)
|
|
||||||
.to_string(DbQueryBuilder {});
|
|
||||||
sqlx::query(&query).fetch_one(&self.sql_pool).await?
|
|
||||||
};
|
|
||||||
let username = row.get::<String, _>(&*RegistrationAttempts::UserId.to_string());
|
|
||||||
let registration_data = opaque::server::registration::ServerRegistration::deserialize(
|
|
||||||
&row.get::<Vec<u8>, _>(&*RegistrationAttempts::ServerRegistrationData.to_string()),
|
|
||||||
)
|
|
||||||
.map_err(|_| {
|
|
||||||
DomainError::InternalError(format!(
|
|
||||||
"Corrupted registration data for user `{}` [id `{}`]",
|
|
||||||
username, request.registration_key
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let password_file = opaque::server::registration::get_password_file(
|
let password_file =
|
||||||
registration_data,
|
opaque::server::registration::get_password_file(request.registration_upload);
|
||||||
request.registration_upload,
|
|
||||||
)?;
|
|
||||||
{
|
{
|
||||||
// Set the user password to the new password.
|
// Set the user password to the new password.
|
||||||
let update_query = Query::update()
|
let update_query = Query::update()
|
||||||
@ -270,14 +202,6 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
|||||||
.to_string(DbQueryBuilder {});
|
.to_string(DbQueryBuilder {});
|
||||||
sqlx::query(&update_query).execute(&self.sql_pool).await?;
|
sqlx::query(&update_query).execute(&self.sql_pool).await?;
|
||||||
}
|
}
|
||||||
{
|
|
||||||
// Delete the registration attempt.
|
|
||||||
let delete_query = Query::delete()
|
|
||||||
.from_table(RegistrationAttempts::Table)
|
|
||||||
.and_where(Expr::col(RegistrationAttempts::RandomId).eq(request.registration_key))
|
|
||||||
.to_string(DbQueryBuilder {});
|
|
||||||
sqlx::query(&delete_query).execute(&self.sql_pool).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,7 +265,7 @@ mod tests {
|
|||||||
)?;
|
)?;
|
||||||
opaque_handler
|
opaque_handler
|
||||||
.login_finish(ClientLoginFinishRequest {
|
.login_finish(ClientLoginFinishRequest {
|
||||||
login_key: start_response.login_key,
|
server_data: start_response.server_data,
|
||||||
credential_finalization: login_finish.message,
|
credential_finalization: login_finish.message,
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
@ -370,7 +294,7 @@ mod tests {
|
|||||||
)?;
|
)?;
|
||||||
opaque_handler
|
opaque_handler
|
||||||
.registration_finish(ClientRegistrationFinishRequest {
|
.registration_finish(ClientRegistrationFinishRequest {
|
||||||
registration_key: start_response.registration_key,
|
server_data: start_response.server_data,
|
||||||
registration_upload: registration_finish.message,
|
registration_upload: registration_finish.message,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -34,27 +34,6 @@ pub enum Memberships {
|
|||||||
GroupId,
|
GroupId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains the temporary data that needs to be kept between the first and second message when
|
|
||||||
/// logging in with the OPAQUE protocol.
|
|
||||||
#[derive(Iden)]
|
|
||||||
pub enum LoginAttempts {
|
|
||||||
Table,
|
|
||||||
RandomId,
|
|
||||||
UserId,
|
|
||||||
ServerLoginData,
|
|
||||||
Timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same for registration.
|
|
||||||
#[derive(Iden)]
|
|
||||||
pub enum RegistrationAttempts {
|
|
||||||
Table,
|
|
||||||
RandomId,
|
|
||||||
UserId,
|
|
||||||
ServerRegistrationData,
|
|
||||||
Timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn init_table(pool: &Pool) -> sqlx::Result<()> {
|
pub async fn init_table(pool: &Pool) -> sqlx::Result<()> {
|
||||||
// SQLite needs this pragma to be turned on. Other DB might not understand this, so ignore the
|
// SQLite needs this pragma to be turned on. Other DB might not understand this, so ignore the
|
||||||
// error.
|
// error.
|
||||||
@ -135,80 +114,6 @@ pub async fn init_table(pool: &Pool) -> sqlx::Result<()> {
|
|||||||
.execute(pool)
|
.execute(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
sqlx::query(
|
|
||||||
&Table::create()
|
|
||||||
.table(LoginAttempts::Table)
|
|
||||||
.if_not_exists()
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(LoginAttempts::RandomId)
|
|
||||||
.string_len(32)
|
|
||||||
.primary_key(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(LoginAttempts::UserId)
|
|
||||||
.string_len(255)
|
|
||||||
.not_null(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(LoginAttempts::ServerLoginData)
|
|
||||||
.binary()
|
|
||||||
.not_null(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(LoginAttempts::Timestamp)
|
|
||||||
.date_time()
|
|
||||||
.not_null(),
|
|
||||||
)
|
|
||||||
.foreign_key(
|
|
||||||
ForeignKey::create()
|
|
||||||
.name("LoginAttemptsUserIdForeignKey")
|
|
||||||
.table(LoginAttempts::Table, Users::Table)
|
|
||||||
.col(LoginAttempts::UserId, Users::UserId)
|
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
|
||||||
)
|
|
||||||
.to_string(DbQueryBuilder {}),
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
sqlx::query(
|
|
||||||
&Table::create()
|
|
||||||
.table(RegistrationAttempts::Table)
|
|
||||||
.if_not_exists()
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(RegistrationAttempts::RandomId)
|
|
||||||
.string_len(32)
|
|
||||||
.primary_key(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(RegistrationAttempts::UserId)
|
|
||||||
.string_len(255)
|
|
||||||
.not_null(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(RegistrationAttempts::ServerRegistrationData)
|
|
||||||
.binary()
|
|
||||||
.not_null(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(RegistrationAttempts::Timestamp)
|
|
||||||
.date_time()
|
|
||||||
.not_null(),
|
|
||||||
)
|
|
||||||
.foreign_key(
|
|
||||||
ForeignKey::create()
|
|
||||||
.name("RegistrationAttemptsUserIdForeignKey")
|
|
||||||
.table(RegistrationAttempts::Table, Users::Table)
|
|
||||||
.col(RegistrationAttempts::UserId, Users::UserId)
|
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
|
||||||
.on_update(ForeignKeyAction::Cascade),
|
|
||||||
)
|
|
||||||
.to_string(DbQueryBuilder {}),
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ use figment::{
|
|||||||
providers::{Env, Format, Serialized, Toml},
|
providers::{Env, Format, Serialized, Toml},
|
||||||
Figment,
|
Figment,
|
||||||
};
|
};
|
||||||
use lldap_model::{opaque, opaque::KeyPair};
|
use lldap_model::opaque::{server::ServerSetup, KeyPair};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::infra::cli::CLIOpts;
|
use crate::infra::cli::CLIOpts;
|
||||||
@ -27,18 +27,18 @@ pub struct Configuration {
|
|||||||
pub key_file: String,
|
pub key_file: String,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[builder(field(private), setter(strip_option))]
|
#[builder(field(private), setter(strip_option))]
|
||||||
server_keys: Option<KeyPair>,
|
server_setup: Option<ServerSetup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigurationBuilder {
|
impl ConfigurationBuilder {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn build(self) -> Result<Configuration> {
|
pub fn build(self) -> Result<Configuration> {
|
||||||
let server_keys = get_server_keys(self.key_file.as_deref().unwrap_or("server_key"))?;
|
let server_setup = get_server_setup(self.key_file.as_deref().unwrap_or("server_key"))?;
|
||||||
Ok(self.server_keys(server_keys).private_build()?)
|
Ok(self.server_setup(server_setup).private_build()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self) -> Result<(), String> {
|
fn validate(&self) -> Result<(), String> {
|
||||||
if self.server_keys.is_none() {
|
if self.server_setup.is_none() {
|
||||||
Err("Don't use `private_build`, use `build` instead".to_string())
|
Err("Don't use `private_build`, use `build` instead".to_string())
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -47,8 +47,12 @@ impl ConfigurationBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Configuration {
|
impl Configuration {
|
||||||
|
pub fn get_server_setup(&self) -> &ServerSetup {
|
||||||
|
self.server_setup.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_server_keys(&self) -> &KeyPair {
|
pub fn get_server_keys(&self) -> &KeyPair {
|
||||||
self.server_keys.as_ref().unwrap()
|
self.get_server_setup().keypair()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_with_cli(mut self: Configuration, cli_opts: CLIOpts) -> Configuration {
|
fn merge_with_cli(mut self: Configuration, cli_opts: CLIOpts) -> Configuration {
|
||||||
@ -80,30 +84,29 @@ impl Configuration {
|
|||||||
database_url: String::from("sqlite://users.db?mode=rwc"),
|
database_url: String::from("sqlite://users.db?mode=rwc"),
|
||||||
verbose: false,
|
verbose: false,
|
||||||
key_file: String::from("server_key"),
|
key_file: String::from("server_key"),
|
||||||
server_keys: None,
|
server_setup: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_server_keys(file_path: &str) -> Result<KeyPair> {
|
fn get_server_setup(file_path: &str) -> Result<ServerSetup> {
|
||||||
use opaque_ke::ciphersuite::CipherSuite;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
let path = Path::new(file_path);
|
let path = Path::new(file_path);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
let bytes = std::fs::read(file_path)
|
let bytes = std::fs::read(file_path)
|
||||||
.map_err(|e| anyhow!("Could not read key file `{}`: {}", file_path, e))?;
|
.map_err(|e| anyhow!("Could not read key file `{}`: {}", file_path, e))?;
|
||||||
Ok(KeyPair::from_private_key_slice(&bytes)?)
|
Ok(ServerSetup::deserialize(&bytes)?)
|
||||||
} else {
|
} else {
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let keypair = opaque::DefaultSuite::generate_random_keypair(&mut rng);
|
let server_setup = ServerSetup::new(&mut rng);
|
||||||
std::fs::write(path, keypair.private().as_slice()).map_err(|e| {
|
std::fs::write(path, server_setup.serialize()).map_err(|e| {
|
||||||
anyhow!(
|
anyhow!(
|
||||||
"Could not write the generated server keys to file `{}`: {}",
|
"Could not write the generated server setup to file `{}`: {}",
|
||||||
file_path,
|
file_path,
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
Ok(keypair)
|
Ok(server_setup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +119,6 @@ pub fn init(cli_opts: CLIOpts) -> Result<Configuration> {
|
|||||||
.extract()?;
|
.extract()?;
|
||||||
|
|
||||||
let mut config = config.merge_with_cli(cli_opts);
|
let mut config = config.merge_with_cli(cli_opts);
|
||||||
config.server_keys = Some(get_server_keys(&config.key_file)?);
|
config.server_setup = Some(get_server_setup(&config.key_file)?);
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
domain::sql_tables::{DbQueryBuilder, LoginAttempts, Pool, RegistrationAttempts},
|
domain::sql_tables::{DbQueryBuilder, Pool},
|
||||||
infra::jwt_sql_tables::{JwtRefreshStorage, JwtStorage},
|
infra::jwt_sql_tables::{JwtRefreshStorage, JwtStorage},
|
||||||
};
|
};
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
@ -70,34 +70,6 @@ impl Scheduler {
|
|||||||
{
|
{
|
||||||
log::error!("DB error while cleaning up JWT storage: {}", e);
|
log::error!("DB error while cleaning up JWT storage: {}", e);
|
||||||
};
|
};
|
||||||
if let Err(e) = sqlx::query(
|
|
||||||
&Query::delete()
|
|
||||||
.from_table(LoginAttempts::Table)
|
|
||||||
.and_where(
|
|
||||||
Expr::col(LoginAttempts::Timestamp)
|
|
||||||
.lt(Local::now().naive_utc() - chrono::Duration::minutes(5)),
|
|
||||||
)
|
|
||||||
.to_string(DbQueryBuilder {}),
|
|
||||||
)
|
|
||||||
.execute(&sql_pool)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
log::error!("DB error while cleaning up login attempts: {}", e);
|
|
||||||
};
|
|
||||||
if let Err(e) = sqlx::query(
|
|
||||||
&Query::delete()
|
|
||||||
.from_table(RegistrationAttempts::Table)
|
|
||||||
.and_where(
|
|
||||||
Expr::col(RegistrationAttempts::Timestamp)
|
|
||||||
.lt(Local::now().naive_utc() - chrono::Duration::minutes(5)),
|
|
||||||
)
|
|
||||||
.to_string(DbQueryBuilder {}),
|
|
||||||
)
|
|
||||||
.execute(&sql_pool)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
log::error!("DB error while cleaning up registration attempts: {}", e);
|
|
||||||
};
|
|
||||||
log::info!("DB cleaned!");
|
log::info!("DB cleaned!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +32,11 @@ pub(crate) fn error_to_http_response(error: DomainError) -> HttpResponse {
|
|||||||
DomainError::AuthenticationError(_) | DomainError::AuthenticationProtocolError(_) => {
|
DomainError::AuthenticationError(_) | DomainError::AuthenticationProtocolError(_) => {
|
||||||
HttpResponse::Unauthorized()
|
HttpResponse::Unauthorized()
|
||||||
}
|
}
|
||||||
DomainError::DatabaseError(_) | DomainError::InternalError(_) => {
|
DomainError::DatabaseError(_)
|
||||||
HttpResponse::InternalServerError()
|
| DomainError::InternalError(_)
|
||||||
|
| DomainError::UnknownCryptoError(_) => HttpResponse::InternalServerError(),
|
||||||
|
DomainError::Base64DecodeError(_) | DomainError::BinarySerializationError(_) => {
|
||||||
|
HttpResponse::BadRequest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.body(error.to_string())
|
.body(error.to_string())
|
||||||
|
Loading…
Reference in New Issue
Block a user