1
0
Fork 0
mirror of https://forgejo.ellis.link/continuwuation/continuwuity.git synced 2025-08-01 20:58:31 +00:00
continuwuity/src/service/uiaa/mod.rs

146 lines
4.6 KiB
Rust
Raw Normal View History

mod data;
2022-10-08 13:04:55 +02:00
pub use data::Data;
2022-09-07 13:25:51 +02:00
2022-10-05 20:34:31 +02:00
use ruma::{
api::client::{
error::ErrorKind,
uiaa::{AuthType, IncomingAuthData, IncomingPassword, IncomingUserIdentifier, UiaaInfo},
},
2022-10-09 17:25:06 +02:00
CanonicalJsonValue, DeviceId, UserId,
2022-10-05 20:34:31 +02:00
};
use tracing::error;
2021-06-08 18:10:00 +02:00
2022-10-05 20:34:31 +02:00
use crate::{api::client_server::SESSION_ID_LENGTH, services, utils, Error, Result};
2020-06-06 18:44:50 +02:00
2022-10-05 12:45:54 +02:00
pub struct Service {
2022-10-08 13:02:52 +02:00
pub db: &'static dyn Data,
2020-06-06 18:44:50 +02:00
}
2022-10-05 12:45:54 +02:00
impl Service {
2020-06-06 18:44:50 +02:00
/// Creates a new Uiaa session. Make sure the session token is unique.
2020-07-25 14:25:24 -04:00
pub fn create(
&self,
user_id: &UserId,
device_id: &DeviceId,
uiaainfo: &UiaaInfo,
json_body: &CanonicalJsonValue,
2020-07-25 14:25:24 -04:00
) -> Result<()> {
2022-10-05 20:33:55 +02:00
self.db.set_uiaa_request(
user_id,
device_id,
uiaainfo.session.as_ref().expect("session should be set"), // TODO: better session error handling (why is it optional in ruma?)
json_body,
)?;
2022-10-05 20:33:55 +02:00
self.db.update_uiaa_session(
user_id,
device_id,
uiaainfo.session.as_ref().expect("session should be set"),
Some(uiaainfo),
)
2020-06-06 18:44:50 +02:00
}
pub fn try_auth(
&self,
user_id: &UserId,
2020-07-25 14:25:24 -04:00
device_id: &DeviceId,
auth: &IncomingAuthData,
2020-06-06 18:44:50 +02:00
uiaainfo: &UiaaInfo,
) -> Result<(bool, UiaaInfo)> {
let mut uiaainfo = auth
.session()
2022-10-05 20:33:55 +02:00
.map(|session| self.db.get_uiaa_session(user_id, device_id, session))
.unwrap_or_else(|| Ok(uiaainfo.clone()))?;
2020-06-06 18:44:50 +02:00
if uiaainfo.session.is_none() {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
}
match auth {
2020-06-06 18:44:50 +02:00
// Find out what the user completed
IncomingAuthData::Password(IncomingPassword {
identifier,
password,
..
}) => {
let username = match identifier {
2022-09-07 13:25:51 +02:00
IncomingUserIdentifier::UserIdOrLocalpart(username) => username,
_ => {
2020-06-09 15:13:17 +02:00
return Err(Error::BadRequest(
ErrorKind::Unrecognized,
"Identifier type not recognized.",
))
2020-06-06 18:44:50 +02:00
}
};
2020-06-06 18:44:50 +02:00
2022-10-05 20:34:31 +02:00
let user_id = UserId::parse_with_server_name(
username.clone(),
services().globals.server_name(),
)
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "User ID is invalid."))?;
// Check if password is correct
2022-09-07 13:25:51 +02:00
if let Some(hash) = services().users.password_hash(&user_id)? {
let hash_matches =
argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false);
if !hash_matches {
uiaainfo.auth_error = Some(ruma::api::client::error::ErrorBody {
kind: ErrorKind::Forbidden,
message: "Invalid username or password.".to_owned(),
});
return Ok((false, uiaainfo));
2020-06-06 18:44:50 +02:00
}
}
// Password was correct! Let's add it to `completed`
uiaainfo.completed.push(AuthType::Password);
}
IncomingAuthData::Dummy(_) => {
uiaainfo.completed.push(AuthType::Dummy);
2020-06-06 18:44:50 +02:00
}
k => error!("type not supported: {:?}", k),
}
2020-06-06 18:44:50 +02:00
// Check if a flow now succeeds
let mut completed = false;
'flows: for flow in &mut uiaainfo.flows {
for stage in &flow.stages {
if !uiaainfo.completed.contains(stage) {
continue 'flows;
}
2020-06-06 18:44:50 +02:00
}
// We didn't break, so this flow succeeded!
completed = true;
}
2020-06-06 18:44:50 +02:00
if !completed {
2022-10-05 20:33:55 +02:00
self.db.update_uiaa_session(
user_id,
device_id,
uiaainfo.session.as_ref().expect("session is always set"),
Some(&uiaainfo),
)?;
return Ok((false, uiaainfo));
2020-06-06 18:44:50 +02:00
}
// UIAA was successful! Remove this session and return true
2022-10-05 20:33:55 +02:00
self.db.update_uiaa_session(
user_id,
device_id,
uiaainfo.session.as_ref().expect("session is always set"),
None,
)?;
Ok((true, uiaainfo))
2020-06-06 18:44:50 +02:00
}
pub fn get_uiaa_request(
&self,
user_id: &UserId,
device_id: &DeviceId,
session: &str,
2022-01-19 23:56:55 +01:00
) -> Option<CanonicalJsonValue> {
self.db.get_uiaa_request(user_id, device_id, session)
}
2020-06-06 18:44:50 +02:00
}