use super::super::{
errors::{
forbidden_owner_only, invalid_params, mapped_errors_to_jsonrpc_error,
params_required,
},
method_names,
params::{
AcceptInvitationParams, CheckEmailPasswordValidityParams,
CheckTokenAndActivateUserParams, CheckTokenAndResetPasswordParams,
CreateAccountMetaParams, CreateConnectionStringParams,
CreateDefaultAccountParams, CreateDefaultUserParams,
DeleteAccountMetaParams, DeleteConnectionStringParams,
DeleteMyAccountParams, FetchMyProfileParams,
FetchTenantPublicInfoParams, RevokeConnectionStringParams,
StartPasswordRedefinitionParams, TotpCheckTokenParams,
TotpDisableParams, TotpFinishActivationParams,
TotpStartActivationParams, UpdateAccountMetaParams,
UpdateOwnAccountNameParams,
},
response_kind::{
create_response_kind_to_result, delete_response_kind_to_result,
fetch_many_response_kind_to_result, fetch_response_kind_to_result,
updating_response_kind_to_result,
},
types::{self, JsonRpcError},
};
use crate::{
dtos::MyceliumProfileData,
middleware::{
check_credentials_with_multi_identity_provider,
parse_issuer_from_request,
},
};
use actix_web::{web, HttpRequest};
use myc_core::{
domain::dtos::{
account::AccountMetaKey,
email::Email,
guest_role::Permission,
profile::{LicensedResources, TenantsOwnership},
security_group::PermissionedRole,
},
models::AccountLifeCycle,
use_cases::role_scoped::beginner::{
account::{
create_user_account, delete_my_account, get_my_account_details,
update_own_account_name,
},
guest_user::accept_invitation,
meta::{create_account_meta, delete_account_meta, update_account_meta},
tenant::fetch_tenant_public_info,
token::{
create_connection_string, delete_connection_string,
list_my_connection_strings, revoke_connection_string,
},
user::{
check_email_password_validity, check_token_and_activate_user,
check_token_and_reset_password, create_default_user,
start_password_redefinition, totp_check_token, totp_disable,
totp_finish_activation, totp_start_activation,
},
},
};
use myc_diesel::repositories::SqlAppModule;
use myc_http_tools::responses::GatewayError;
use myc_http_tools::settings::MYCELIUM_PROVIDER_KEY;
use shaku::HasComponent;
use std::str::FromStr;
use tracing::warn;
pub async fn dispatch_beginners(
profile: &MyceliumProfileData,
app_module: &web::Data<SqlAppModule>,
req: Option<&HttpRequest>,
life_cycle_settings: Option<&web::Data<AccountLifeCycle>>,
method: &str,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value, JsonRpcError> {
match method {
method_names::BEGINNERS_ACCOUNTS_CREATE => {
let req =
req.ok_or_else(|| invalid_params("Request context required"))?;
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let (email, external_provider) =
check_credentials_with_multi_identity_provider(req.to_owned())
.await
.map_err(|err| {
warn!("check_credentials err: {:?}", err);
JsonRpcError {
code: types::codes::INVALID_REQUEST,
message: err.to_string(),
data: None,
}
})?;
let issuer = if let Some(provider) = external_provider {
match provider.issuer.async_get_or_error().await {
Ok(issuer) => issuer,
Err(err) => {
warn!("issuer err: {:?}", err);
return Err(JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: err.to_string(),
data: None,
});
}
}
} else {
MYCELIUM_PROVIDER_KEY.to_string()
};
let p: CreateDefaultAccountParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let result = create_user_account(
email,
Some(issuer),
p.name,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(result).map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_ACCOUNTS_GET => {
let result = get_my_account_details(
profile.to_profile(),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
fetch_response_kind_to_result(result)
}
method_names::BEGINNERS_ACCOUNTS_UPDATE_NAME => {
let p: UpdateOwnAccountNameParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
if p.account_id != profile.acc_id {
warn!(
"Account {} trying to update {}",
profile.acc_id, p.account_id
);
return Err(forbidden_owner_only());
}
let result = update_own_account_name(
profile.to_profile(),
p.name,
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
updating_response_kind_to_result(result)
}
method_names::BEGINNERS_ACCOUNTS_DELETE => {
let p: DeleteMyAccountParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
if p.account_id != profile.acc_id {
warn!(
"Account {} trying to delete {}",
profile.acc_id, p.account_id
);
return Err(forbidden_owner_only());
}
let result = delete_my_account(
profile.to_profile(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
delete_response_kind_to_result(result)
}
method_names::BEGINNERS_GUESTS_ACCEPT_INVITATION => {
let p: AcceptInvitationParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let permission = Permission::from_i32(p.permission);
let result = accept_invitation(
profile.to_profile(),
p.account_id,
p.guest_role_name,
permission,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
updating_response_kind_to_result(result)
}
method_names::BEGINNERS_META_CREATE => {
let p: CreateAccountMetaParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let key = AccountMetaKey::from_str(&p.key)
.map_err(|_| invalid_params("The key is invalid"))?;
let result = create_account_meta(
profile.to_profile(),
key,
p.value,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
create_response_kind_to_result(result)
}
method_names::BEGINNERS_META_UPDATE => {
let p: UpdateAccountMetaParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let key = AccountMetaKey::from_str(&p.key)
.map_err(|_| invalid_params("The key is invalid"))?;
let result = update_account_meta(
profile.to_profile(),
key,
p.value,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
updating_response_kind_to_result(result)
}
method_names::BEGINNERS_META_DELETE => {
let p: DeleteAccountMetaParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let key = AccountMetaKey::from_str(&p.key)
.map_err(|_| invalid_params("The key is invalid"))?;
let result = delete_account_meta(
profile.to_profile(),
key,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(result).map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_PROFILE_GET => {
let p: FetchMyProfileParams = params
.map(serde_json::from_value)
.transpose()
.map_err(|e| invalid_params(e.to_string()))?
.unwrap_or_default();
let with_url = p.with_url.unwrap_or(true);
let mut profile_data = profile.clone();
if with_url {
if let Some(licensed_resources) =
profile_data.licensed_resources
{
let resources = match licensed_resources {
LicensedResources::Urls(urls) => urls,
LicensedResources::Records(records) => records
.iter()
.map(|r| r.to_string())
.collect::<Vec<String>>(),
};
profile_data.licensed_resources = if resources.is_empty() {
None
} else {
Some(LicensedResources::Urls(resources))
};
}
if let Some(tenants_ownership) = profile_data.tenants_ownership
{
let ownerships = match tenants_ownership {
TenantsOwnership::Urls(urls) => urls,
TenantsOwnership::Records(records) => records
.iter()
.map(|r| r.to_string())
.collect::<Vec<String>>(),
};
profile_data.tenants_ownership = if ownerships.is_empty() {
None
} else {
Some(TenantsOwnership::Urls(ownerships))
};
}
}
serde_json::to_value(profile_data.to_profile()).map_err(|e| {
JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
}
})
}
method_names::BEGINNERS_TENANTS_GET_PUBLIC_INFO => {
let p: FetchTenantPublicInfoParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let result = fetch_tenant_public_info(
profile.to_profile(),
p.tenant_id,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
fetch_response_kind_to_result(result)
}
method_names::BEGINNERS_TOKENS_CREATE => {
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let p: CreateConnectionStringParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let roles: Option<Vec<PermissionedRole>> = p.roles.map(|v| {
v.into_iter()
.map(|r| PermissionedRole {
name: r.name,
permission: r.permission.map(Permission::from_i32),
})
.collect()
});
let result = create_connection_string(
profile.to_profile(),
p.name,
p.expiration,
p.tenant_id,
p.service_account_id,
roles,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(
serde_json::json!({ "connectionString": result }),
)
.map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_TOKENS_LIST => {
let result = list_my_connection_strings(
profile.to_profile(),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
fetch_many_response_kind_to_result(result)
}
method_names::BEGINNERS_TOKENS_REVOKE => {
let p: RevokeConnectionStringParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let result = revoke_connection_string(
profile.to_profile(),
p.token_id,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
delete_response_kind_to_result(result)
}
method_names::BEGINNERS_TOKENS_DELETE => {
let p: DeleteConnectionStringParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let result = delete_connection_string(
profile.to_profile(),
p.token_id,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
delete_response_kind_to_result(result)
}
method_names::BEGINNERS_USERS => {
let req =
req.ok_or_else(|| invalid_params("Request context required"))?;
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let provider = match parse_issuer_from_request(req.to_owned()).await
{
Err(GatewayError::Unauthorized(_)) => None,
Err(err) => {
warn!("Invalid issuer: {:?}", err);
return Err(JsonRpcError {
code: types::codes::INVALID_REQUEST,
message: "Invalid issuer.".to_string(),
data: None,
});
}
Ok((issuer, _)) => Some(issuer),
};
let p: CreateDefaultUserParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let _ = create_default_user(
p.email,
p.first_name,
p.last_name,
p.password,
provider,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(
serde_json::json!({"message": "User created successfully"}),
)
.map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_CHECK_TOKEN_AND_ACTIVATE_USER => {
let p: CheckTokenAndActivateUserParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let result = check_token_and_activate_user(
p.token,
email,
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(result).map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_START_PASSWORD_REDEFINITION => {
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let p: StartPasswordRedefinitionParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let _ = start_password_redefinition(
email,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(true).map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_CHECK_TOKEN_AND_RESET_PASSWORD => {
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let p: CheckTokenAndResetPasswordParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let _ = check_token_and_reset_password(
p.token,
email,
p.new_password,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(true).map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_CHECK_EMAIL_PASSWORD_VALIDITY => {
let p: CheckEmailPasswordValidityParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let (valid, user) = check_email_password_validity(
email,
p.password,
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(serde_json::json!({
"valid": valid,
"user": user
}))
.map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_TOTP_START_ACTIVATION => {
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let p: TotpStartActivationParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let (totp_url, totp_secret) = totp_start_activation(
email,
p.qr_code,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(serde_json::json!({
"totpUrl": totp_url,
"totpSecret": totp_secret
}))
.map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_TOTP_FINISH_ACTIVATION => {
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let p: TotpFinishActivationParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let _ = totp_finish_activation(
email,
p.token,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(serde_json::json!({ "finished": true }))
.map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_TOTP_CHECK_TOKEN => {
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let p: TotpCheckTokenParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let user = totp_check_token(
email,
p.token,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(user).map_err(|e| JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
})
}
method_names::BEGINNERS_USERS_TOTP_DISABLE => {
let life_cycle = life_cycle_settings
.ok_or_else(|| invalid_params("Life cycle config required"))?
.get_ref();
let p: TotpDisableParams =
serde_json::from_value(params.ok_or_else(params_required)?)
.map_err(|e| invalid_params(e.to_string()))?;
let email = Email::from_string(p.email.clone())
.map_err(|e| invalid_params(e.to_string()))?;
let _ = totp_disable(
email,
p.token,
life_cycle.to_owned(),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
Box::new(&*app_module.resolve_ref()),
)
.await
.map_err(mapped_errors_to_jsonrpc_error)?;
serde_json::to_value(serde_json::json!({})).map_err(|e| {
JsonRpcError {
code: types::codes::INTERNAL_ERROR,
message: e.to_string(),
data: None,
}
})
}
_ => Err(JsonRpcError {
code: types::codes::METHOD_NOT_FOUND,
message: format!("Method not found: {}", method),
data: None,
}),
}
}