Compare commits
27 Commits
user-attri
...
user-ui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7119d96712 | ||
|
|
2973529c97 | ||
|
|
442c70b6d2 | ||
|
|
64140b4939 | ||
|
|
6ebeee4126 | ||
|
|
a05ae617a1 | ||
|
|
7538059f6a | ||
|
|
ee4a62e1e2 | ||
|
|
8a6ce87fb5 | ||
|
|
af670dbc93 | ||
|
|
5840b3009d | ||
|
|
18f814ba02 | ||
|
|
b55caae3cc | ||
|
|
93b4840e93 | ||
|
|
e0e0da9ebf | ||
|
|
3316f54133 | ||
|
|
c012c2891b | ||
|
|
d459ac0c78 | ||
|
|
c9f9a687a3 | ||
|
|
4c47d06c9b | ||
|
|
e88db526b4 | ||
|
|
e947b8eef0 | ||
|
|
ee72b571d0 | ||
|
|
cf492db570 | ||
|
|
6120a0dca5 | ||
|
|
523d418459 | ||
|
|
55225bc15b |
@@ -37,11 +37,9 @@ version = "0.3"
|
|||||||
features = [
|
features = [
|
||||||
"Document",
|
"Document",
|
||||||
"Element",
|
"Element",
|
||||||
"Event",
|
|
||||||
"FileReader",
|
"FileReader",
|
||||||
"FormData",
|
"FormData",
|
||||||
"HtmlDocument",
|
"HtmlDocument",
|
||||||
"HtmlFormElement",
|
|
||||||
"HtmlInputElement",
|
"HtmlInputElement",
|
||||||
"HtmlOptionElement",
|
"HtmlOptionElement",
|
||||||
"HtmlOptionsCollection",
|
"HtmlOptionsCollection",
|
||||||
|
|||||||
@@ -11,15 +11,17 @@ query GetUserDetails($id: String!) {
|
|||||||
groups {
|
groups {
|
||||||
id
|
id
|
||||||
displayName
|
displayName
|
||||||
}
|
}
|
||||||
attributes {
|
attributes {
|
||||||
name
|
name
|
||||||
value
|
value
|
||||||
schema {
|
}
|
||||||
|
}
|
||||||
|
schema {
|
||||||
|
user_attrubutes {
|
||||||
|
attributes {
|
||||||
name
|
name
|
||||||
attributeType
|
attributeType
|
||||||
isList
|
|
||||||
isVisible
|
|
||||||
isEditable
|
isEditable
|
||||||
isHardcoded
|
isHardcoded
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
|
use yew::{function_component, html, virtual_dom::AttrValue, Callback, InputEvent, Properties, NodeRef};
|
||||||
use crate::infra::schema::AttributeType;
|
use crate::infra::schema::AttributeType;
|
||||||
use yew::{
|
|
||||||
function_component, html, virtual_dom::AttrValue, Callback, InputEvent, NodeRef, Properties,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
<input
|
<input
|
||||||
@@ -45,7 +43,7 @@ fn attribute_input(props: &AttributeInputProps) -> Html {
|
|||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct SingleAttributeInputProps {
|
pub struct SingleAttributeInputProps {
|
||||||
pub name: String,
|
pub name: AttrValue,
|
||||||
pub attribute_type: AttributeType,
|
pub attribute_type: AttributeType,
|
||||||
#[prop_or(None)]
|
#[prop_or(None)]
|
||||||
pub value: Option<String>,
|
pub value: Option<String>,
|
||||||
@@ -61,9 +59,9 @@ pub fn single_attribute_input(props: &SingleAttributeInputProps) -> Html {
|
|||||||
</label>
|
</label>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<AttributeInput
|
<AttributeInput
|
||||||
attribute_type={props.attribute_type.clone()}
|
attribute_type={props.attribute_type}
|
||||||
name={props.name.clone()}
|
name={props.name}
|
||||||
value={props.value.clone()} />
|
value={props.value} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use crate::{
|
|||||||
remove_user_from_group::RemoveUserFromGroupComponent,
|
remove_user_from_group::RemoveUserFromGroupComponent,
|
||||||
router::{AppRoute, Link},
|
router::{AppRoute, Link},
|
||||||
user_details_form::UserDetailsForm,
|
user_details_form::UserDetailsForm,
|
||||||
}, infra::{schema::AttributeType, common_component::{CommonComponent, CommonComponentParts}},
|
},
|
||||||
convert_attribute_type
|
infra::common_component::{CommonComponent, CommonComponentParts},
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Error, Result};
|
use anyhow::{bail, Error, Result};
|
||||||
use graphql_client::GraphQLQuery;
|
use graphql_client::GraphQLQuery;
|
||||||
@@ -22,10 +22,6 @@ pub struct GetUserDetails;
|
|||||||
|
|
||||||
pub type User = get_user_details::GetUserDetailsUser;
|
pub type User = get_user_details::GetUserDetailsUser;
|
||||||
pub type Group = get_user_details::GetUserDetailsUserGroups;
|
pub type Group = get_user_details::GetUserDetailsUserGroups;
|
||||||
pub type Attribute = get_user_details::GetUserDetailsUserAttributes;
|
|
||||||
pub type AttributeSchema = get_user_details::GetUserDetailsUserAttributesSchema;
|
|
||||||
|
|
||||||
convert_attribute_type!(get_user_details::AttributeType);
|
|
||||||
|
|
||||||
pub struct UserDetails {
|
pub struct UserDetails {
|
||||||
common: CommonComponentParts<Self>,
|
common: CommonComponentParts<Self>,
|
||||||
|
|||||||
@@ -2,24 +2,22 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{
|
components::{
|
||||||
form::{attribute_input::SingleAttributeInput, field::Field, static_value::StaticValue, submit::Submit},
|
form::{field::Field, static_value::StaticValue, submit::Submit},
|
||||||
user_details::{AttributeSchema, User},
|
user_details::User,
|
||||||
}, convert_attribute_type, infra::{common_component::{CommonComponent, CommonComponentParts}, schema::AttributeType}
|
},
|
||||||
|
infra::common_component::{CommonComponent, CommonComponentParts},
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail, Error, Ok, Result};
|
use anyhow::{bail, Error, Result};
|
||||||
use gloo_console::log;
|
|
||||||
use gloo_file::{
|
use gloo_file::{
|
||||||
callbacks::{read_as_bytes, FileReader},
|
callbacks::{read_as_bytes, FileReader},
|
||||||
File,
|
File,
|
||||||
};
|
};
|
||||||
use graphql_client::GraphQLQuery;
|
use graphql_client::GraphQLQuery;
|
||||||
use validator::HasLen;
|
|
||||||
use validator_derive::Validate;
|
use validator_derive::Validate;
|
||||||
use web_sys::{FileList, FormData, HtmlFormElement, HtmlInputElement, InputEvent};
|
use web_sys::{FileList, HtmlInputElement, InputEvent, SubmitEvent};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_form_derive::Model;
|
use yew_form_derive::Model;
|
||||||
|
use gloo_console::log;
|
||||||
use super::user_details::Attribute;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct JsFile {
|
struct JsFile {
|
||||||
@@ -76,7 +74,6 @@ pub struct UserDetailsForm {
|
|||||||
/// True if we just successfully updated the user, to display a success message.
|
/// True if we just successfully updated the user, to display a success message.
|
||||||
just_updated: bool,
|
just_updated: bool,
|
||||||
user: User,
|
user: User,
|
||||||
form_ref: NodeRef,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
@@ -92,6 +89,8 @@ pub enum Msg {
|
|||||||
FileLoaded(String, Result<Vec<u8>>),
|
FileLoaded(String, Result<Vec<u8>>),
|
||||||
/// We got the response from the server about our update message.
|
/// We got the response from the server about our update message.
|
||||||
UserUpdated(Result<update_user::ResponseData>),
|
UserUpdated(Result<update_user::ResponseData>),
|
||||||
|
/// The "Submit" button was clicked.
|
||||||
|
OnSubmit(SubmitEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(yew::Properties, Clone, PartialEq, Eq)]
|
#[derive(yew::Properties, Clone, PartialEq, Eq)]
|
||||||
@@ -154,14 +153,11 @@ impl CommonComponent<UserDetailsForm> for UserDetailsForm {
|
|||||||
}
|
}
|
||||||
self.reader = None;
|
self.reader = None;
|
||||||
Ok(false)
|
Ok(false)
|
||||||
} // Msg::OnSubmit(e) => {
|
}
|
||||||
// e.prevent_default();
|
Msg::OnSubmit(e) => {
|
||||||
// let form: HtmlFormElement = e.target_unchecked_into();
|
log!(format!("{:#?}", e));
|
||||||
// let data = FormData::new_with_form(&form).unwrap();
|
Ok(true)
|
||||||
// log!(format!("form data{:#?}", data));
|
}
|
||||||
// log!(format!("form data data{:#?}", *data));
|
|
||||||
// Ok(true)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +184,6 @@ impl Component for UserDetailsForm {
|
|||||||
just_updated: false,
|
just_updated: false,
|
||||||
reader: None,
|
reader: None,
|
||||||
user: ctx.props().user.clone(),
|
user: ctx.props().user.clone(),
|
||||||
form_ref: NodeRef::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +204,7 @@ impl Component for UserDetailsForm {
|
|||||||
};
|
};
|
||||||
html! {
|
html! {
|
||||||
<div class="py-3">
|
<div class="py-3">
|
||||||
<form class="form">
|
<form class="form" onsubmit={link.callback(|e: SubmitEvent| {e.prevent_default(); Msg::OnSubmit(e)})}>
|
||||||
<StaticValue label="User ID" id="userId">
|
<StaticValue label="User ID" id="userId">
|
||||||
<i>{&self.user.id}</i>
|
<i>{&self.user.id}</i>
|
||||||
</StaticValue>
|
</StaticValue>
|
||||||
@@ -287,7 +282,6 @@ impl Component for UserDetailsForm {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{self.user.attributes.iter().map(get_custom_attribute_input).collect::<Vec<_>>()}
|
|
||||||
<Submit
|
<Submit
|
||||||
text="Save changes"
|
text="Save changes"
|
||||||
disabled={self.common.is_task_running()}
|
disabled={self.common.is_task_running()}
|
||||||
@@ -310,45 +304,6 @@ impl Component for UserDetailsForm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttributeValue = (String, Vec<String>);
|
|
||||||
|
|
||||||
fn get_values_from_form_data(
|
|
||||||
schema: Vec<AttributeSchema>,
|
|
||||||
form: &FormData,
|
|
||||||
) -> Result<Vec<AttributeValue>> {
|
|
||||||
schema
|
|
||||||
.into_iter()
|
|
||||||
.map(|attr| -> Result<AttributeValue> {
|
|
||||||
let val = form
|
|
||||||
.get_all(attr.name.as_str())
|
|
||||||
.iter()
|
|
||||||
.map(|js_val| js_val.as_string().unwrap())
|
|
||||||
.filter(|val| !val.is_empty())
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
if val.length() > 1 && !attr.is_list {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Multiple values supplied for non-list attribute {}",
|
|
||||||
attr.name
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok((attr.name.clone(), val))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_custom_attribute_input(attribute: &Attribute) -> Html {
|
|
||||||
if attribute.schema.is_list {
|
|
||||||
html!{<p>{"list attr"}</p>}
|
|
||||||
} else {
|
|
||||||
let value = if attribute.value.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(attribute.value[0].clone())
|
|
||||||
};
|
|
||||||
html!{<SingleAttributeInput name={attribute.name.clone()} attribute_type={Into::<AttributeType>::into(attribute.schema.attribute_type.clone())} value={value}/>}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserDetailsForm {
|
impl UserDetailsForm {
|
||||||
fn submit_user_update_form(&mut self, ctx: &Context<Self>) -> Result<bool> {
|
fn submit_user_update_form(&mut self, ctx: &Context<Self>) -> Result<bool> {
|
||||||
if !self.form.validate() {
|
if !self.form.validate() {
|
||||||
@@ -361,40 +316,7 @@ impl UserDetailsForm {
|
|||||||
{
|
{
|
||||||
bail!("Image file hasn't finished loading, try again");
|
bail!("Image file hasn't finished loading, try again");
|
||||||
}
|
}
|
||||||
let form = self.form_ref.cast::<HtmlFormElement>().unwrap();
|
|
||||||
let form_data = FormData::new_with_form(&form)
|
|
||||||
.map_err(|e| anyhow!("Failed to get FormData: {:#?}", e.as_string()))?;
|
|
||||||
let mut all_values = get_values_from_form_data(
|
|
||||||
self.user
|
|
||||||
.attributes
|
|
||||||
.iter()
|
|
||||||
.map(|attr| attr.schema.clone())
|
|
||||||
.filter(|attr| !attr.is_hardcoded)
|
|
||||||
.filter(|attr| attr.is_editable)
|
|
||||||
.collect(),
|
|
||||||
&form_data,
|
|
||||||
)?;
|
|
||||||
let base_user = &self.user;
|
let base_user = &self.user;
|
||||||
let base_attrs = &self.user.attributes;
|
|
||||||
all_values.retain(|(name, val)| {
|
|
||||||
let name = name.clone();
|
|
||||||
let base_val = base_attrs
|
|
||||||
.into_iter()
|
|
||||||
.find(|base_val| base_val.name == name)
|
|
||||||
.unwrap();
|
|
||||||
let new_values = val.clone();
|
|
||||||
base_val.value != new_values
|
|
||||||
});
|
|
||||||
let remove_names: Option<Vec<String>> = if all_values.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(all_values.iter().map(|(name, _)| name.clone()).collect())
|
|
||||||
};
|
|
||||||
let insert_attrs: Option<Vec<update_user::AttributeValueInput>> = if remove_names.is_none() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(all_values.into_iter().map(|(name, value)| update_user::AttributeValueInput{name, value}).collect())
|
|
||||||
};
|
|
||||||
let mut user_input = update_user::UpdateUserInput {
|
let mut user_input = update_user::UpdateUserInput {
|
||||||
id: self.user.id.clone(),
|
id: self.user.id.clone(),
|
||||||
email: None,
|
email: None,
|
||||||
@@ -402,8 +324,8 @@ impl UserDetailsForm {
|
|||||||
firstName: None,
|
firstName: None,
|
||||||
lastName: None,
|
lastName: None,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
removeAttributes: remove_names,
|
removeAttributes: None,
|
||||||
insertAttributes: insert_attrs,
|
insertAttributes: None,
|
||||||
};
|
};
|
||||||
let default_user_input = user_input.clone();
|
let default_user_input = user_input.clone();
|
||||||
let model = self.form.model();
|
let model = self.form.model();
|
||||||
|
|||||||
@@ -16,26 +16,21 @@ fn get_claims_from_jwt(jwt: &str) -> Result<JWTClaims> {
|
|||||||
Ok(token.claims().clone())
|
Ok(token.claims().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RequestType<Body: Serialize> {
|
const NO_BODY: Option<()> = None;
|
||||||
Get,
|
|
||||||
Post(Body),
|
|
||||||
}
|
|
||||||
|
|
||||||
const GET_REQUEST: RequestType<()> = RequestType::Get;
|
|
||||||
|
|
||||||
fn base_url() -> String {
|
fn base_url() -> String {
|
||||||
yew_router::utils::base_url().unwrap_or_default()
|
yew_router::utils::base_url().unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn call_server<Body: Serialize>(
|
async fn call_server(
|
||||||
url: &str,
|
url: &str,
|
||||||
body: RequestType<Body>,
|
body: Option<impl Serialize>,
|
||||||
error_message: &'static str,
|
error_message: &'static str,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let mut request = Request::new(url)
|
let mut request = Request::new(url)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.credentials(RequestCredentials::SameOrigin);
|
.credentials(RequestCredentials::SameOrigin);
|
||||||
if let RequestType::Post(b) = body {
|
if let Some(b) = body {
|
||||||
request = request
|
request = request
|
||||||
.body(serde_json::to_string(&b)?)
|
.body(serde_json::to_string(&b)?)
|
||||||
.method(Method::POST);
|
.method(Method::POST);
|
||||||
@@ -56,7 +51,7 @@ async fn call_server<Body: Serialize>(
|
|||||||
|
|
||||||
async fn call_server_json_with_error_message<CallbackResult, Body: Serialize>(
|
async fn call_server_json_with_error_message<CallbackResult, Body: Serialize>(
|
||||||
url: &str,
|
url: &str,
|
||||||
request: RequestType<Body>,
|
request: Option<Body>,
|
||||||
error_message: &'static str,
|
error_message: &'static str,
|
||||||
) -> Result<CallbackResult>
|
) -> Result<CallbackResult>
|
||||||
where
|
where
|
||||||
@@ -68,7 +63,7 @@ where
|
|||||||
|
|
||||||
async fn call_server_empty_response_with_error_message<Body: Serialize>(
|
async fn call_server_empty_response_with_error_message<Body: Serialize>(
|
||||||
url: &str,
|
url: &str,
|
||||||
request: RequestType<Body>,
|
request: Option<Body>,
|
||||||
error_message: &'static str,
|
error_message: &'static str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
call_server(url, request, error_message).await.map(|_| ())
|
call_server(url, request, error_message).await.map(|_| ())
|
||||||
@@ -107,7 +102,7 @@ impl HostService {
|
|||||||
let request_body = QueryType::build_query(variables);
|
let request_body = QueryType::build_query(variables);
|
||||||
call_server_json_with_error_message::<graphql_client::Response<_>, _>(
|
call_server_json_with_error_message::<graphql_client::Response<_>, _>(
|
||||||
&(base_url() + "/api/graphql"),
|
&(base_url() + "/api/graphql"),
|
||||||
RequestType::Post(request_body),
|
Some(request_body),
|
||||||
error_message,
|
error_message,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -119,7 +114,7 @@ impl HostService {
|
|||||||
) -> Result<Box<login::ServerLoginStartResponse>> {
|
) -> Result<Box<login::ServerLoginStartResponse>> {
|
||||||
call_server_json_with_error_message(
|
call_server_json_with_error_message(
|
||||||
&(base_url() + "/auth/opaque/login/start"),
|
&(base_url() + "/auth/opaque/login/start"),
|
||||||
RequestType::Post(request),
|
Some(request),
|
||||||
"Could not start authentication: ",
|
"Could not start authentication: ",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -128,7 +123,7 @@ impl HostService {
|
|||||||
pub async fn login_finish(request: login::ClientLoginFinishRequest) -> Result<(String, bool)> {
|
pub async fn login_finish(request: login::ClientLoginFinishRequest) -> Result<(String, bool)> {
|
||||||
call_server_json_with_error_message::<login::ServerLoginResponse, _>(
|
call_server_json_with_error_message::<login::ServerLoginResponse, _>(
|
||||||
&(base_url() + "/auth/opaque/login/finish"),
|
&(base_url() + "/auth/opaque/login/finish"),
|
||||||
RequestType::Post(request),
|
Some(request),
|
||||||
"Could not finish authentication",
|
"Could not finish authentication",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -140,7 +135,7 @@ impl HostService {
|
|||||||
) -> Result<Box<registration::ServerRegistrationStartResponse>> {
|
) -> Result<Box<registration::ServerRegistrationStartResponse>> {
|
||||||
call_server_json_with_error_message(
|
call_server_json_with_error_message(
|
||||||
&(base_url() + "/auth/opaque/register/start"),
|
&(base_url() + "/auth/opaque/register/start"),
|
||||||
RequestType::Post(request),
|
Some(request),
|
||||||
"Could not start registration: ",
|
"Could not start registration: ",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -151,7 +146,7 @@ impl HostService {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
call_server_empty_response_with_error_message(
|
call_server_empty_response_with_error_message(
|
||||||
&(base_url() + "/auth/opaque/register/finish"),
|
&(base_url() + "/auth/opaque/register/finish"),
|
||||||
RequestType::Post(request),
|
Some(request),
|
||||||
"Could not finish registration",
|
"Could not finish registration",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -160,7 +155,7 @@ impl HostService {
|
|||||||
pub async fn refresh() -> Result<(String, bool)> {
|
pub async fn refresh() -> Result<(String, bool)> {
|
||||||
call_server_json_with_error_message::<login::ServerLoginResponse, _>(
|
call_server_json_with_error_message::<login::ServerLoginResponse, _>(
|
||||||
&(base_url() + "/auth/refresh"),
|
&(base_url() + "/auth/refresh"),
|
||||||
GET_REQUEST,
|
NO_BODY,
|
||||||
"Could not start authentication: ",
|
"Could not start authentication: ",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -171,7 +166,7 @@ impl HostService {
|
|||||||
pub async fn logout() -> Result<()> {
|
pub async fn logout() -> Result<()> {
|
||||||
call_server_empty_response_with_error_message(
|
call_server_empty_response_with_error_message(
|
||||||
&(base_url() + "/auth/logout"),
|
&(base_url() + "/auth/logout"),
|
||||||
GET_REQUEST,
|
NO_BODY,
|
||||||
"Could not logout",
|
"Could not logout",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -184,7 +179,7 @@ impl HostService {
|
|||||||
base_url(),
|
base_url(),
|
||||||
url_escape::encode_query(&username)
|
url_escape::encode_query(&username)
|
||||||
),
|
),
|
||||||
RequestType::Post(""),
|
NO_BODY,
|
||||||
"Could not initiate password reset",
|
"Could not initiate password reset",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -195,7 +190,7 @@ impl HostService {
|
|||||||
) -> Result<lldap_auth::password_reset::ServerPasswordResetResponse> {
|
) -> Result<lldap_auth::password_reset::ServerPasswordResetResponse> {
|
||||||
call_server_json_with_error_message(
|
call_server_json_with_error_message(
|
||||||
&format!("{}/auth/reset/step2/{}", base_url(), token),
|
&format!("{}/auth/reset/step2/{}", base_url(), token),
|
||||||
GET_REQUEST,
|
NO_BODY,
|
||||||
"Could not validate token",
|
"Could not validate token",
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::infra::api::HostService;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use graphql_client::GraphQLQuery;
|
use graphql_client::GraphQLQuery;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
use yew::{use_effect, use_state_eq, UseStateHandle};
|
use yew::{use_effect, use_state, UseStateHandle};
|
||||||
|
|
||||||
// Enum to represent a result that is fetched asynchronously.
|
// Enum to represent a result that is fetched asynchronously.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -13,28 +13,14 @@ pub enum LoadableResult<T> {
|
|||||||
Loaded(Result<T>),
|
Loaded(Result<T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PartialEq> PartialEq for LoadableResult<T> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(LoadableResult::Loading, LoadableResult::Loading) => true,
|
|
||||||
(LoadableResult::Loaded(Ok(d1)), LoadableResult::Loaded(Ok(d2))) => d1.eq(d2),
|
|
||||||
(LoadableResult::Loaded(Err(e1)), LoadableResult::Loaded(Err(e2))) => {
|
|
||||||
e1.to_string().eq(&e2.to_string())
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn use_graphql_call<QueryType>(
|
pub fn use_graphql_call<QueryType>(
|
||||||
variables: QueryType::Variables,
|
variables: QueryType::Variables,
|
||||||
) -> UseStateHandle<LoadableResult<QueryType::ResponseData>>
|
) -> UseStateHandle<LoadableResult<QueryType::ResponseData>>
|
||||||
where
|
where
|
||||||
QueryType: GraphQLQuery + 'static,
|
QueryType: GraphQLQuery + 'static,
|
||||||
<QueryType as graphql_client::GraphQLQuery>::ResponseData: std::cmp::PartialEq,
|
|
||||||
{
|
{
|
||||||
let loadable_result: UseStateHandle<LoadableResult<QueryType::ResponseData>> =
|
let loadable_result: UseStateHandle<LoadableResult<QueryType::ResponseData>> =
|
||||||
use_state_eq(|| LoadableResult::Loading);
|
use_state(|| LoadableResult::Loading);
|
||||||
{
|
{
|
||||||
let loadable_result = loadable_result.clone();
|
let loadable_result = loadable_result.clone();
|
||||||
use_effect(move || {
|
use_effect(move || {
|
||||||
|
|||||||
@@ -59,8 +59,38 @@ macro_rules! convert_attribute_type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct Attribute {
|
||||||
|
pub name: String,
|
||||||
|
pub value: Vec<String>,
|
||||||
|
pub attribute_type: AttributeType,
|
||||||
|
pub is_list: bool,
|
||||||
|
pub is_editable: bool,
|
||||||
|
pub is_hardcoded: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro to generate traits for converting between AttributeType and the
|
||||||
|
// graphql generated equivalents.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! combine_schema_and_values {
|
||||||
|
($schema_list:ident, $value_list:ident, $output_list:ident) => {
|
||||||
|
let set_attributes = value_list.clone();
|
||||||
|
let mut attribute_schema = schema_list.clone();
|
||||||
|
attribute_schema.retain(|schema| !schema.is_hardcoded);
|
||||||
|
let $output_list = attribute_schema.into_iter().map(|schema| {
|
||||||
|
Attribute {
|
||||||
|
name: schema.name.clone(),
|
||||||
|
value: set_attributes.iter().find(|attribute_value| attribute_value.name == schema.name).unwrap().value.clone(),
|
||||||
|
attribute_type: AttributeType::from(schema.attribute_type),
|
||||||
|
is_list: schema.is_list,
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
};
|
||||||
|
=======
|
||||||
pub fn validate_attribute_type(attribute_type: &str) -> Result<(), ValidationError> {
|
pub fn validate_attribute_type(attribute_type: &str) -> Result<(), ValidationError> {
|
||||||
AttributeType::from_str(attribute_type)
|
AttributeType::from_str(attribute_type)
|
||||||
.map_err(|_| ValidationError::new("Invalid attribute type"))?;
|
.map_err(|_| ValidationError::new("Invalid attribute type"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
>>>>>>> 8f2391a (app: create group attribute schema page (#825))
|
||||||
}
|
}
|
||||||
|
|||||||
5
schema.graphql
generated
5
schema.graphql
generated
@@ -18,10 +18,6 @@ type Mutation {
|
|||||||
addGroupAttribute(name: String!, attributeType: AttributeType!, isList: Boolean!, isVisible: Boolean!, isEditable: Boolean!): Success!
|
addGroupAttribute(name: String!, attributeType: AttributeType!, isList: Boolean!, isVisible: Boolean!, isEditable: Boolean!): Success!
|
||||||
deleteUserAttribute(name: String!): Success!
|
deleteUserAttribute(name: String!): Success!
|
||||||
deleteGroupAttribute(name: String!): Success!
|
deleteGroupAttribute(name: String!): Success!
|
||||||
addUserObjectClass(name: String!): Success!
|
|
||||||
addGroupObjectClass(name: String!): Success!
|
|
||||||
deleteUserObjectClass(name: String!): Success!
|
|
||||||
deleteGroupObjectClass(name: String!): Success!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Group {
|
type Group {
|
||||||
@@ -166,7 +162,6 @@ enum AttributeType {
|
|||||||
|
|
||||||
type AttributeList {
|
type AttributeList {
|
||||||
attributes: [AttributeSchema!]!
|
attributes: [AttributeSchema!]!
|
||||||
extraLdapObjectClasses: [String!]!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Success {
|
type Success {
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ use crate::domain::{
|
|||||||
error::Result,
|
error::Result,
|
||||||
types::{
|
types::{
|
||||||
AttributeName, AttributeType, AttributeValue, Email, Group, GroupDetails, GroupId,
|
AttributeName, AttributeType, AttributeValue, Email, Group, GroupDetails, GroupId,
|
||||||
GroupName, JpegPhoto, LdapObjectClass, Serialized, User, UserAndGroups, UserColumn, UserId,
|
GroupName, JpegPhoto, Serialized, User, UserAndGroups, UserColumn, UserId, Uuid,
|
||||||
Uuid,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@@ -176,8 +175,6 @@ impl AttributeList {
|
|||||||
pub struct Schema {
|
pub struct Schema {
|
||||||
pub user_attributes: AttributeList,
|
pub user_attributes: AttributeList,
|
||||||
pub group_attributes: AttributeList,
|
pub group_attributes: AttributeList,
|
||||||
pub extra_user_object_classes: Vec<LdapObjectClass>,
|
|
||||||
pub extra_group_object_classes: Vec<LdapObjectClass>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -230,11 +227,6 @@ pub trait SchemaBackendHandler: ReadSchemaBackendHandler {
|
|||||||
// Note: It's up to the caller to make sure that the attribute is not hardcoded.
|
// Note: It's up to the caller to make sure that the attribute is not hardcoded.
|
||||||
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
|
|
||||||
async fn add_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn add_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::domain::{
|
|||||||
handler::{GroupListerBackendHandler, GroupRequestFilter},
|
handler::{GroupListerBackendHandler, GroupRequestFilter},
|
||||||
ldap::error::LdapError,
|
ldap::error::LdapError,
|
||||||
schema::{PublicSchema, SchemaGroupAttributeExtractor},
|
schema::{PublicSchema, SchemaGroupAttributeExtractor},
|
||||||
types::{AttributeName, AttributeType, Group, LdapObjectClass, UserId, Uuid},
|
types::{AttributeName, AttributeType, Group, UserId, Uuid},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -30,17 +30,7 @@ pub fn get_group_attribute(
|
|||||||
) -> Option<Vec<Vec<u8>>> {
|
) -> Option<Vec<Vec<u8>>> {
|
||||||
let attribute = AttributeName::from(attribute);
|
let attribute = AttributeName::from(attribute);
|
||||||
let attribute_values = match map_group_field(&attribute, schema) {
|
let attribute_values = match map_group_field(&attribute, schema) {
|
||||||
GroupFieldType::ObjectClass => {
|
GroupFieldType::ObjectClass => vec![b"groupOfUniqueNames".to_vec()],
|
||||||
let mut classes = vec![b"groupOfUniqueNames".to_vec()];
|
|
||||||
classes.extend(
|
|
||||||
schema
|
|
||||||
.get_schema()
|
|
||||||
.extra_group_object_classes
|
|
||||||
.iter()
|
|
||||||
.map(|c| c.as_str().as_bytes().to_vec()),
|
|
||||||
);
|
|
||||||
classes
|
|
||||||
}
|
|
||||||
// Always returned as part of the base response.
|
// Always returned as part of the base response.
|
||||||
GroupFieldType::Dn => return None,
|
GroupFieldType::Dn => return None,
|
||||||
GroupFieldType::EntryDn => {
|
GroupFieldType::EntryDn => {
|
||||||
@@ -177,13 +167,10 @@ fn convert_group_filter(
|
|||||||
)?;
|
)?;
|
||||||
Ok(GroupRequestFilter::Member(user_name))
|
Ok(GroupRequestFilter::Member(user_name))
|
||||||
}
|
}
|
||||||
GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from(
|
GroupFieldType::ObjectClass => Ok(GroupRequestFilter::from(matches!(
|
||||||
matches!(value.as_str(), "groupofuniquenames" | "groupofnames")
|
value.as_str(),
|
||||||
|| schema
|
"groupofuniquenames" | "groupofnames"
|
||||||
.get_schema()
|
))),
|
||||||
.extra_group_object_classes
|
|
||||||
.contains(&LdapObjectClass::from(value)),
|
|
||||||
)),
|
|
||||||
GroupFieldType::Dn | GroupFieldType::EntryDn => {
|
GroupFieldType::Dn | GroupFieldType::EntryDn => {
|
||||||
Ok(get_group_id_from_distinguished_name(
|
Ok(get_group_id_from_distinguished_name(
|
||||||
value.as_str(),
|
value.as_str(),
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ use crate::domain::{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
schema::{PublicSchema, SchemaUserAttributeExtractor},
|
schema::{PublicSchema, SchemaUserAttributeExtractor},
|
||||||
types::{
|
types::{AttributeName, AttributeType, GroupDetails, User, UserAndGroups, UserColumn, UserId},
|
||||||
AttributeName, AttributeType, GroupDetails, LdapObjectClass, User, UserAndGroups,
|
|
||||||
UserColumn, UserId,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get_user_attribute(
|
pub fn get_user_attribute(
|
||||||
@@ -31,22 +28,12 @@ pub fn get_user_attribute(
|
|||||||
) -> Option<Vec<Vec<u8>>> {
|
) -> Option<Vec<Vec<u8>>> {
|
||||||
let attribute = AttributeName::from(attribute);
|
let attribute = AttributeName::from(attribute);
|
||||||
let attribute_values = match map_user_field(&attribute, schema) {
|
let attribute_values = match map_user_field(&attribute, schema) {
|
||||||
UserFieldType::ObjectClass => {
|
UserFieldType::ObjectClass => vec![
|
||||||
let mut classes = vec![
|
b"inetOrgPerson".to_vec(),
|
||||||
b"inetOrgPerson".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"person".to_vec(),
|
||||||
b"person".to_vec(),
|
],
|
||||||
];
|
|
||||||
classes.extend(
|
|
||||||
schema
|
|
||||||
.get_schema()
|
|
||||||
.extra_user_object_classes
|
|
||||||
.iter()
|
|
||||||
.map(|c| c.as_str().as_bytes().to_vec()),
|
|
||||||
);
|
|
||||||
classes
|
|
||||||
}
|
|
||||||
// dn is always returned as part of the base response.
|
// dn is always returned as part of the base response.
|
||||||
UserFieldType::Dn => return None,
|
UserFieldType::Dn => return None,
|
||||||
UserFieldType::EntryDn => {
|
UserFieldType::EntryDn => {
|
||||||
@@ -209,15 +196,10 @@ fn convert_user_filter(
|
|||||||
}
|
}
|
||||||
Ok(UserRequestFilter::from(false))
|
Ok(UserRequestFilter::from(false))
|
||||||
}
|
}
|
||||||
UserFieldType::ObjectClass => Ok(UserRequestFilter::from(
|
UserFieldType::ObjectClass => Ok(UserRequestFilter::from(matches!(
|
||||||
matches!(
|
value.as_str(),
|
||||||
value.as_str(),
|
"person" | "inetorgperson" | "posixaccount" | "mailaccount"
|
||||||
"person" | "inetorgperson" | "posixaccount" | "mailaccount"
|
))),
|
||||||
) || schema
|
|
||||||
.get_schema()
|
|
||||||
.extra_user_object_classes
|
|
||||||
.contains(&LdapObjectClass::from(value)),
|
|
||||||
)),
|
|
||||||
UserFieldType::MemberOf => Ok(UserRequestFilter::MemberOf(
|
UserFieldType::MemberOf => Ok(UserRequestFilter::MemberOf(
|
||||||
get_group_id_from_distinguished_name(
|
get_group_id_from_distinguished_name(
|
||||||
&value,
|
&value,
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
use sea_orm::entity::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::domain::types::LdapObjectClass;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
|
||||||
#[sea_orm(table_name = "group_object_classes")]
|
|
||||||
pub struct Model {
|
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
|
||||||
pub lower_object_class: String,
|
|
||||||
pub object_class: LdapObjectClass,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
|
||||||
pub enum Relation {}
|
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
|
||||||
|
|
||||||
impl From<Model> for LdapObjectClass {
|
|
||||||
fn from(value: Model) -> Self {
|
|
||||||
value.object_class
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.3
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
pub mod groups;
|
pub mod groups;
|
||||||
@@ -9,10 +11,8 @@ pub mod users;
|
|||||||
|
|
||||||
pub mod user_attribute_schema;
|
pub mod user_attribute_schema;
|
||||||
pub mod user_attributes;
|
pub mod user_attributes;
|
||||||
pub mod user_object_classes;
|
|
||||||
|
|
||||||
pub mod group_attribute_schema;
|
pub mod group_attribute_schema;
|
||||||
pub mod group_attributes;
|
pub mod group_attributes;
|
||||||
pub mod group_object_classes;
|
|
||||||
|
|
||||||
pub use prelude::*;
|
pub use prelude::*;
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ pub use super::group_attribute_schema::Column as GroupAttributeSchemaColumn;
|
|||||||
pub use super::group_attribute_schema::Entity as GroupAttributeSchema;
|
pub use super::group_attribute_schema::Entity as GroupAttributeSchema;
|
||||||
pub use super::group_attributes::Column as GroupAttributesColumn;
|
pub use super::group_attributes::Column as GroupAttributesColumn;
|
||||||
pub use super::group_attributes::Entity as GroupAttributes;
|
pub use super::group_attributes::Entity as GroupAttributes;
|
||||||
pub use super::group_object_classes::Column as GroupObjectClassesColumn;
|
|
||||||
pub use super::group_object_classes::Entity as GroupObjectClasses;
|
|
||||||
pub use super::groups::Column as GroupColumn;
|
pub use super::groups::Column as GroupColumn;
|
||||||
pub use super::groups::Entity as Group;
|
pub use super::groups::Entity as Group;
|
||||||
pub use super::jwt_refresh_storage::Column as JwtRefreshStorageColumn;
|
pub use super::jwt_refresh_storage::Column as JwtRefreshStorageColumn;
|
||||||
@@ -20,7 +18,5 @@ pub use super::user_attribute_schema::Column as UserAttributeSchemaColumn;
|
|||||||
pub use super::user_attribute_schema::Entity as UserAttributeSchema;
|
pub use super::user_attribute_schema::Entity as UserAttributeSchema;
|
||||||
pub use super::user_attributes::Column as UserAttributesColumn;
|
pub use super::user_attributes::Column as UserAttributesColumn;
|
||||||
pub use super::user_attributes::Entity as UserAttributes;
|
pub use super::user_attributes::Entity as UserAttributes;
|
||||||
pub use super::user_object_classes::Column as UserObjectClassesColumn;
|
|
||||||
pub use super::user_object_classes::Entity as UserObjectClasses;
|
|
||||||
pub use super::users::Column as UserColumn;
|
pub use super::users::Column as UserColumn;
|
||||||
pub use super::users::Entity as User;
|
pub use super::users::Entity as User;
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
use sea_orm::entity::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::domain::types::LdapObjectClass;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
|
||||||
#[sea_orm(table_name = "user_object_classes")]
|
|
||||||
pub struct Model {
|
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
|
||||||
pub lower_object_class: String,
|
|
||||||
pub object_class: LdapObjectClass,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
|
||||||
pub enum Relation {}
|
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
|
||||||
|
|
||||||
impl From<Model> for LdapObjectClass {
|
|
||||||
fn from(value: Model) -> Self {
|
|
||||||
value.object_class
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -88,20 +88,6 @@ pub enum GroupAttributes {
|
|||||||
GroupAttributeValue,
|
GroupAttributeValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
|
||||||
pub enum UserObjectClasses {
|
|
||||||
Table,
|
|
||||||
LowerObjectClass,
|
|
||||||
ObjectClass,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
|
||||||
pub enum GroupObjectClasses {
|
|
||||||
Table,
|
|
||||||
LowerObjectClass,
|
|
||||||
ObjectClass,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata about the SQL DB.
|
// Metadata about the SQL DB.
|
||||||
#[derive(DeriveIden)]
|
#[derive(DeriveIden)]
|
||||||
pub enum Metadata {
|
pub enum Metadata {
|
||||||
@@ -1045,51 +1031,6 @@ async fn migrate_to_v8(transaction: DatabaseTransaction) -> Result<DatabaseTrans
|
|||||||
Ok(transaction)
|
Ok(transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn migrate_to_v9(transaction: DatabaseTransaction) -> Result<DatabaseTransaction, DbErr> {
|
|
||||||
let builder = transaction.get_database_backend();
|
|
||||||
transaction
|
|
||||||
.execute(
|
|
||||||
builder.build(
|
|
||||||
Table::create()
|
|
||||||
.table(UserObjectClasses::Table)
|
|
||||||
.if_not_exists()
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(UserObjectClasses::LowerObjectClass)
|
|
||||||
.string_len(255)
|
|
||||||
.not_null()
|
|
||||||
.primary_key(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(UserObjectClasses::ObjectClass)
|
|
||||||
.string_len(255)
|
|
||||||
.not_null(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
transaction
|
|
||||||
.execute(
|
|
||||||
builder.build(
|
|
||||||
Table::create()
|
|
||||||
.table(GroupObjectClasses::Table)
|
|
||||||
.if_not_exists()
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(GroupObjectClasses::LowerObjectClass)
|
|
||||||
.string_len(255)
|
|
||||||
.not_null()
|
|
||||||
.primary_key(),
|
|
||||||
)
|
|
||||||
.col(
|
|
||||||
ColumnDef::new(GroupObjectClasses::ObjectClass)
|
|
||||||
.string_len(255)
|
|
||||||
.not_null(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(transaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is needed to make an array of async functions.
|
// This is needed to make an array of async functions.
|
||||||
macro_rules! to_sync {
|
macro_rules! to_sync {
|
||||||
($l:ident) => {
|
($l:ident) => {
|
||||||
@@ -1118,7 +1059,6 @@ pub async fn migrate_from_version(
|
|||||||
to_sync!(migrate_to_v6),
|
to_sync!(migrate_to_v6),
|
||||||
to_sync!(migrate_to_v7),
|
to_sync!(migrate_to_v7),
|
||||||
to_sync!(migrate_to_v8),
|
to_sync!(migrate_to_v8),
|
||||||
to_sync!(migrate_to_v9),
|
|
||||||
];
|
];
|
||||||
assert_eq!(migrations.len(), (LAST_SCHEMA_VERSION.0 - 1) as usize);
|
assert_eq!(migrations.len(), (LAST_SCHEMA_VERSION.0 - 1) as usize);
|
||||||
for migration in 2..=last_version.0 {
|
for migration in 2..=last_version.0 {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::domain::{
|
|||||||
},
|
},
|
||||||
model,
|
model,
|
||||||
sql_backend_handler::SqlBackendHandler,
|
sql_backend_handler::SqlBackendHandler,
|
||||||
types::{AttributeName, LdapObjectClass},
|
types::AttributeName,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
@@ -66,44 +66,6 @@ impl SchemaBackendHandler for SqlBackendHandler {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_user_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
let mut name_key = name.to_string();
|
|
||||||
name_key.make_ascii_lowercase();
|
|
||||||
model::user_object_classes::ActiveModel {
|
|
||||||
lower_object_class: Set(name_key),
|
|
||||||
object_class: Set(name.clone()),
|
|
||||||
}
|
|
||||||
.insert(&self.sql_pool)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_group_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
let mut name_key = name.to_string();
|
|
||||||
name_key.make_ascii_lowercase();
|
|
||||||
model::group_object_classes::ActiveModel {
|
|
||||||
lower_object_class: Set(name_key),
|
|
||||||
object_class: Set(name.clone()),
|
|
||||||
}
|
|
||||||
.insert(&self.sql_pool)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
model::UserObjectClasses::delete_by_id(name.as_str().to_ascii_lowercase())
|
|
||||||
.exec(&self.sql_pool)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
model::GroupObjectClasses::delete_by_id(name.as_str().to_ascii_lowercase())
|
|
||||||
.exec(&self.sql_pool)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SqlBackendHandler {
|
impl SqlBackendHandler {
|
||||||
@@ -117,8 +79,6 @@ impl SqlBackendHandler {
|
|||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Self::get_group_attributes(transaction).await?,
|
attributes: Self::get_group_attributes(transaction).await?,
|
||||||
},
|
},
|
||||||
extra_user_object_classes: Self::get_user_object_classes(transaction).await?,
|
|
||||||
extra_group_object_classes: Self::get_group_object_classes(transaction).await?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,30 +105,6 @@ impl SqlBackendHandler {
|
|||||||
.map(|m| m.into())
|
.map(|m| m.into())
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_object_classes(
|
|
||||||
transaction: &DatabaseTransaction,
|
|
||||||
) -> Result<Vec<LdapObjectClass>> {
|
|
||||||
Ok(model::UserObjectClasses::find()
|
|
||||||
.order_by_asc(model::UserObjectClassesColumn::ObjectClass)
|
|
||||||
.all(transaction)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.map(Into::into)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_group_object_classes(
|
|
||||||
transaction: &DatabaseTransaction,
|
|
||||||
) -> Result<Vec<LdapObjectClass>> {
|
|
||||||
Ok(model::GroupObjectClasses::find()
|
|
||||||
.order_by_asc(model::GroupObjectClassesColumn::ObjectClass)
|
|
||||||
.all(transaction)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.map(Into::into)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -215,9 +151,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Vec::new()
|
attributes: Vec::new()
|
||||||
},
|
}
|
||||||
extra_user_object_classes: Vec::new(),
|
|
||||||
extra_group_object_classes: Vec::new(),
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -313,50 +247,4 @@ mod tests {
|
|||||||
.attributes
|
.attributes
|
||||||
.contains(&expected_value));
|
.contains(&expected_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_user_object_class_add_and_delete() {
|
|
||||||
let fixture = TestFixture::new().await;
|
|
||||||
let new_object_class = LdapObjectClass::new("newObjectClass");
|
|
||||||
fixture
|
|
||||||
.handler
|
|
||||||
.add_user_object_class(&new_object_class)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
fixture
|
|
||||||
.handler
|
|
||||||
.get_schema()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.extra_user_object_classes,
|
|
||||||
vec![new_object_class.clone()]
|
|
||||||
);
|
|
||||||
fixture
|
|
||||||
.handler
|
|
||||||
.add_user_object_class(&LdapObjectClass::new("newobjEctclass"))
|
|
||||||
.await
|
|
||||||
.expect_err("Should not be able to add the same object class twice");
|
|
||||||
assert_eq!(
|
|
||||||
fixture
|
|
||||||
.handler
|
|
||||||
.get_schema()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.extra_user_object_classes,
|
|
||||||
vec![new_object_class.clone()]
|
|
||||||
);
|
|
||||||
fixture
|
|
||||||
.handler
|
|
||||||
.delete_user_object_class(&new_object_class)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert!(fixture
|
|
||||||
.handler
|
|
||||||
.get_schema()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.extra_user_object_classes
|
|
||||||
.is_empty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub type DbConnection = sea_orm::DatabaseConnection;
|
|||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord, DeriveValueType)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord, DeriveValueType)]
|
||||||
pub struct SchemaVersion(pub i16);
|
pub struct SchemaVersion(pub i16);
|
||||||
|
|
||||||
pub const LAST_SCHEMA_VERSION: SchemaVersion = SchemaVersion(9);
|
pub const LAST_SCHEMA_VERSION: SchemaVersion = SchemaVersion(8);
|
||||||
|
|
||||||
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord)]
|
#[derive(Copy, PartialEq, Eq, Debug, Clone, PartialOrd, Ord)]
|
||||||
pub struct PrivateKeyHash(pub [u8; 32]);
|
pub struct PrivateKeyHash(pub [u8; 32]);
|
||||||
|
|||||||
@@ -271,8 +271,6 @@ impl TryFromU64 for AttributeName {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
make_case_insensitive_comparable_string!(LdapObjectClass);
|
|
||||||
make_case_insensitive_comparable_string!(Email);
|
make_case_insensitive_comparable_string!(Email);
|
||||||
make_case_insensitive_comparable_string!(GroupName);
|
make_case_insensitive_comparable_string!(GroupName);
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,7 @@ use crate::domain::{
|
|||||||
UpdateUserRequest, UserBackendHandler, UserListerBackendHandler, UserRequestFilter,
|
UpdateUserRequest, UserBackendHandler, UserListerBackendHandler, UserRequestFilter,
|
||||||
},
|
},
|
||||||
schema::PublicSchema,
|
schema::PublicSchema,
|
||||||
types::{
|
types::{AttributeName, Group, GroupDetails, GroupId, GroupName, User, UserAndGroups, UserId},
|
||||||
AttributeName, Group, GroupDetails, GroupId, GroupName, LdapObjectClass, User,
|
|
||||||
UserAndGroups, UserId,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
@@ -115,10 +112,6 @@ pub trait AdminBackendHandler:
|
|||||||
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
||||||
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
async fn add_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn add_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -194,18 +187,6 @@ impl<Handler: BackendHandler> AdminBackendHandler for Handler {
|
|||||||
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()> {
|
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()> {
|
||||||
<Handler as SchemaBackendHandler>::delete_group_attribute(self, name).await
|
<Handler as SchemaBackendHandler>::delete_group_attribute(self, name).await
|
||||||
}
|
}
|
||||||
async fn add_user_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
<Handler as SchemaBackendHandler>::add_user_object_class(self, name).await
|
|
||||||
}
|
|
||||||
async fn add_group_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
<Handler as SchemaBackendHandler>::add_group_object_class(self, name).await
|
|
||||||
}
|
|
||||||
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
<Handler as SchemaBackendHandler>::delete_user_object_class(self, name).await
|
|
||||||
}
|
|
||||||
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()> {
|
|
||||||
<Handler as SchemaBackendHandler>::delete_group_object_class(self, name).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AccessControlledBackendHandler<Handler> {
|
pub struct AccessControlledBackendHandler<Handler> {
|
||||||
|
|||||||
@@ -677,7 +677,7 @@ where
|
|||||||
if enable_password_reset {
|
if enable_password_reset {
|
||||||
cfg.service(
|
cfg.service(
|
||||||
web::resource("/reset/step1/{user_id}")
|
web::resource("/reset/step1/{user_id}")
|
||||||
.route(web::post().to(get_password_reset_step1_handler::<Backend>)),
|
.route(web::get().to(get_password_reset_step1_handler::<Backend>)),
|
||||||
)
|
)
|
||||||
.service(
|
.service(
|
||||||
web::resource("/reset/step2/{token}")
|
web::resource("/reset/step2/{token}")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
types::{
|
types::{
|
||||||
AttributeName, AttributeType, AttributeValue as DomainAttributeValue, GroupId,
|
AttributeName, AttributeType, AttributeValue as DomainAttributeValue, GroupId,
|
||||||
JpegPhoto, LdapObjectClass, UserId,
|
JpegPhoto, UserId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
@@ -490,90 +490,6 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(Success::new())
|
Ok(Success::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_user_object_class(
|
|
||||||
context: &Context<Handler>,
|
|
||||||
name: String,
|
|
||||||
) -> FieldResult<Success> {
|
|
||||||
let span = debug_span!("[GraphQL mutation] add_user_object_class");
|
|
||||||
span.in_scope(|| {
|
|
||||||
debug!(?name);
|
|
||||||
});
|
|
||||||
let handler = context
|
|
||||||
.get_admin_handler()
|
|
||||||
.ok_or_else(field_error_callback(
|
|
||||||
&span,
|
|
||||||
"Unauthorized object class addition",
|
|
||||||
))?;
|
|
||||||
handler
|
|
||||||
.add_user_object_class(&LdapObjectClass::from(name))
|
|
||||||
.instrument(span)
|
|
||||||
.await?;
|
|
||||||
Ok(Success::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_group_object_class(
|
|
||||||
context: &Context<Handler>,
|
|
||||||
name: String,
|
|
||||||
) -> FieldResult<Success> {
|
|
||||||
let span = debug_span!("[GraphQL mutation] add_group_object_class");
|
|
||||||
span.in_scope(|| {
|
|
||||||
debug!(?name);
|
|
||||||
});
|
|
||||||
let handler = context
|
|
||||||
.get_admin_handler()
|
|
||||||
.ok_or_else(field_error_callback(
|
|
||||||
&span,
|
|
||||||
"Unauthorized object class addition",
|
|
||||||
))?;
|
|
||||||
handler
|
|
||||||
.add_group_object_class(&LdapObjectClass::from(name))
|
|
||||||
.instrument(span)
|
|
||||||
.await?;
|
|
||||||
Ok(Success::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_user_object_class(
|
|
||||||
context: &Context<Handler>,
|
|
||||||
name: String,
|
|
||||||
) -> FieldResult<Success> {
|
|
||||||
let span = debug_span!("[GraphQL mutation] delete_user_object_class");
|
|
||||||
span.in_scope(|| {
|
|
||||||
debug!(?name);
|
|
||||||
});
|
|
||||||
let handler = context
|
|
||||||
.get_admin_handler()
|
|
||||||
.ok_or_else(field_error_callback(
|
|
||||||
&span,
|
|
||||||
"Unauthorized object class deletion",
|
|
||||||
))?;
|
|
||||||
handler
|
|
||||||
.delete_user_object_class(&LdapObjectClass::from(name))
|
|
||||||
.instrument(span)
|
|
||||||
.await?;
|
|
||||||
Ok(Success::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_group_object_class(
|
|
||||||
context: &Context<Handler>,
|
|
||||||
name: String,
|
|
||||||
) -> FieldResult<Success> {
|
|
||||||
let span = debug_span!("[GraphQL mutation] delete_group_object_class");
|
|
||||||
span.in_scope(|| {
|
|
||||||
debug!(?name);
|
|
||||||
});
|
|
||||||
let handler = context
|
|
||||||
.get_admin_handler()
|
|
||||||
.ok_or_else(field_error_callback(
|
|
||||||
&span,
|
|
||||||
"Unauthorized object class deletion",
|
|
||||||
))?;
|
|
||||||
handler
|
|
||||||
.delete_group_object_class(&LdapObjectClass::from(name))
|
|
||||||
.instrument(span)
|
|
||||||
.await?;
|
|
||||||
Ok(Success::new())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_group_with_details<Handler: BackendHandler>(
|
async fn create_group_with_details<Handler: BackendHandler>(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::{
|
|||||||
ldap::utils::{map_user_field, UserFieldType},
|
ldap::utils::{map_user_field, UserFieldType},
|
||||||
model::UserColumn,
|
model::UserColumn,
|
||||||
schema::PublicSchema,
|
schema::PublicSchema,
|
||||||
types::{AttributeType, GroupDetails, GroupId, JpegPhoto, LdapObjectClass, UserId},
|
types::{AttributeType, GroupDetails, GroupId, JpegPhoto, UserId},
|
||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
access_control::{ReadonlyBackendHandler, UserReadableBackendHandler},
|
access_control::{ReadonlyBackendHandler, UserReadableBackendHandler},
|
||||||
@@ -523,32 +523,26 @@ impl<Handler: BackendHandler> From<DomainAttributeSchema> for AttributeSchema<Ha
|
|||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
pub struct AttributeList<Handler: BackendHandler> {
|
pub struct AttributeList<Handler: BackendHandler> {
|
||||||
attributes: DomainAttributeList,
|
schema: DomainAttributeList,
|
||||||
extra_classes: Vec<LdapObjectClass>,
|
|
||||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql_object(context = Context<Handler>)]
|
#[graphql_object(context = Context<Handler>)]
|
||||||
impl<Handler: BackendHandler> AttributeList<Handler> {
|
impl<Handler: BackendHandler> AttributeList<Handler> {
|
||||||
fn attributes(&self) -> Vec<AttributeSchema<Handler>> {
|
fn attributes(&self) -> Vec<AttributeSchema<Handler>> {
|
||||||
self.attributes
|
self.schema
|
||||||
.attributes
|
.attributes
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_ldap_object_classes(&self) -> Vec<String> {
|
|
||||||
self.extra_classes.iter().map(|c| c.to_string()).collect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: BackendHandler> AttributeList<Handler> {
|
impl<Handler: BackendHandler> From<DomainAttributeList> for AttributeList<Handler> {
|
||||||
fn new(attributes: DomainAttributeList, extra_classes: Vec<LdapObjectClass>) -> Self {
|
fn from(value: DomainAttributeList) -> Self {
|
||||||
Self {
|
Self {
|
||||||
attributes,
|
schema: value,
|
||||||
extra_classes,
|
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -563,16 +557,10 @@ pub struct Schema<Handler: BackendHandler> {
|
|||||||
#[graphql_object(context = Context<Handler>)]
|
#[graphql_object(context = Context<Handler>)]
|
||||||
impl<Handler: BackendHandler> Schema<Handler> {
|
impl<Handler: BackendHandler> Schema<Handler> {
|
||||||
fn user_schema(&self) -> AttributeList<Handler> {
|
fn user_schema(&self) -> AttributeList<Handler> {
|
||||||
AttributeList::<Handler>::new(
|
self.schema.get_schema().user_attributes.clone().into()
|
||||||
self.schema.get_schema().user_attributes.clone(),
|
|
||||||
self.schema.get_schema().extra_user_object_classes.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
fn group_schema(&self) -> AttributeList<Handler> {
|
fn group_schema(&self) -> AttributeList<Handler> {
|
||||||
AttributeList::<Handler>::new(
|
self.schema.get_schema().group_attributes.clone().into()
|
||||||
self.schema.get_schema().group_attributes.clone(),
|
|
||||||
self.schema.get_schema().extra_group_object_classes.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,7 +670,7 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
domain::{
|
domain::{
|
||||||
handler::AttributeList,
|
handler::AttributeList,
|
||||||
types::{AttributeName, AttributeType, LdapObjectClass, Serialized},
|
types::{AttributeName, AttributeType, Serialized},
|
||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
access_control::{Permission, ValidationResults},
|
access_control::{Permission, ValidationResults},
|
||||||
@@ -767,11 +755,6 @@ mod tests {
|
|||||||
is_hardcoded: false,
|
is_hardcoded: false,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
extra_user_object_classes: vec![
|
|
||||||
LdapObjectClass::from("customUserClass"),
|
|
||||||
LdapObjectClass::from("myUserClass"),
|
|
||||||
],
|
|
||||||
extra_group_object_classes: vec![LdapObjectClass::from("customGroupClass")],
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
mock.expect_get_user_details()
|
mock.expect_get_user_details()
|
||||||
@@ -963,7 +946,6 @@ mod tests {
|
|||||||
isEditable
|
isEditable
|
||||||
isHardcoded
|
isHardcoded
|
||||||
}
|
}
|
||||||
extraLdapObjectClasses
|
|
||||||
}
|
}
|
||||||
groupSchema {
|
groupSchema {
|
||||||
attributes {
|
attributes {
|
||||||
@@ -974,7 +956,6 @@ mod tests {
|
|||||||
isEditable
|
isEditable
|
||||||
isHardcoded
|
isHardcoded
|
||||||
}
|
}
|
||||||
extraLdapObjectClasses
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
@@ -1059,8 +1040,7 @@ mod tests {
|
|||||||
"isEditable": false,
|
"isEditable": false,
|
||||||
"isHardcoded": true,
|
"isHardcoded": true,
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
"extraLdapObjectClasses": ["customUserClass"],
|
|
||||||
},
|
},
|
||||||
"groupSchema": {
|
"groupSchema": {
|
||||||
"attributes": [
|
"attributes": [
|
||||||
@@ -1096,8 +1076,7 @@ mod tests {
|
|||||||
"isEditable": false,
|
"isEditable": false,
|
||||||
"isHardcoded": true,
|
"isHardcoded": true,
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
"extraLdapObjectClasses": [],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -1114,7 +1093,6 @@ mod tests {
|
|||||||
attributes {
|
attributes {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
extraLdapObjectClasses
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
@@ -1136,8 +1114,6 @@ mod tests {
|
|||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Vec::new(),
|
attributes: Vec::new(),
|
||||||
},
|
},
|
||||||
extra_user_object_classes: vec![LdapObjectClass::from("customUserClass")],
|
|
||||||
extra_group_object_classes: Vec::new(),
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1163,8 +1139,7 @@ mod tests {
|
|||||||
{"name": "mail"},
|
{"name": "mail"},
|
||||||
{"name": "user_id"},
|
{"name": "user_id"},
|
||||||
{"name": "uuid"},
|
{"name": "uuid"},
|
||||||
],
|
]
|
||||||
"extraLdapObjectClasses": ["customUserClass"],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ),
|
} ),
|
||||||
|
|||||||
@@ -1290,8 +1290,7 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec(),
|
b"person".to_vec()
|
||||||
b"customUserClass".to_vec(),
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -1333,8 +1332,7 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec(),
|
b"person".to_vec()
|
||||||
b"customUserClass".to_vec(),
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -1921,49 +1919,7 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec(),
|
b"person".to_vec()
|
||||||
b"customUserClass".to_vec(),
|
|
||||||
]
|
|
||||||
},]
|
|
||||||
}),
|
|
||||||
make_search_success()
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_search_filters_custom_object_class() {
|
|
||||||
let mut mock = MockTestBackendHandler::new();
|
|
||||||
mock.expect_list_users()
|
|
||||||
.with(eq(Some(UserRequestFilter::from(true))), eq(false))
|
|
||||||
.times(1)
|
|
||||||
.return_once(|_, _| {
|
|
||||||
Ok(vec![UserAndGroups {
|
|
||||||
user: User {
|
|
||||||
user_id: UserId::new("bob_1"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
groups: None,
|
|
||||||
}])
|
|
||||||
});
|
|
||||||
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
|
||||||
let request = make_user_search_request(
|
|
||||||
LdapFilter::Equality("objectClass".to_owned(), "CUSTOMuserCLASS".to_owned()),
|
|
||||||
vec!["objectclass"],
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
ldap_handler.do_search_or_dse(&request).await,
|
|
||||||
Ok(vec![
|
|
||||||
LdapOp::SearchResultEntry(LdapSearchResultEntry {
|
|
||||||
dn: "uid=bob_1,ou=people,dc=example,dc=com".to_string(),
|
|
||||||
attributes: vec![LdapPartialAttribute {
|
|
||||||
atype: "objectclass".to_string(),
|
|
||||||
vals: vec![
|
|
||||||
b"inetOrgPerson".to_vec(),
|
|
||||||
b"posixAccount".to_vec(),
|
|
||||||
b"mailAccount".to_vec(),
|
|
||||||
b"person".to_vec(),
|
|
||||||
b"customUserClass".to_vec(),
|
|
||||||
]
|
]
|
||||||
},]
|
},]
|
||||||
}),
|
}),
|
||||||
@@ -2027,8 +1983,7 @@ mod tests {
|
|||||||
b"inetOrgPerson".to_vec(),
|
b"inetOrgPerson".to_vec(),
|
||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec(),
|
b"person".to_vec()
|
||||||
b"customUserClass".to_vec(),
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -2113,7 +2068,6 @@ mod tests {
|
|||||||
b"posixAccount".to_vec(),
|
b"posixAccount".to_vec(),
|
||||||
b"mailAccount".to_vec(),
|
b"mailAccount".to_vec(),
|
||||||
b"person".to_vec(),
|
b"person".to_vec(),
|
||||||
b"customUserClass".to_vec(),
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
LdapPartialAttribute {
|
LdapPartialAttribute {
|
||||||
@@ -2895,11 +2849,6 @@ mod tests {
|
|||||||
is_hardcoded: false,
|
is_hardcoded: false,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
extra_user_object_classes: vec![
|
|
||||||
LdapObjectClass::from("customUserClass"),
|
|
||||||
LdapObjectClass::from("myUserClass"),
|
|
||||||
],
|
|
||||||
extra_group_object_classes: vec![LdapObjectClass::from("customGroupClass")],
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_readonly_handler(mock).await;
|
let mut ldap_handler = setup_bound_readonly_handler(mock).await;
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ mockall::mock! {
|
|||||||
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
||||||
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_user_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
async fn delete_group_attribute(&self, name: &AttributeName) -> Result<()>;
|
||||||
async fn add_user_object_class(&self, request: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn add_group_object_class(&self, request: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn delete_user_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
async fn delete_group_object_class(&self, name: &LdapObjectClass) -> Result<()>;
|
|
||||||
}
|
}
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl BackendHandler for TestBackendHandler {}
|
impl BackendHandler for TestBackendHandler {}
|
||||||
@@ -106,8 +102,6 @@ pub fn setup_default_schema(mock: &mut MockTestBackendHandler) {
|
|||||||
group_attributes: AttributeList {
|
group_attributes: AttributeList {
|
||||||
attributes: Vec::new(),
|
attributes: Vec::new(),
|
||||||
},
|
},
|
||||||
extra_user_object_classes: vec![LdapObjectClass::from("customUserClass")],
|
|
||||||
extra_group_object_classes: Vec::new(),
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user