Compare commits
1 Commits
group-ui
...
server-use
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a627e69e46 |
@@ -374,7 +374,6 @@ folder for help with:
|
|||||||
- [Kasm](example_configs/kasm.md)
|
- [Kasm](example_configs/kasm.md)
|
||||||
- [KeyCloak](example_configs/keycloak.md)
|
- [KeyCloak](example_configs/keycloak.md)
|
||||||
- [LibreNMS](example_configs/librenms.md)
|
- [LibreNMS](example_configs/librenms.md)
|
||||||
- [Maddy](example_configs/maddy.md)
|
|
||||||
- [Mastodon](example_configs/mastodon.env.example)
|
- [Mastodon](example_configs/mastodon.env.example)
|
||||||
- [Matrix](example_configs/matrix_synapse.yml)
|
- [Matrix](example_configs/matrix_synapse.yml)
|
||||||
- [Mealie](example_configs/mealie.md)
|
- [Mealie](example_configs/mealie.md)
|
||||||
|
|||||||
@@ -8,21 +8,5 @@ query GetGroupDetails($id: Int!) {
|
|||||||
id
|
id
|
||||||
displayName
|
displayName
|
||||||
}
|
}
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
schema {
|
|
||||||
groupSchema {
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
attributeType
|
|
||||||
isList
|
|
||||||
isVisible
|
|
||||||
isEditable
|
|
||||||
isHardcoded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
components::{
|
|
||||||
group_details::Attribute,
|
|
||||||
router::{AppRoute, Link},
|
|
||||||
},
|
|
||||||
infra::common_component::{CommonComponent, CommonComponentParts},
|
|
||||||
};
|
|
||||||
use anyhow::{bail, Error, Result};
|
|
||||||
use gloo_console::log;
|
|
||||||
use graphql_client::GraphQLQuery;
|
|
||||||
use yew::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
|
||||||
pub struct AttributeInputProps {
|
|
||||||
pub attribute: Attribute,
|
|
||||||
pub on_changed: Callback<(String, Vec<String>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component(SingleAttributeInput)]
|
|
||||||
fn single_attribute_input(props: &AttributeInputProps) -> Html {
|
|
||||||
let attribute = props.attribute.clone();
|
|
||||||
let on_changed = props.on_changed.clone();
|
|
||||||
let on_input = Callback::from(move |e: InputEvent| on_changed.emit((attribute.name.clone(), vec![e.data().unwrap_or_default()])));
|
|
||||||
html!{
|
|
||||||
<div class="row mb-3">
|
|
||||||
<label for={props.attribute.name.clone()}
|
|
||||||
class="form-label col-4 col-form-label">
|
|
||||||
{props.attribute.name.clone()}
|
|
||||||
{":"}
|
|
||||||
</label>
|
|
||||||
<div class="col-8">
|
|
||||||
<input id={props.attribute.name.clone()} name={props.attribute.name.clone()} type="text" class="form-control" oninput={on_input} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component(ListAttributeInput)]
|
|
||||||
fn list_attribute_input(props: &AttributeInputProps) -> Html {
|
|
||||||
html!{}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component(AttributeInput)]
|
|
||||||
fn attribute_input(props: &AttributeInputProps) -> Html {
|
|
||||||
if props.attribute.is_list {
|
|
||||||
html!{
|
|
||||||
<ListAttributeInput
|
|
||||||
attribute={props.attribute.clone()}
|
|
||||||
on_changed={props.on_changed.clone()} />
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
html!{
|
|
||||||
<SingleAttributeInput
|
|
||||||
attribute={props.attribute.clone()}
|
|
||||||
on_changed={props.on_changed.clone()} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
|
||||||
pub struct Props {
|
|
||||||
pub attributes: Vec<Attribute>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component(GroupAttributesForm)]
|
|
||||||
pub fn group_attributes_form(Props{ attributes }: &Props) -> Html {
|
|
||||||
let attributes = use_state(|| attributes.clone());
|
|
||||||
let on_changed = {
|
|
||||||
let attributes = attributes.clone();
|
|
||||||
Callback::from(move |(name, value): (String, Vec<String>)| {
|
|
||||||
let mut new_attributes = attributes.deref().clone();
|
|
||||||
new_attributes.iter_mut().filter(|attribute| attribute.name == name).for_each(|attribute| attribute.value = value.clone());
|
|
||||||
attributes.set(new_attributes.clone());
|
|
||||||
log!("New attributes:");
|
|
||||||
new_attributes.iter().for_each(|attribute| log!("Name: {attribute.name}, Value: {attribute.value}"));
|
|
||||||
})
|
|
||||||
};
|
|
||||||
html!{
|
|
||||||
{for attributes.iter().map(|attribute| html!{<AttributeInput attribute={attribute.clone()} on_changed={on_changed.clone()} />})}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ use crate::{
|
|||||||
components::{
|
components::{
|
||||||
add_group_member::{self, AddGroupMemberComponent},
|
add_group_member::{self, AddGroupMemberComponent},
|
||||||
remove_user_from_group::RemoveUserFromGroupComponent,
|
remove_user_from_group::RemoveUserFromGroupComponent,
|
||||||
group_attributes_form::GroupAttributesForm,
|
|
||||||
router::{AppRoute, Link},
|
router::{AppRoute, Link},
|
||||||
},
|
},
|
||||||
infra::common_component::{CommonComponent, CommonComponentParts},
|
infra::common_component::{CommonComponent, CommonComponentParts},
|
||||||
@@ -23,22 +22,12 @@ pub struct GetGroupDetails;
|
|||||||
pub type Group = get_group_details::GetGroupDetailsGroup;
|
pub type Group = get_group_details::GetGroupDetailsGroup;
|
||||||
pub type User = get_group_details::GetGroupDetailsGroupUsers;
|
pub type User = get_group_details::GetGroupDetailsGroupUsers;
|
||||||
pub type AddGroupMemberUser = add_group_member::User;
|
pub type AddGroupMemberUser = add_group_member::User;
|
||||||
pub type AttributeSchema = get_group_details::GetGroupDetailsSchemaGroupSchemaAttributes;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
|
||||||
pub struct Attribute {
|
|
||||||
pub name: String,
|
|
||||||
pub value: Vec<String>,
|
|
||||||
pub attribute_type: String,
|
|
||||||
pub is_list: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GroupDetails {
|
pub struct GroupDetails {
|
||||||
common: CommonComponentParts<Self>,
|
common: CommonComponentParts<Self>,
|
||||||
/// The group info. If none, the error is in `error`. If `error` is None, then we haven't
|
/// The group info. If none, the error is in `error`. If `error` is None, then we haven't
|
||||||
/// received the server response yet.
|
/// received the server response yet.
|
||||||
group: Option<Group>,
|
group: Option<Group>,
|
||||||
attributes: Vec<Attribute>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State machine describing the possible transitions of the component state.
|
/// State machine describing the possible transitions of the component state.
|
||||||
@@ -196,22 +185,7 @@ impl CommonComponent<GroupDetails> for GroupDetails {
|
|||||||
fn handle_msg(&mut self, _: &Context<Self>, msg: <Self as Component>::Message) -> Result<bool> {
|
fn handle_msg(&mut self, _: &Context<Self>, msg: <Self as Component>::Message) -> Result<bool> {
|
||||||
match msg {
|
match msg {
|
||||||
Msg::GroupDetailsResponse(response) => match response {
|
Msg::GroupDetailsResponse(response) => match response {
|
||||||
Ok(response) => {
|
Ok(group) => self.group = Some(group.group),
|
||||||
let group = response.group;
|
|
||||||
self.group = Some(group.clone());
|
|
||||||
let set_attributes = group.attributes.clone();
|
|
||||||
let mut attribute_schema = response.schema.group_schema.attributes;
|
|
||||||
attribute_schema.retain(|schema| !schema.is_hardcoded);
|
|
||||||
let attributes = 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: format!("{:?}",schema.attribute_type),
|
|
||||||
is_list: schema.is_list,
|
|
||||||
}
|
|
||||||
}).collect();
|
|
||||||
self.attributes = attributes;
|
|
||||||
},
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.group = None;
|
self.group = None;
|
||||||
bail!("Error getting user details: {}", e);
|
bail!("Error getting user details: {}", e);
|
||||||
@@ -248,7 +222,6 @@ impl Component for GroupDetails {
|
|||||||
let mut table = Self {
|
let mut table = Self {
|
||||||
common: CommonComponentParts::<Self>::create(),
|
common: CommonComponentParts::<Self>::create(),
|
||||||
group: None,
|
group: None,
|
||||||
attributes: Vec::default(),
|
|
||||||
};
|
};
|
||||||
table.get_group_details(ctx);
|
table.get_group_details(ctx);
|
||||||
table
|
table
|
||||||
@@ -266,7 +239,6 @@ impl Component for GroupDetails {
|
|||||||
html! {
|
html! {
|
||||||
<div>
|
<div>
|
||||||
{self.view_details(u)}
|
{self.view_details(u)}
|
||||||
<GroupAttributesForm attributes={self.attributes.clone()} />
|
|
||||||
{self.view_user_list(ctx, u)}
|
{self.view_user_list(ctx, u)}
|
||||||
{self.view_add_user_button(ctx, u)}
|
{self.view_add_user_button(ctx, u)}
|
||||||
{self.view_messages(error)}
|
{self.view_messages(error)}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ pub mod create_group;
|
|||||||
pub mod create_user;
|
pub mod create_user;
|
||||||
pub mod delete_group;
|
pub mod delete_group;
|
||||||
pub mod delete_user;
|
pub mod delete_user;
|
||||||
pub mod group_attributes_form;
|
|
||||||
pub mod group_details;
|
pub mod group_details;
|
||||||
pub mod group_table;
|
pub mod group_table;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
# Configuration for Maddy Mail Server
|
|
||||||
|
|
||||||
Documentation for maddy LDAP can be found [here](https://maddy.email/reference/auth/ldap/).
|
|
||||||
Maddy will automatically create an imap-acct if a new user connects via LDAP.
|
|
||||||
Replace `dc=example,dc=com` with your LLDAP configured domain.
|
|
||||||
|
|
||||||
|
|
||||||
## Simple Setup
|
|
||||||
Depending on the mail client(s) the simple setup can work for you. However, if this does not work for you, follow the instructions in the `Advanced Setup` section.
|
|
||||||
|
|
||||||
### DN Template
|
|
||||||
You only have to specify the dn template:
|
|
||||||
```
|
|
||||||
dn_template "cn={username},ou=people,dc=example,dc=com"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Config Example with Docker
|
|
||||||
Example maddy configuration with LLDAP running in docker.
|
|
||||||
You can replace `local_authdb` with another name if you want to use multiple auth backends.
|
|
||||||
If you only want to use one storage backend make sure to disable `auth.pass_table local_authdb` in your config if it is still active.
|
|
||||||
```
|
|
||||||
auth.ldap local_authdb {
|
|
||||||
urls ldap://lldap:3890
|
|
||||||
|
|
||||||
dn_template "cn={username},ou=people,dc=example,dc=com"
|
|
||||||
|
|
||||||
starttls off
|
|
||||||
debug off
|
|
||||||
connect_timeout 1m
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Advanced Setup
|
|
||||||
If the simple setup does not work for you, you can use a proper lookup.
|
|
||||||
|
|
||||||
### Bind Credentials
|
|
||||||
If you have a service account in LLDAP with restricted rights (e.g. `lldap_strict_readonly`), replace `admin` with your LLDAP service account.
|
|
||||||
Replace `admin_password` with the password of either the admin or service account.
|
|
||||||
```
|
|
||||||
bind plain "cn=admin,ou=people,dc=example,dc=com" "admin_password"
|
|
||||||
```
|
|
||||||
If you do not want to use plain auth check the [maddy LDAP page](https://maddy.email/reference/auth/ldap/) for other options.
|
|
||||||
|
|
||||||
### Base DN
|
|
||||||
```
|
|
||||||
base_dn "dc=example,dc=com"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Filter
|
|
||||||
Depending on the mail client, maddy receives and sends either the username or the full E-Mail address as username (even if the username is not an E-Mail).
|
|
||||||
For the username use:
|
|
||||||
```
|
|
||||||
filter "(&(objectClass=person)(uid={username}))"
|
|
||||||
```
|
|
||||||
For mapping the username (as E-Mail):
|
|
||||||
```
|
|
||||||
filter "(&(objectClass=person)(mail={username}))"
|
|
||||||
```
|
|
||||||
For allowing both, username and username as E-Mail use:
|
|
||||||
```
|
|
||||||
filter "(&(|(uid={username})(mail={username}))(objectClass=person))"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Config Example with Docker
|
|
||||||
Example maddy configuration with LLDAP running in docker.
|
|
||||||
You can replace `local_authdb` with another name if you want to use multiple auth backends.
|
|
||||||
If you only want to use one storage backend make sure to disable `auth.pass_table local_authdb` in your config if it is still active.
|
|
||||||
```
|
|
||||||
auth.ldap local_authdb {
|
|
||||||
urls ldap://lldap:3890
|
|
||||||
|
|
||||||
bind plain "cn=admin,ou=people,dc=example,dc=com" "admin_password"
|
|
||||||
base_dn "dc=example,dc=com"
|
|
||||||
filter "(&(|(uid={username})(mail={username}))(objectClass=person))"
|
|
||||||
|
|
||||||
starttls off
|
|
||||||
debug off
|
|
||||||
connect_timeout 1m
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ use sea_orm::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{EnumString, IntoStaticStr};
|
use strum::{EnumString, IntoStaticStr};
|
||||||
|
|
||||||
|
use super::handler::AttributeSchema;
|
||||||
pub use super::model::UserColumn;
|
pub use super::model::UserColumn;
|
||||||
pub use lldap_auth::types::UserId;
|
pub use lldap_auth::types::UserId;
|
||||||
|
|
||||||
@@ -533,6 +534,38 @@ pub struct UserAndGroups {
|
|||||||
pub groups: Option<Vec<GroupDetails>>,
|
pub groups: Option<Vec<GroupDetails>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct AttributeValueAndSchema {
|
||||||
|
pub value: AttributeValue,
|
||||||
|
pub schema: AttributeSchema,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct UserAndSchema {
|
||||||
|
pub user: User,
|
||||||
|
pub schema: Vec<AttributeSchema>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct GroupAndSchema {
|
||||||
|
pub group: Group,
|
||||||
|
pub schema: Vec<AttributeSchema>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct GroupDetailsAndSchema {
|
||||||
|
pub group: GroupDetails,
|
||||||
|
pub schema: Vec<AttributeSchema>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct UserAndGroupsAndSchema {
|
||||||
|
pub user: User,
|
||||||
|
pub user_schema: Vec<AttributeSchema>,
|
||||||
|
pub group: Option<Vec<GroupDetails>>,
|
||||||
|
pub group_schema: Vec<AttributeSchema>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -25,9 +25,14 @@ type DomainRequestFilter = crate::domain::handler::UserRequestFilter;
|
|||||||
type DomainUser = crate::domain::types::User;
|
type DomainUser = crate::domain::types::User;
|
||||||
type DomainGroup = crate::domain::types::Group;
|
type DomainGroup = crate::domain::types::Group;
|
||||||
type DomainUserAndGroups = crate::domain::types::UserAndGroups;
|
type DomainUserAndGroups = crate::domain::types::UserAndGroups;
|
||||||
|
type DomainUserAndSchema = crate::domain::types::UserAndSchema;
|
||||||
|
type DomainGroupAndSchema = crate::domain::types::GroupAndSchema;
|
||||||
|
type DomainGroupDetailsAndSchema = crate::domain::types::GroupDetailsAndSchema;
|
||||||
|
type DomainUserAndGroupsAndSchema = crate::domain::types::UserAndGroupsAndSchema;
|
||||||
type DomainAttributeList = crate::domain::handler::AttributeList;
|
type DomainAttributeList = crate::domain::handler::AttributeList;
|
||||||
type DomainAttributeSchema = crate::domain::handler::AttributeSchema;
|
type DomainAttributeSchema = crate::domain::handler::AttributeSchema;
|
||||||
type DomainAttributeValue = crate::domain::types::AttributeValue;
|
type DomainAttributeValue = crate::domain::types::AttributeValue;
|
||||||
|
type DomainAttributeValueAndSchema = crate::domain::types::AttributeValueAndSchema;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
||||||
/// A filter for requests, specifying a boolean expression based on field constraints. Only one of
|
/// A filter for requests, specifying a boolean expression based on field constraints. Only one of
|
||||||
@@ -143,11 +148,15 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||||||
&span,
|
&span,
|
||||||
"Unauthorized access to user data",
|
"Unauthorized access to user data",
|
||||||
))?;
|
))?;
|
||||||
Ok(handler
|
let user = handler
|
||||||
.get_user_details(&user_id)
|
.get_user_details(&user_id)
|
||||||
.instrument(span)
|
.instrument(span)
|
||||||
.await
|
.await?;
|
||||||
.map(Into::into)?)
|
let schema = self.get_schema(context, span).await?;
|
||||||
|
return Ok(DomainUserAndSchema {
|
||||||
|
user,
|
||||||
|
schema: schema.get_schema().user_attributes.attributes,
|
||||||
|
}.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn users(
|
async fn users(
|
||||||
@@ -237,6 +246,7 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||||||
/// Represents a single user.
|
/// Represents a single user.
|
||||||
pub struct User<Handler: BackendHandler> {
|
pub struct User<Handler: BackendHandler> {
|
||||||
user: DomainUser,
|
user: DomainUser,
|
||||||
|
schema: Vec<DomainAttributeSchema>,
|
||||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +255,7 @@ impl<Handler: BackendHandler> Default for User<Handler> {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
user: DomainUser::default(),
|
user: DomainUser::default(),
|
||||||
|
schema: Vec::default(),
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,19 +343,21 @@ impl<Handler: BackendHandler> User<Handler> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: BackendHandler> From<DomainUser> for User<Handler> {
|
impl<Handler: BackendHandler> From<DomainUserAndSchema> for User<Handler> {
|
||||||
fn from(user: DomainUser) -> Self {
|
fn from(user: DomainUserAndSchema) -> Self {
|
||||||
Self {
|
Self {
|
||||||
user,
|
user: user.user,
|
||||||
|
schema: user.schema,
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: BackendHandler> From<DomainUserAndGroups> for User<Handler> {
|
impl<Handler: BackendHandler> From<DomainUserAndGroupsAndSchema> for User<Handler> {
|
||||||
fn from(user: DomainUserAndGroups) -> Self {
|
fn from(user: DomainUserAndGroupsAndSchema) -> Self {
|
||||||
Self {
|
Self {
|
||||||
user: user.user,
|
user: user.user,
|
||||||
|
schema: user.user_schema,
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,6 +371,7 @@ pub struct Group<Handler: BackendHandler> {
|
|||||||
creation_date: chrono::NaiveDateTime,
|
creation_date: chrono::NaiveDateTime,
|
||||||
uuid: String,
|
uuid: String,
|
||||||
attributes: Vec<DomainAttributeValue>,
|
attributes: Vec<DomainAttributeValue>,
|
||||||
|
schema: Vec<DomainAttributeSchema>,
|
||||||
members: Option<Vec<String>>,
|
members: Option<Vec<String>>,
|
||||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||||
}
|
}
|
||||||
@@ -409,29 +423,31 @@ impl<Handler: BackendHandler> Group<Handler> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: BackendHandler> From<GroupDetails> for Group<Handler> {
|
impl<Handler: BackendHandler> From<DomainGroupDetailsAndSchema> for Group<Handler> {
|
||||||
fn from(group_details: GroupDetails) -> Self {
|
fn from(group_details: DomainGroupDetailsAndSchema) -> Self {
|
||||||
Self {
|
Self {
|
||||||
group_id: group_details.group_id.0,
|
group_id: group_details.group.group_id.0,
|
||||||
display_name: group_details.display_name.to_string(),
|
display_name: group_details.group.display_name.to_string(),
|
||||||
creation_date: group_details.creation_date,
|
creation_date: group_details.group.creation_date,
|
||||||
uuid: group_details.uuid.into_string(),
|
uuid: group_details.group.uuid.into_string(),
|
||||||
attributes: group_details.attributes,
|
attributes: group_details.group.attributes,
|
||||||
members: None,
|
members: None,
|
||||||
|
schema: group_details.schema,
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: BackendHandler> From<DomainGroup> for Group<Handler> {
|
impl<Handler: BackendHandler> From<DomainGroupAndSchema> for Group<Handler> {
|
||||||
fn from(group: DomainGroup) -> Self {
|
fn from(group: DomainGroupAndSchema) -> Self {
|
||||||
Self {
|
Self {
|
||||||
group_id: group.id.0,
|
group_id: group.group.id.0,
|
||||||
display_name: group.display_name.to_string(),
|
display_name: group.group.display_name.to_string(),
|
||||||
creation_date: group.creation_date,
|
creation_date: group.group.creation_date,
|
||||||
uuid: group.uuid.into_string(),
|
uuid: group.group.uuid.into_string(),
|
||||||
attributes: group.attributes,
|
attributes: group.group.attributes,
|
||||||
members: Some(group.users.into_iter().map(UserId::into_string).collect()),
|
members: Some(group.group.users.into_iter().map(UserId::into_string).collect()),
|
||||||
|
schema: group.schema,
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -529,6 +545,7 @@ impl<Handler: BackendHandler> From<PublicSchema> for Schema<Handler> {
|
|||||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
pub struct AttributeValue<Handler: BackendHandler, Extractor> {
|
pub struct AttributeValue<Handler: BackendHandler, Extractor> {
|
||||||
attribute: DomainAttributeValue,
|
attribute: DomainAttributeValue,
|
||||||
|
schema: DomainAttributeSchema,
|
||||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||||
_phantom_extractor: std::marker::PhantomData<Extractor>,
|
_phantom_extractor: std::marker::PhantomData<Extractor>,
|
||||||
}
|
}
|
||||||
@@ -601,12 +618,13 @@ pub fn serialize_attribute(
|
|||||||
.ok_or_else(|| FieldError::from(anyhow::anyhow!("Unknown attribute: {}", &attribute.name)))
|
.ok_or_else(|| FieldError::from(anyhow::anyhow!("Unknown attribute: {}", &attribute.name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: BackendHandler, Extractor> From<DomainAttributeValue>
|
impl<Handler: BackendHandler, Extractor> From<DomainAttributeValueAndSchema>
|
||||||
for AttributeValue<Handler, Extractor>
|
for AttributeValue<Handler, Extractor>
|
||||||
{
|
{
|
||||||
fn from(value: DomainAttributeValue) -> Self {
|
fn from(value: DomainAttributeValueAndSchema) -> Self {
|
||||||
Self {
|
Self {
|
||||||
attribute: value,
|
attribute: value.value,
|
||||||
|
schema: value.schema,
|
||||||
_phantom: std::marker::PhantomData,
|
_phantom: std::marker::PhantomData,
|
||||||
_phantom_extractor: std::marker::PhantomData,
|
_phantom_extractor: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user