mirror of
https://github.com/lldap/lldap.git
synced 2024-11-25 09:06:03 +00:00
server: Move the definition of UserId down to lldap_auth
This commit is contained in:
parent
10609b25e9
commit
2ea17c04ba
18 changed files with 212 additions and 162 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2572,6 +2572,7 @@ dependencies = [
|
||||||
"opaque-ke",
|
"opaque-ke",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.9.9",
|
"sha2 0.9.9",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
@ -97,7 +97,7 @@ impl CommonComponent<ChangePasswordForm> for ChangePasswordForm {
|
||||||
.context("Could not initialize login")?;
|
.context("Could not initialize login")?;
|
||||||
self.opaque_data = OpaqueData::Login(login_start_request.state);
|
self.opaque_data = OpaqueData::Login(login_start_request.state);
|
||||||
let req = login::ClientLoginStartRequest {
|
let req = login::ClientLoginStartRequest {
|
||||||
username: ctx.props().username.clone(),
|
username: ctx.props().username.clone().into(),
|
||||||
login_start_request: login_start_request.message,
|
login_start_request: login_start_request.message,
|
||||||
};
|
};
|
||||||
self.common.call_backend(
|
self.common.call_backend(
|
||||||
|
@ -134,7 +134,7 @@ impl CommonComponent<ChangePasswordForm> for ChangePasswordForm {
|
||||||
)
|
)
|
||||||
.context("Could not initiate password change")?;
|
.context("Could not initiate password change")?;
|
||||||
let req = registration::ClientRegistrationStartRequest {
|
let req = registration::ClientRegistrationStartRequest {
|
||||||
username: ctx.props().username.clone(),
|
username: ctx.props().username.clone().into(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
};
|
};
|
||||||
self.opaque_data = OpaqueData::Registration(registration_start_request.state);
|
self.opaque_data = OpaqueData::Registration(registration_start_request.state);
|
||||||
|
|
|
@ -123,7 +123,7 @@ impl CommonComponent<CreateUserForm> for CreateUserForm {
|
||||||
&mut rng,
|
&mut rng,
|
||||||
)?;
|
)?;
|
||||||
let req = registration::ClientRegistrationStartRequest {
|
let req = registration::ClientRegistrationStartRequest {
|
||||||
username: user_id,
|
username: user_id.into(),
|
||||||
registration_start_request: message,
|
registration_start_request: message,
|
||||||
};
|
};
|
||||||
self.common
|
self.common
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl CommonComponent<LoginForm> for LoginForm {
|
||||||
opaque::client::login::start_login(&password, &mut rng)
|
opaque::client::login::start_login(&password, &mut rng)
|
||||||
.context("Could not initialize login")?;
|
.context("Could not initialize login")?;
|
||||||
let req = login::ClientLoginStartRequest {
|
let req = login::ClientLoginStartRequest {
|
||||||
username,
|
username: username.into(),
|
||||||
login_start_request: message,
|
login_start_request: message,
|
||||||
};
|
};
|
||||||
self.common
|
self.common
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl CommonComponent<ResetPasswordStep2Form> for ResetPasswordStep2Form {
|
||||||
opaque_registration::start_registration(new_password.as_bytes(), &mut rng)
|
opaque_registration::start_registration(new_password.as_bytes(), &mut rng)
|
||||||
.context("Could not initiate password change")?;
|
.context("Could not initiate password change")?;
|
||||||
let req = registration::ClientRegistrationStartRequest {
|
let req = registration::ClientRegistrationStartRequest {
|
||||||
username: self.username.clone().unwrap(),
|
username: self.username.as_ref().unwrap().into(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
};
|
};
|
||||||
self.opaque_data = Some(registration_start_request.state);
|
self.opaque_data = Some(registration_start_request.state);
|
||||||
|
|
|
@ -13,6 +13,7 @@ default = ["opaque_server", "opaque_client"]
|
||||||
opaque_server = []
|
opaque_server = []
|
||||||
opaque_client = []
|
opaque_client = []
|
||||||
js = []
|
js = []
|
||||||
|
sea_orm = ["dep:sea-orm"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rust-argon2 = "0.8"
|
rust-argon2 = "0.8"
|
||||||
|
@ -31,6 +32,12 @@ version = "0.6"
|
||||||
version = "*"
|
version = "*"
|
||||||
features = [ "serde" ]
|
features = [ "serde" ]
|
||||||
|
|
||||||
|
[dependencies.sea-orm]
|
||||||
|
version= "0.12"
|
||||||
|
default-features = false
|
||||||
|
features = ["macros"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
# For WASM targets, use the JS getrandom.
|
# For WASM targets, use the JS getrandom.
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.getrandom]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.getrandom]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
|
|
110
auth/src/lib.rs
110
auth/src/lib.rs
|
@ -9,17 +9,17 @@ pub mod opaque;
|
||||||
|
|
||||||
/// The messages for the 3-step OPAQUE and simple login process.
|
/// The messages for the 3-step OPAQUE and simple login process.
|
||||||
pub mod login {
|
pub mod login {
|
||||||
use super::*;
|
use super::{types::UserId, *};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ServerData {
|
pub struct ServerData {
|
||||||
pub username: String,
|
pub username: UserId,
|
||||||
pub server_login: opaque::server::login::ServerLogin,
|
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: UserId,
|
||||||
pub login_start_request: opaque::server::login::CredentialRequest,
|
pub login_start_request: opaque::server::login::CredentialRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@ pub mod login {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ClientSimpleLoginRequest {
|
pub struct ClientSimpleLoginRequest {
|
||||||
pub username: String,
|
pub username: UserId,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ClientSimpleLoginRequest {
|
impl fmt::Debug for ClientSimpleLoginRequest {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("ClientSimpleLoginRequest")
|
f.debug_struct("ClientSimpleLoginRequest")
|
||||||
.field("username", &self.username)
|
.field("username", &self.username.as_str())
|
||||||
.field("password", &"***********")
|
.field("password", &"***********")
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -63,16 +63,16 @@ pub mod login {
|
||||||
/// The messages for the 3-step OPAQUE registration process.
|
/// The messages for the 3-step OPAQUE registration process.
|
||||||
/// It is used to reset a user's password.
|
/// It is used to reset a user's password.
|
||||||
pub mod registration {
|
pub mod registration {
|
||||||
use super::*;
|
use super::{types::UserId, *};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ServerData {
|
pub struct ServerData {
|
||||||
pub username: String,
|
pub username: UserId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ClientRegistrationStartRequest {
|
pub struct ClientRegistrationStartRequest {
|
||||||
pub username: String,
|
pub username: UserId,
|
||||||
pub registration_start_request: opaque::server::registration::RegistrationRequest,
|
pub registration_start_request: opaque::server::registration::RegistrationRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +104,100 @@ pub mod password_reset {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod types {
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "sea_orm")]
|
||||||
|
use sea_orm::{DbErr, DeriveValueType, QueryResult, TryFromU64, Value};
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Default, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "sea_orm", derive(DeriveValueType))]
|
||||||
|
#[serde(from = "String")]
|
||||||
|
pub struct CaseInsensitiveString(String);
|
||||||
|
|
||||||
|
impl CaseInsensitiveString {
|
||||||
|
pub fn new(s: &str) -> Self {
|
||||||
|
Self(s.to_ascii_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_string(self) -> String {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for CaseInsensitiveString {
|
||||||
|
fn from(mut s: String) -> Self {
|
||||||
|
s.make_ascii_lowercase();
|
||||||
|
Self(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&String> for CaseInsensitiveString {
|
||||||
|
fn from(s: &String) -> Self {
|
||||||
|
Self::new(s.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for CaseInsensitiveString {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
Self::new(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Default, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "sea_orm", derive(DeriveValueType))]
|
||||||
|
#[serde(from = "CaseInsensitiveString")]
|
||||||
|
pub struct UserId(CaseInsensitiveString);
|
||||||
|
|
||||||
|
impl UserId {
|
||||||
|
pub fn new(s: &str) -> Self {
|
||||||
|
s.into()
|
||||||
|
}
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
pub fn into_string(self) -> String {
|
||||||
|
self.0.into_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> From<T> for UserId
|
||||||
|
where
|
||||||
|
T: Into<CaseInsensitiveString>,
|
||||||
|
{
|
||||||
|
fn from(s: T) -> Self {
|
||||||
|
Self(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for UserId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sea_orm")]
|
||||||
|
impl From<&UserId> for Value {
|
||||||
|
fn from(user_id: &UserId) -> Self {
|
||||||
|
user_id.as_str().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "sea_orm")]
|
||||||
|
impl TryFromU64 for UserId {
|
||||||
|
fn try_from_u64(_n: u64) -> Result<Self, DbErr> {
|
||||||
|
Err(DbErr::ConvertFromU64(
|
||||||
|
"UserId cannot be constructed from u64",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct JWTClaims {
|
pub struct JWTClaims {
|
||||||
pub exp: DateTime<Utc>,
|
pub exp: DateTime<Utc>,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::types::UserId;
|
||||||
use opaque_ke::ciphersuite::CipherSuite;
|
use opaque_ke::ciphersuite::CipherSuite;
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
|
@ -145,12 +146,12 @@ pub mod server {
|
||||||
pub fn start_registration(
|
pub fn start_registration(
|
||||||
server_setup: &ServerSetup,
|
server_setup: &ServerSetup,
|
||||||
registration_request: RegistrationRequest,
|
registration_request: RegistrationRequest,
|
||||||
username: &str,
|
username: &UserId,
|
||||||
) -> AuthenticationResult<ServerRegistrationStartResult> {
|
) -> AuthenticationResult<ServerRegistrationStartResult> {
|
||||||
Ok(ServerRegistration::start(
|
Ok(ServerRegistration::start(
|
||||||
server_setup,
|
server_setup,
|
||||||
registration_request,
|
registration_request,
|
||||||
username.as_bytes(),
|
username.as_str().as_bytes(),
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,14 +179,14 @@ pub mod server {
|
||||||
server_setup: &ServerSetup,
|
server_setup: &ServerSetup,
|
||||||
password_file: Option<ServerRegistration>,
|
password_file: Option<ServerRegistration>,
|
||||||
credential_request: CredentialRequest,
|
credential_request: CredentialRequest,
|
||||||
username: &str,
|
username: &UserId,
|
||||||
) -> AuthenticationResult<ServerLoginStartResult> {
|
) -> AuthenticationResult<ServerLoginStartResult> {
|
||||||
Ok(ServerLogin::start(
|
Ok(ServerLogin::start(
|
||||||
rng,
|
rng,
|
||||||
server_setup,
|
server_setup,
|
||||||
password_file,
|
password_file,
|
||||||
credential_request,
|
credential_request,
|
||||||
username.as_bytes(),
|
username.as_str().as_bytes(),
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginStartParameters::default(),
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ fn try_login(
|
||||||
let ClientLoginStartResult { state, message } =
|
let ClientLoginStartResult { state, message } =
|
||||||
start_login(password, &mut rng).context("Could not initialize login")?;
|
start_login(password, &mut rng).context("Could not initialize login")?;
|
||||||
let req = ClientLoginStartRequest {
|
let req = ClientLoginStartRequest {
|
||||||
username: username.to_owned(),
|
username: username.into(),
|
||||||
login_start_request: message,
|
login_start_request: message,
|
||||||
};
|
};
|
||||||
let response = client
|
let response = client
|
||||||
|
|
|
@ -79,6 +79,7 @@ version = "0.10.1"
|
||||||
|
|
||||||
[dependencies.lldap_auth]
|
[dependencies.lldap_auth]
|
||||||
path = "../auth"
|
path = "../auth"
|
||||||
|
features = ["opaque_server", "opaque_client", "sea_orm"]
|
||||||
|
|
||||||
[dependencies.opaque-ke]
|
[dependencies.opaque-ke]
|
||||||
version = "0.6"
|
version = "0.6"
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub mod tests {
|
||||||
opaque::client::registration::start_registration(pass.as_bytes(), &mut rng).unwrap();
|
opaque::client::registration::start_registration(pass.as_bytes(), &mut rng).unwrap();
|
||||||
let response = handler
|
let response = handler
|
||||||
.registration_start(registration::ClientRegistrationStartRequest {
|
.registration_start(registration::ClientRegistrationStartRequest {
|
||||||
username: name.to_string(),
|
username: name.into(),
|
||||||
registration_start_request: client_registration_start.message,
|
registration_start_request: client_registration_start.message,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -33,7 +33,7 @@ fn passwords_match(
|
||||||
server_setup,
|
server_setup,
|
||||||
Some(password_file),
|
Some(password_file),
|
||||||
client_login_start_result.message,
|
client_login_start_result.message,
|
||||||
username.as_str(),
|
username,
|
||||||
)?;
|
)?;
|
||||||
client::login::finish_login(
|
client::login::finish_login(
|
||||||
client_login_start_result.state,
|
client_login_start_result.state,
|
||||||
|
@ -100,15 +100,13 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||||
&self,
|
&self,
|
||||||
request: login::ClientLoginStartRequest,
|
request: login::ClientLoginStartRequest,
|
||||||
) -> Result<login::ServerLoginStartResponse> {
|
) -> Result<login::ServerLoginStartResponse> {
|
||||||
|
let user_id = request.username;
|
||||||
let maybe_password_file = self
|
let maybe_password_file = self
|
||||||
.get_password_file_for_user(UserId::new(&request.username))
|
.get_password_file_for_user(user_id.clone())
|
||||||
.await?
|
.await?
|
||||||
.map(|bytes| {
|
.map(|bytes| {
|
||||||
opaque::server::ServerRegistration::deserialize(&bytes).map_err(|_| {
|
opaque::server::ServerRegistration::deserialize(&bytes).map_err(|_| {
|
||||||
DomainError::InternalError(format!(
|
DomainError::InternalError(format!("Corrupted password file for {}", &user_id))
|
||||||
"Corrupted password file for {}",
|
|
||||||
&request.username
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
@ -120,11 +118,11 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||||
self.config.get_server_setup(),
|
self.config.get_server_setup(),
|
||||||
maybe_password_file,
|
maybe_password_file,
|
||||||
request.login_start_request,
|
request.login_start_request,
|
||||||
&request.username,
|
&user_id,
|
||||||
)?;
|
)?;
|
||||||
let secret_key = self.get_orion_secret_key()?;
|
let secret_key = self.get_orion_secret_key()?;
|
||||||
let server_data = login::ServerData {
|
let server_data = login::ServerData {
|
||||||
username: request.username,
|
username: user_id,
|
||||||
server_login: start_response.state,
|
server_login: start_response.state,
|
||||||
};
|
};
|
||||||
let encrypted_state = orion::aead::seal(&secret_key, &bincode::serialize(&server_data)?)?;
|
let encrypted_state = orion::aead::seal(&secret_key, &bincode::serialize(&server_data)?)?;
|
||||||
|
@ -151,7 +149,7 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||||
opaque::server::login::finish_login(server_login, request.credential_finalization)?
|
opaque::server::login::finish_login(server_login, request.credential_finalization)?
|
||||||
.session_key;
|
.session_key;
|
||||||
|
|
||||||
Ok(UserId::new(&username))
|
Ok(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all, level = "debug", err)]
|
#[instrument(skip_all, level = "debug", err)]
|
||||||
|
@ -191,7 +189,7 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||||
opaque::server::registration::get_password_file(request.registration_upload);
|
opaque::server::registration::get_password_file(request.registration_upload);
|
||||||
// Set the user password to the new password.
|
// Set the user password to the new password.
|
||||||
let user_update = model::users::ActiveModel {
|
let user_update = model::users::ActiveModel {
|
||||||
user_id: ActiveValue::Set(UserId::new(&username)),
|
user_id: ActiveValue::Set(username),
|
||||||
password_hash: ActiveValue::Set(Some(password_file.serialize())),
|
password_hash: ActiveValue::Set(Some(password_file.serialize())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
@ -204,7 +202,7 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||||
#[instrument(skip_all, level = "debug", err, fields(username = %username.as_str()))]
|
#[instrument(skip_all, level = "debug", err, fields(username = %username.as_str()))]
|
||||||
pub(crate) async fn register_password(
|
pub(crate) async fn register_password(
|
||||||
opaque_handler: &SqlOpaqueHandler,
|
opaque_handler: &SqlOpaqueHandler,
|
||||||
username: &UserId,
|
username: UserId,
|
||||||
password: &SecUtf8,
|
password: &SecUtf8,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
|
@ -213,7 +211,7 @@ pub(crate) async fn register_password(
|
||||||
opaque::client::registration::start_registration(password.unsecure().as_bytes(), &mut rng)?;
|
opaque::client::registration::start_registration(password.unsecure().as_bytes(), &mut rng)?;
|
||||||
let start_response = opaque_handler
|
let start_response = opaque_handler
|
||||||
.registration_start(ClientRegistrationStartRequest {
|
.registration_start(ClientRegistrationStartRequest {
|
||||||
username: username.to_string(),
|
username,
|
||||||
registration_start_request: registration_start.message,
|
registration_start_request: registration_start.message,
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -245,7 +243,7 @@ mod tests {
|
||||||
let login_start = opaque::client::login::start_login(password, &mut rng)?;
|
let login_start = opaque::client::login::start_login(password, &mut rng)?;
|
||||||
let start_response = opaque_handler
|
let start_response = opaque_handler
|
||||||
.login_start(ClientLoginStartRequest {
|
.login_start(ClientLoginStartRequest {
|
||||||
username: username.to_string(),
|
username: UserId::new(username),
|
||||||
login_start_request: login_start.message,
|
login_start_request: login_start.message,
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -276,7 +274,7 @@ mod tests {
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
register_password(
|
register_password(
|
||||||
&opaque_handler,
|
&opaque_handler,
|
||||||
&UserId::new("bob"),
|
UserId::new("bob"),
|
||||||
&secstr::SecUtf8::from("bob00"),
|
&secstr::SecUtf8::from("bob00"),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::cmp::Ordering;
|
||||||
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use chrono::{NaiveDateTime, TimeZone};
|
use chrono::{NaiveDateTime, TimeZone};
|
||||||
|
use lldap_auth::types::CaseInsensitiveString;
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
entity::IntoActiveValue,
|
entity::IntoActiveValue,
|
||||||
sea_query::{value::ValueType, ArrayType, BlobSize, ColumnType, Nullable, ValueTypeErr},
|
sea_query::{value::ValueType, ArrayType, BlobSize, ColumnType, Nullable, ValueTypeErr},
|
||||||
|
@ -11,6 +12,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use strum::{EnumString, IntoStaticStr};
|
use strum::{EnumString, IntoStaticStr};
|
||||||
|
|
||||||
pub use super::model::UserColumn;
|
pub use super::model::UserColumn;
|
||||||
|
pub use lldap_auth::types::UserId;
|
||||||
|
|
||||||
#[derive(PartialEq, Hash, Eq, Clone, Debug, Default, Serialize, Deserialize, DeriveValueType)]
|
#[derive(PartialEq, Hash, Eq, Clone, Debug, Default, Serialize, Deserialize, DeriveValueType)]
|
||||||
#[serde(try_from = "&str")]
|
#[serde(try_from = "&str")]
|
||||||
|
@ -122,112 +124,6 @@ impl Serialized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
PartialOrd,
|
|
||||||
Ord,
|
|
||||||
Clone,
|
|
||||||
Debug,
|
|
||||||
Default,
|
|
||||||
Hash,
|
|
||||||
Serialize,
|
|
||||||
Deserialize,
|
|
||||||
DeriveValueType,
|
|
||||||
)]
|
|
||||||
#[serde(from = "String")]
|
|
||||||
pub struct CaseInsensitiveString(String);
|
|
||||||
|
|
||||||
impl CaseInsensitiveString {
|
|
||||||
pub fn new(user_id: &str) -> Self {
|
|
||||||
Self(user_id.to_lowercase())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
self.0.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_string(self) -> String {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for CaseInsensitiveString {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Self::new(&s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! make_case_insensitive_string {
|
|
||||||
($c:ident) => {
|
|
||||||
#[derive(
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
PartialOrd,
|
|
||||||
Ord,
|
|
||||||
Clone,
|
|
||||||
Debug,
|
|
||||||
Default,
|
|
||||||
Hash,
|
|
||||||
Serialize,
|
|
||||||
Deserialize,
|
|
||||||
DeriveValueType,
|
|
||||||
)]
|
|
||||||
#[serde(from = "CaseInsensitiveString")]
|
|
||||||
pub struct $c(CaseInsensitiveString);
|
|
||||||
|
|
||||||
impl $c {
|
|
||||||
pub fn new(raw: &str) -> Self {
|
|
||||||
Self(CaseInsensitiveString::new(raw))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
self.0.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_string(self) -> String {
|
|
||||||
self.0.into_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CaseInsensitiveString> for $c {
|
|
||||||
fn from(s: CaseInsensitiveString) -> Self {
|
|
||||||
Self(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for $c {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Self(CaseInsensitiveString::from(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for $c {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
Self::new(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for $c {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.0.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&$c> for Value {
|
|
||||||
fn from(user_id: &$c) -> Self {
|
|
||||||
user_id.as_str().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFromU64 for $c {
|
|
||||||
fn try_from_u64(_n: u64) -> Result<Self, DbErr> {
|
|
||||||
Err(DbErr::ConvertFromU64("$c cannot be constructed from u64"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare_str_case_insensitive(s1: &str, s2: &str) -> Ordering {
|
fn compare_str_case_insensitive(s1: &str, s2: &str) -> Ordering {
|
||||||
let mut it_1 = s1.chars().flat_map(|c| c.to_lowercase());
|
let mut it_1 = s1.chars().flat_map(|c| c.to_lowercase());
|
||||||
let mut it_2 = s2.chars().flat_map(|c| c.to_lowercase());
|
let mut it_2 = s2.chars().flat_map(|c| c.to_lowercase());
|
||||||
|
@ -323,8 +219,58 @@ macro_rules! make_case_insensitive_comparable_string {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
make_case_insensitive_string!(UserId);
|
#[derive(
|
||||||
make_case_insensitive_string!(AttributeName);
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Hash,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
DeriveValueType,
|
||||||
|
)]
|
||||||
|
#[serde(from = "CaseInsensitiveString")]
|
||||||
|
pub struct AttributeName(CaseInsensitiveString);
|
||||||
|
|
||||||
|
impl AttributeName {
|
||||||
|
pub fn new(s: &str) -> Self {
|
||||||
|
s.into()
|
||||||
|
}
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
pub fn into_string(self) -> String {
|
||||||
|
self.0.into_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> From<T> for AttributeName
|
||||||
|
where
|
||||||
|
T: Into<CaseInsensitiveString>,
|
||||||
|
{
|
||||||
|
fn from(s: T) -> Self {
|
||||||
|
Self(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for AttributeName {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&AttributeName> for Value {
|
||||||
|
fn from(attribute_name: &AttributeName) -> Self {
|
||||||
|
attribute_name.as_str().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl TryFromU64 for AttributeName {
|
||||||
|
fn try_from_u64(_n: u64) -> Result<Self, DbErr> {
|
||||||
|
Err(DbErr::ConvertFromU64(
|
||||||
|
"AttributeName cannot be constructed from u64",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
make_case_insensitive_comparable_string!(Email);
|
make_case_insensitive_comparable_string!(Email);
|
||||||
make_case_insensitive_comparable_string!(GroupName);
|
make_case_insensitive_comparable_string!(GroupName);
|
||||||
|
|
||||||
|
|
|
@ -428,13 +428,13 @@ async fn simple_login<Backend>(
|
||||||
where
|
where
|
||||||
Backend: TcpBackendHandler + BackendHandler + OpaqueHandler + LoginHandler + 'static,
|
Backend: TcpBackendHandler + BackendHandler + OpaqueHandler + LoginHandler + 'static,
|
||||||
{
|
{
|
||||||
let user_id = UserId::new(&request.username);
|
let login::ClientSimpleLoginRequest { username, password } = request.into_inner();
|
||||||
let bind_request = BindRequest {
|
let bind_request = BindRequest {
|
||||||
name: user_id.clone(),
|
name: username.clone(),
|
||||||
password: request.password.clone(),
|
password,
|
||||||
};
|
};
|
||||||
data.get_login_handler().bind(bind_request).await?;
|
data.get_login_handler().bind(bind_request).await?;
|
||||||
get_login_successful_response(&data, &user_id).await
|
get_login_successful_response(&data, &username).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn simple_login_handler<Backend>(
|
async fn simple_login_handler<Backend>(
|
||||||
|
@ -500,14 +500,14 @@ where
|
||||||
.await
|
.await
|
||||||
.map_err(|e| TcpError::BadRequest(format!("{:#?}", e)))?
|
.map_err(|e| TcpError::BadRequest(format!("{:#?}", e)))?
|
||||||
.into_inner();
|
.into_inner();
|
||||||
let user_id = UserId::new(®istration_start_request.username);
|
let user_id = ®istration_start_request.username;
|
||||||
let user_is_admin = data
|
let user_is_admin = data
|
||||||
.get_readonly_handler()
|
.get_readonly_handler()
|
||||||
.get_user_groups(&user_id)
|
.get_user_groups(user_id)
|
||||||
.await?
|
.await?
|
||||||
.iter()
|
.iter()
|
||||||
.any(|g| g.display_name == "lldap_admin".into());
|
.any(|g| g.display_name == "lldap_admin".into());
|
||||||
if !validation_result.can_change_password(&user_id, user_is_admin) {
|
if !validation_result.can_change_password(user_id, user_is_admin) {
|
||||||
return Err(TcpError::UnauthorizedError(
|
return Err(TcpError::UnauthorizedError(
|
||||||
"Not authorized to change the user's password".to_string(),
|
"Not authorized to change the user's password".to_string(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -306,7 +306,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||||
async fn change_password<B: OpaqueHandler>(
|
async fn change_password<B: OpaqueHandler>(
|
||||||
&self,
|
&self,
|
||||||
backend_handler: &B,
|
backend_handler: &B,
|
||||||
user: &UserId,
|
user: UserId,
|
||||||
password: &[u8],
|
password: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use lldap_auth::*;
|
use lldap_auth::*;
|
||||||
|
@ -314,7 +314,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||||
let registration_start_request =
|
let registration_start_request =
|
||||||
opaque::client::registration::start_registration(password, &mut rng)?;
|
opaque::client::registration::start_registration(password, &mut rng)?;
|
||||||
let req = registration::ClientRegistrationStartRequest {
|
let req = registration::ClientRegistrationStartRequest {
|
||||||
username: user.to_string(),
|
username: user.clone(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
};
|
};
|
||||||
let registration_start_response = backend_handler.registration_start(req).await?;
|
let registration_start_response = backend_handler.registration_start(req).await?;
|
||||||
|
@ -371,7 +371,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
} else if let Err(e) = self
|
} else if let Err(e) = self
|
||||||
.change_password(self.get_opaque_handler(), &uid, password.as_bytes())
|
.change_password(self.get_opaque_handler(), uid, password.as_bytes())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(LdapError {
|
Err(LdapError {
|
||||||
|
@ -413,7 +413,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||||
|
|
||||||
async fn handle_modify_change(
|
async fn handle_modify_change(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: &UserId,
|
user_id: UserId,
|
||||||
credentials: &ValidationResults,
|
credentials: &ValidationResults,
|
||||||
user_is_admin: bool,
|
user_is_admin: bool,
|
||||||
change: &LdapModify,
|
change: &LdapModify,
|
||||||
|
@ -429,7 +429,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if !credentials.can_change_password(user_id, user_is_admin) {
|
if !credentials.can_change_password(&user_id, user_is_admin) {
|
||||||
return Err(LdapError {
|
return Err(LdapError {
|
||||||
code: LdapResultCode::InsufficentAccessRights,
|
code: LdapResultCode::InsufficentAccessRights,
|
||||||
message: format!(
|
message: format!(
|
||||||
|
@ -488,7 +488,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
||||||
.iter()
|
.iter()
|
||||||
.any(|g| g.display_name == "lldap_admin".into());
|
.any(|g| g.display_name == "lldap_admin".into());
|
||||||
for change in &request.changes {
|
for change in &request.changes {
|
||||||
self.handle_modify_change(&uid, &credentials, user_is_admin, change)
|
self.handle_modify_change(uid.clone(), &credentials, user_is_admin, change)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
Ok(vec![make_modify_response(
|
Ok(vec![make_modify_response(
|
||||||
|
@ -2199,7 +2199,7 @@ mod tests {
|
||||||
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let request = registration::ClientRegistrationStartRequest {
|
let request = registration::ClientRegistrationStartRequest {
|
||||||
username: "bob".to_string(),
|
username: "bob".into(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
};
|
};
|
||||||
let start_response = opaque::server::registration::start_registration(
|
let start_response = opaque::server::registration::start_registration(
|
||||||
|
@ -2247,7 +2247,7 @@ mod tests {
|
||||||
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let request = registration::ClientRegistrationStartRequest {
|
let request = registration::ClientRegistrationStartRequest {
|
||||||
username: "bob".to_string(),
|
username: "bob".into(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
};
|
};
|
||||||
let start_response = opaque::server::registration::start_registration(
|
let start_response = opaque::server::registration::start_registration(
|
||||||
|
@ -2297,7 +2297,7 @@ mod tests {
|
||||||
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let request = registration::ClientRegistrationStartRequest {
|
let request = registration::ClientRegistrationStartRequest {
|
||||||
username: "bob".to_string(),
|
username: "bob".into(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
};
|
};
|
||||||
let start_response = opaque::server::registration::start_registration(
|
let start_response = opaque::server::registration::start_registration(
|
||||||
|
|
|
@ -46,7 +46,9 @@ async fn create_admin_user(handler: &SqlBackendHandler, config: &Configuration)
|
||||||
display_name: Some("Administrator".to_string()),
|
display_name: Some("Administrator".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.and_then(|_| register_password(handler, &config.ldap_user_dn, &config.ldap_user_pass))
|
.and_then(|_| {
|
||||||
|
register_password(handler, config.ldap_user_dn.clone(), &config.ldap_user_pass)
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.context("Error creating admin user")?;
|
.context("Error creating admin user")?;
|
||||||
let groups = handler
|
let groups = handler
|
||||||
|
@ -137,7 +139,7 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
|
||||||
warn!("Forcing admin password reset to the config-provided password");
|
warn!("Forcing admin password reset to the config-provided password");
|
||||||
register_password(
|
register_password(
|
||||||
&backend_handler,
|
&backend_handler,
|
||||||
&config.ldap_user_dn,
|
config.ldap_user_dn.clone(),
|
||||||
&config.ldap_user_pass,
|
&config.ldap_user_pass,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub fn get_token(client: &Client) -> String {
|
||||||
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||||
.body(
|
.body(
|
||||||
serde_json::to_string(&lldap_auth::login::ClientSimpleLoginRequest {
|
serde_json::to_string(&lldap_auth::login::ClientSimpleLoginRequest {
|
||||||
username,
|
username: username.into(),
|
||||||
password,
|
password,
|
||||||
})
|
})
|
||||||
.expect("Failed to encode the username/password as json to log in"),
|
.expect("Failed to encode the username/password as json to log in"),
|
||||||
|
|
|
@ -49,7 +49,7 @@ fn get_token(base_url: &Url, username: &str, password: &str) -> Result<String> {
|
||||||
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||||
.body(
|
.body(
|
||||||
serde_json::to_string(&lldap_auth::login::ClientSimpleLoginRequest {
|
serde_json::to_string(&lldap_auth::login::ClientSimpleLoginRequest {
|
||||||
username: username.to_string(),
|
username: username.into(),
|
||||||
password: password.to_string(),
|
password: password.to_string(),
|
||||||
})
|
})
|
||||||
.expect("Failed to encode the username/password as json to log in"),
|
.expect("Failed to encode the username/password as json to log in"),
|
||||||
|
@ -121,7 +121,7 @@ fn main() -> Result<()> {
|
||||||
opaque::client::registration::start_registration(opts.password.as_bytes(), &mut rng)
|
opaque::client::registration::start_registration(opts.password.as_bytes(), &mut rng)
|
||||||
.context("Could not initiate password change")?;
|
.context("Could not initiate password change")?;
|
||||||
let start_request = registration::ClientRegistrationStartRequest {
|
let start_request = registration::ClientRegistrationStartRequest {
|
||||||
username: opts.username.to_string(),
|
username: opts.username.clone().into(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
};
|
};
|
||||||
let res = register_start(&opts.base_url, &token, start_request)?;
|
let res = register_start(&opts.base_url, &token, start_request)?;
|
||||||
|
|
Loading…
Reference in a new issue