Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changeset/changesets/eerily-striving-louse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
lldap-operator-traits: minor
---
Define async trait abstractions for lldap user, group, membership, attribute schema, and password operations
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/lldap-operator-traits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ description = "Trait definitions for lldap user and group operations"
workspace = true

[dependencies]

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt"] }
191 changes: 191 additions & 0 deletions crates/lldap-operator-traits/src/attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use std::future::Future;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AttributeType {
String,
Integer,
JpegPhoto,
DateTime,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AttributeSchema {
pub name: String,
pub attribute_type: AttributeType,
pub is_list: bool,
pub is_visible: bool,
pub is_editable: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AddAttributeInput {
pub name: String,
pub attribute_type: AttributeType,
pub is_list: bool,
pub is_visible: bool,
pub is_editable: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AttributeValue {
pub name: String,
pub value: Vec<String>,
}

pub trait AttributeSchemaClient {
type Error: std::error::Error + Send + Sync + 'static;

fn list_user_attribute_schema(
&self,
) -> impl Future<Output = Result<Vec<AttributeSchema>, Self::Error>> + Send;

fn list_group_attribute_schema(
&self,
) -> impl Future<Output = Result<Vec<AttributeSchema>, Self::Error>> + Send;

fn add_user_attribute(
&self,
input: &AddAttributeInput,
) -> impl Future<Output = Result<(), Self::Error>> + Send;

fn add_group_attribute(
&self,
input: &AddAttributeInput,
) -> impl Future<Output = Result<(), Self::Error>> + Send;

fn delete_user_attribute(
&self,
name: &str,
) -> impl Future<Output = Result<(), Self::Error>> + Send;

fn delete_group_attribute(
&self,
name: &str,
) -> impl Future<Output = Result<(), Self::Error>> + Send;
}

#[cfg(test)]
mod tests {
use std::fmt;

use super::*;

#[derive(Debug)]
struct MockError;

impl fmt::Display for MockError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("mock error")
}
}

impl std::error::Error for MockError {}

struct MockAttributeSchemaClient;

impl AttributeSchemaClient for MockAttributeSchemaClient {
type Error = MockError;

async fn list_user_attribute_schema(&self) -> Result<Vec<AttributeSchema>, Self::Error> {
Ok(vec![AttributeSchema {
name: "phone".to_owned(),
attribute_type: AttributeType::String,
is_list: false,
is_visible: true,
is_editable: true,
}])
}

async fn list_group_attribute_schema(&self) -> Result<Vec<AttributeSchema>, Self::Error> {
Ok(vec![])
}

async fn add_user_attribute(&self, _input: &AddAttributeInput) -> Result<(), Self::Error> {
Ok(())
}

async fn add_group_attribute(&self, _input: &AddAttributeInput) -> Result<(), Self::Error> {
Ok(())
}

async fn delete_user_attribute(&self, _name: &str) -> Result<(), Self::Error> {
Ok(())
}

async fn delete_group_attribute(&self, _name: &str) -> Result<(), Self::Error> {
Ok(())
}
}

#[tokio::test]
async fn list_user_attribute_schema_returns_schemas() {
let client = MockAttributeSchemaClient;
let schemas = client
.list_user_attribute_schema()
.await
.expect("should succeed");
assert_eq!(schemas.len(), 1);
assert_eq!(schemas[0].name, "phone");
assert_eq!(schemas[0].attribute_type, AttributeType::String);
}

#[tokio::test]
async fn list_group_attribute_schema_returns_empty() {
let client = MockAttributeSchemaClient;
let schemas = client
.list_group_attribute_schema()
.await
.expect("should succeed");
assert!(schemas.is_empty());
}

#[tokio::test]
async fn add_user_attribute_succeeds() {
let client = MockAttributeSchemaClient;
let input = AddAttributeInput {
name: "department".to_owned(),
attribute_type: AttributeType::String,
is_list: false,
is_visible: true,
is_editable: true,
};
client
.add_user_attribute(&input)
.await
.expect("should succeed");
}

#[tokio::test]
async fn add_group_attribute_succeeds() {
let client = MockAttributeSchemaClient;
let input = AddAttributeInput {
name: "location".to_owned(),
attribute_type: AttributeType::String,
is_list: false,
is_visible: true,
is_editable: false,
};
client
.add_group_attribute(&input)
.await
.expect("should succeed");
}

#[tokio::test]
async fn delete_user_attribute_succeeds() {
let client = MockAttributeSchemaClient;
client
.delete_user_attribute("phone")
.await
.expect("should succeed");
}

#[tokio::test]
async fn delete_group_attribute_succeeds() {
let client = MockAttributeSchemaClient;
client
.delete_group_attribute("location")
.await
.expect("should succeed");
}
}
Loading
Loading