Compare commits

..

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 02572bdf45 Switch to action-rs/toolchain for Rust installation in CI
Co-authored-by: nitnelave <796633+nitnelave@users.noreply.github.com>
2025-08-21 00:01:39 +00:00
copilot-swe-agent[bot] 30cb4ceae0 Use variable for Rust version in CI workflow
Replace hardcoded "1.85.0" references with RUST_VERSION environment variable for better maintainability. Now the Rust version only needs to be updated in one place.

Co-authored-by: nitnelave <796633+nitnelave@users.noreply.github.com>
2025-08-20 23:58:18 +00:00
copilot-swe-agent[bot] 2e2a67c13b Pin Rust version to 1.85.0 in CI workflows and Docker images
Co-authored-by: nitnelave <796633+nitnelave@users.noreply.github.com>
2025-08-20 23:52:12 +00:00
copilot-swe-agent[bot] 4d8fccb502 Initial plan 2025-08-20 23:43:32 +00:00
28 changed files with 94 additions and 370 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
FROM rust:1.85 FROM rust:1.85.0
ARG USERNAME=lldapdev ARG USERNAME=lldapdev
# We need to keep the user as 1001 to match the GitHub runner's UID. # We need to keep the user as 1001 to match the GitHub runner's UID.
+1
View File
@@ -0,0 +1 @@
* @nitnelave
+1 -1
View File
@@ -1,5 +1,5 @@
# Keep tracking base image # Keep tracking base image
FROM rust:1.85-slim-bookworm FROM rust:1.85.0-slim-bookworm
# Set needed env path # Set needed env path
ENV PATH="/opt/armv7l-linux-musleabihf-cross/:/opt/armv7l-linux-musleabihf-cross/bin/:/opt/aarch64-linux-musl-cross/:/opt/aarch64-linux-musl-cross/bin/:/opt/x86_64-linux-musl-cross/:/opt/x86_64-linux-musl-cross/bin/:$PATH" ENV PATH="/opt/armv7l-linux-musleabihf-cross/:/opt/armv7l-linux-musleabihf-cross/bin/:/opt/aarch64-linux-musl-cross/:/opt/aarch64-linux-musl-cross/bin/:/opt/x86_64-linux-musl-cross/:/opt/x86_64-linux-musl-cross/bin/:$PATH"
+44 -21
View File
@@ -8,7 +8,7 @@ on:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
MSRV: 1.85.0 RUST_VERSION: "1.85.0"
jobs: jobs:
pre_job: pre_job:
@@ -35,18 +35,18 @@ jobs:
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.0
- name: Install Rust - name: Install Rust ${{ env.RUST_VERSION }}
id: toolchain uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with: with:
toolchain: "${{ env.MSRV }}" toolchain: ${{ env.RUST_VERSION }}
override: true
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: Build - name: Build
run: cargo build --verbose --workspace run: cargo build --verbose --workspace
- name: Run tests - name: Run tests
run: cargo +${{steps.toolchain.outputs.name}} test --verbose --workspace run: cargo test --verbose --workspace
- name: Generate GraphQL schema - name: Generate GraphQL schema
run: cargo +${{steps.toolchain.outputs.name}} run -- export_graphql_schema -o generated_schema.graphql run: cargo run -- export_graphql_schema -o generated_schema.graphql
- name: Check schema - name: Check schema
run: diff schema.graphql generated_schema.graphql || (echo "The schema file is out of date. Please run `./export_schema.sh`" && false) run: diff schema.graphql generated_schema.graphql || (echo "The schema file is out of date. Please run `./export_schema.sh`" && false)
@@ -59,14 +59,21 @@ jobs:
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.0
- name: Install Rust
id: toolchain - name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master uses: actions-rs/toolchain@v1
with: with:
toolchain: "${{ env.MSRV }}" toolchain: ${{ env.RUST_VERSION }}
components: clippy override: true
components: clippy
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- run: cargo +${{steps.toolchain.outputs.name}} clippy --tests --workspace -- -D warnings
- name: Run cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --tests --all -- -D warnings
format: format:
name: cargo fmt name: cargo fmt
@@ -76,14 +83,21 @@ jobs:
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.0
- name: Install Rust
id: toolchain - name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master uses: actions-rs/toolchain@v1
with: with:
toolchain: "${{ env.MSRV }}" toolchain: ${{ env.RUST_VERSION }}
components: rustfmt override: true
components: rustfmt
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- run: cargo +${{steps.toolchain.outputs.name}} fmt --check --all
- name: Run cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
coverage: coverage:
name: Code coverage name: Code coverage
@@ -96,8 +110,17 @@ jobs:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.0
- name: Install Rust - name: Install Rust ${{ env.RUST_VERSION }} and nightly
run: rustup toolchain install nightly --component llvm-tools-preview && rustup component add llvm-tools-preview --toolchain stable-x86_64-unknown-linux-gnu uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
components: llvm-tools-preview
- name: Install nightly toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: llvm-tools-preview
- uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov
Generated
-1
View File
@@ -2729,7 +2729,6 @@ dependencies = [
"lldap_domain_handlers", "lldap_domain_handlers",
"lldap_domain_model", "lldap_domain_model",
"lldap_ldap", "lldap_ldap",
"lldap_opaque_handler",
"lldap_sql_backend_handler", "lldap_sql_backend_handler",
"lldap_test_utils", "lldap_test_utils",
"lldap_validation", "lldap_validation",
+1
View File
@@ -16,6 +16,7 @@ edition = "2024"
homepage = "https://github.com/lldap/lldap" homepage = "https://github.com/lldap/lldap"
license = "GPL-3.0-only" license = "GPL-3.0-only"
repository = "https://github.com/lldap/lldap" repository = "https://github.com/lldap/lldap"
rust-version = "1.85.0"
[profile.release] [profile.release]
lto = true lto = true
+1 -1
View File
@@ -1,5 +1,5 @@
# Build image # Build image
FROM rust:alpine3.21 AS chef FROM rust:1.85.0-alpine3.21 AS chef
RUN set -x \ RUN set -x \
# Add user # Add user
+2 -17
View File
@@ -13,12 +13,7 @@ pub mod group {
"creation_date" => Some(AttributeDescription { "creation_date" => Some(AttributeDescription {
attribute_identifier: name, attribute_identifier: name,
attribute_name: "creationdate", attribute_name: "creationdate",
aliases: vec![name, "createtimestamp"], aliases: vec![name, "createtimestamp", "modifytimestamp"],
}),
"modified_date" => Some(AttributeDescription {
attribute_identifier: name,
attribute_name: "modifydate",
aliases: vec![name, "modifytimestamp"],
}), }),
"display_name" => Some(AttributeDescription { "display_name" => Some(AttributeDescription {
attribute_identifier: name, attribute_identifier: name,
@@ -65,17 +60,7 @@ pub mod user {
"creation_date" => Some(AttributeDescription { "creation_date" => Some(AttributeDescription {
attribute_identifier: name, attribute_identifier: name,
attribute_name: "creationdate", attribute_name: "creationdate",
aliases: vec![name, "createtimestamp"], aliases: vec![name, "createtimestamp", "modifytimestamp"],
}),
"modified_date" => Some(AttributeDescription {
attribute_identifier: name,
attribute_name: "modifydate",
aliases: vec![name, "modifytimestamp"],
}),
"password_modified_date" => Some(AttributeDescription {
attribute_identifier: name,
attribute_name: "passwordmodifydate",
aliases: vec![name, "pwdchangedtime"],
}), }),
"display_name" => Some(AttributeDescription { "display_name" => Some(AttributeDescription {
attribute_identifier: name, attribute_identifier: name,
-3
View File
@@ -14,7 +14,6 @@ pub struct Model {
pub lowercase_display_name: String, pub lowercase_display_name: String,
pub creation_date: chrono::NaiveDateTime, pub creation_date: chrono::NaiveDateTime,
pub uuid: Uuid, pub uuid: Uuid,
pub modified_date: chrono::NaiveDateTime,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
@@ -40,7 +39,6 @@ impl From<Model> for lldap_domain::types::Group {
uuid: group.uuid, uuid: group.uuid,
users: vec![], users: vec![],
attributes: Vec::new(), attributes: Vec::new(),
modified_date: group.modified_date,
} }
} }
} }
@@ -53,7 +51,6 @@ impl From<Model> for lldap_domain::types::GroupDetails {
creation_date: group.creation_date, creation_date: group.creation_date,
uuid: group.uuid, uuid: group.uuid,
attributes: Vec::new(), attributes: Vec::new(),
modified_date: group.modified_date,
} }
} }
} }
-8
View File
@@ -21,8 +21,6 @@ pub struct Model {
pub totp_secret: Option<String>, pub totp_secret: Option<String>,
pub mfa_type: Option<String>, pub mfa_type: Option<String>,
pub uuid: Uuid, pub uuid: Uuid,
pub modified_date: chrono::NaiveDateTime,
pub password_modified_date: chrono::NaiveDateTime,
} }
impl EntityName for Entity { impl EntityName for Entity {
@@ -42,8 +40,6 @@ pub enum Column {
TotpSecret, TotpSecret,
MfaType, MfaType,
Uuid, Uuid,
ModifiedDate,
PasswordModifiedDate,
} }
impl ColumnTrait for Column { impl ColumnTrait for Column {
@@ -60,8 +56,6 @@ impl ColumnTrait for Column {
Column::TotpSecret => ColumnType::String(StringLen::N(64)), Column::TotpSecret => ColumnType::String(StringLen::N(64)),
Column::MfaType => ColumnType::String(StringLen::N(64)), Column::MfaType => ColumnType::String(StringLen::N(64)),
Column::Uuid => ColumnType::String(StringLen::N(36)), Column::Uuid => ColumnType::String(StringLen::N(36)),
Column::ModifiedDate => ColumnType::DateTime,
Column::PasswordModifiedDate => ColumnType::DateTime,
} }
.def() .def()
} }
@@ -127,8 +121,6 @@ impl From<Model> for lldap_domain::types::User {
creation_date: user.creation_date, creation_date: user.creation_date,
uuid: user.uuid, uuid: user.uuid,
attributes: Vec::new(), attributes: Vec::new(),
modified_date: user.modified_date,
password_modified_date: user.password_modified_date,
} }
} }
} }
-27
View File
@@ -34,24 +34,6 @@ impl From<Schema> for PublicSchema {
is_hardcoded: true, is_hardcoded: true,
is_readonly: true, is_readonly: true,
}, },
AttributeSchema {
name: "modified_date".into(),
attribute_type: AttributeType::DateTime,
is_list: false,
is_visible: true,
is_editable: false,
is_hardcoded: true,
is_readonly: true,
},
AttributeSchema {
name: "password_modified_date".into(),
attribute_type: AttributeType::DateTime,
is_list: false,
is_visible: true,
is_editable: false,
is_hardcoded: true,
is_readonly: true,
},
AttributeSchema { AttributeSchema {
name: "mail".into(), name: "mail".into(),
attribute_type: AttributeType::String, attribute_type: AttributeType::String,
@@ -103,15 +85,6 @@ impl From<Schema> for PublicSchema {
is_hardcoded: true, is_hardcoded: true,
is_readonly: true, is_readonly: true,
}, },
AttributeSchema {
name: "modified_date".into(),
attribute_type: AttributeType::DateTime,
is_list: false,
is_visible: true,
is_editable: false,
is_hardcoded: true,
is_readonly: true,
},
AttributeSchema { AttributeSchema {
name: "uuid".into(), name: "uuid".into(),
attribute_type: AttributeType::String, attribute_type: AttributeType::String,
-6
View File
@@ -546,8 +546,6 @@ pub struct User {
pub creation_date: NaiveDateTime, pub creation_date: NaiveDateTime,
pub uuid: Uuid, pub uuid: Uuid,
pub attributes: Vec<Attribute>, pub attributes: Vec<Attribute>,
pub modified_date: NaiveDateTime,
pub password_modified_date: NaiveDateTime,
} }
#[cfg(feature = "test")] #[cfg(feature = "test")]
@@ -561,8 +559,6 @@ impl Default for User {
creation_date: epoch, creation_date: epoch,
uuid: Uuid::from_name_and_date("", &epoch), uuid: Uuid::from_name_and_date("", &epoch),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: epoch,
password_modified_date: epoch,
} }
} }
} }
@@ -658,7 +654,6 @@ pub struct Group {
pub uuid: Uuid, pub uuid: Uuid,
pub users: Vec<UserId>, pub users: Vec<UserId>,
pub attributes: Vec<Attribute>, pub attributes: Vec<Attribute>,
pub modified_date: NaiveDateTime,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
@@ -668,7 +663,6 @@ pub struct GroupDetails {
pub creation_date: NaiveDateTime, pub creation_date: NaiveDateTime,
pub uuid: Uuid, pub uuid: Uuid,
pub attributes: Vec<Attribute>, pub attributes: Vec<Attribute>,
pub modified_date: NaiveDateTime,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
+11 -15
View File
@@ -55,24 +55,20 @@ version = "1"
mockall = "0.11.4" mockall = "0.11.4"
pretty_assertions = "1" pretty_assertions = "1"
[dev-dependencies.lldap_auth] #[dev-dependencies.lldap_auth]
path = "../auth" #path = "../auth"
features = ["test"] #features = ["test"]
#
[dev-dependencies.lldap_domain] #[dev-dependencies.lldap_opaque_handler]
path = "../domain" #path = "../opaque-handler"
features = ["test"] #features = ["test"]
[dev-dependencies.lldap_opaque_handler]
path = "../opaque-handler"
features = ["test"]
[dev-dependencies.lldap_test_utils] [dev-dependencies.lldap_test_utils]
path = "../test-utils" path = "../test-utils"
#
[dev-dependencies.lldap_sql_backend_handler] #[dev-dependencies.lldap_sql_backend_handler]
path = "../sql-backend-handler" #path = "../sql-backend-handler"
features = ["test"] #features = ["test"]
[dev-dependencies.tokio] [dev-dependencies.tokio]
features = ["full"] features = ["full"]
-48
View File
@@ -716,8 +716,6 @@ impl<Handler: BackendHandler> AttributeValue<Handler> {
let value: Option<DomainAttributeValue> = match attribute_schema.name.as_str() { let value: Option<DomainAttributeValue> = match attribute_schema.name.as_str() {
"user_id" => Some(user.user_id.clone().into_string().into()), "user_id" => Some(user.user_id.clone().into_string().into()),
"creation_date" => Some(user.creation_date.into()), "creation_date" => Some(user.creation_date.into()),
"modified_date" => Some(user.modified_date.into()),
"password_modified_date" => Some(user.password_modified_date.into()),
"mail" => Some(user.email.clone().into_string().into()), "mail" => Some(user.email.clone().into_string().into()),
"uuid" => Some(user.uuid.clone().into_string().into()), "uuid" => Some(user.uuid.clone().into_string().into()),
"display_name" => user.display_name.as_ref().map(|d| d.clone().into()), "display_name" => user.display_name.as_ref().map(|d| d.clone().into()),
@@ -762,7 +760,6 @@ impl<Handler: BackendHandler> AttributeValue<Handler> {
match attribute_schema.name.as_str() { match attribute_schema.name.as_str() {
"group_id" => (group.id.0 as i64).into(), "group_id" => (group.id.0 as i64).into(),
"creation_date" => group.creation_date.into(), "creation_date" => group.creation_date.into(),
"modified_date" => group.modified_date.into(),
"uuid" => group.uuid.clone().into_string().into(), "uuid" => group.uuid.clone().into_string().into(),
"display_name" => group.display_name.clone().into_string().into(), "display_name" => group.display_name.clone().into_string().into(),
_ => panic!("Unexpected hardcoded attribute: {}", attribute_schema.name), _ => panic!("Unexpected hardcoded attribute: {}", attribute_schema.name),
@@ -805,7 +802,6 @@ impl<Handler: BackendHandler> AttributeValue<Handler> {
match attribute_schema.name.as_str() { match attribute_schema.name.as_str() {
"group_id" => (group.group_id.0 as i64).into(), "group_id" => (group.group_id.0 as i64).into(),
"creation_date" => group.creation_date.into(), "creation_date" => group.creation_date.into(),
"modified_date" => group.modified_date.into(),
"uuid" => group.uuid.clone().into_string().into(), "uuid" => group.uuid.clone().into_string().into(),
"display_name" => group.display_name.clone().into_string().into(), "display_name" => group.display_name.clone().into_string().into(),
_ => panic!("Unexpected hardcoded attribute: {}", attribute_schema.name), _ => panic!("Unexpected hardcoded attribute: {}", attribute_schema.name),
@@ -962,7 +958,6 @@ mod tests {
name: "club_name".into(), name: "club_name".into(),
value: "Gang of Four".to_string().into(), value: "Gang of Four".to_string().into(),
}], }],
modified_date: chrono::Utc.timestamp_nanos(42).naive_utc(),
}); });
groups.insert(GroupDetails { groups.insert(GroupDetails {
group_id: GroupId(7), group_id: GroupId(7),
@@ -970,7 +965,6 @@ mod tests {
creation_date: chrono::Utc.timestamp_nanos(12).naive_utc(), creation_date: chrono::Utc.timestamp_nanos(12).naive_utc(),
uuid: lldap_domain::uuid!("b1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: lldap_domain::uuid!("b1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_nanos(12).naive_utc(),
}); });
mock.expect_get_user_groups() mock.expect_get_user_groups()
.with(eq(UserId::new("bob"))) .with(eq(UserId::new("bob")))
@@ -999,14 +993,6 @@ mod tests {
"name": "mail", "name": "mail",
"value": ["bob@bobbers.on"], "value": ["bob@bobbers.on"],
}, },
{
"name": "modified_date",
"value": ["1970-01-01T00:00:00+00:00"],
},
{
"name": "password_modified_date",
"value": ["1970-01-01T00:00:00+00:00"],
},
{ {
"name": "user_id", "name": "user_id",
"value": ["bob"], "value": ["bob"],
@@ -1040,10 +1026,6 @@ mod tests {
"name": "group_id", "name": "group_id",
"value": ["3"], "value": ["3"],
}, },
{
"name": "modified_date",
"value": ["1970-01-01T00:00:00.000000042+00:00"],
},
{ {
"name": "uuid", "name": "uuid",
"value": ["a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"], "value": ["a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"],
@@ -1071,10 +1053,6 @@ mod tests {
"name": "group_id", "name": "group_id",
"value": ["7"], "value": ["7"],
}, },
{
"name": "modified_date",
"value": ["1970-01-01T00:00:00.000000012+00:00"],
},
{ {
"name": "uuid", "name": "uuid",
"value": ["b1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"], "value": ["b1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"],
@@ -1268,22 +1246,6 @@ mod tests {
"isEditable": true, "isEditable": true,
"isHardcoded": true, "isHardcoded": true,
}, },
{
"name": "modified_date",
"attributeType": "DATE_TIME",
"isList": false,
"isVisible": true,
"isEditable": false,
"isHardcoded": true,
},
{
"name": "password_modified_date",
"attributeType": "DATE_TIME",
"isList": false,
"isVisible": true,
"isEditable": false,
"isHardcoded": true,
},
{ {
"name": "user_id", "name": "user_id",
"attributeType": "STRING", "attributeType": "STRING",
@@ -1329,14 +1291,6 @@ mod tests {
"isEditable": false, "isEditable": false,
"isHardcoded": true, "isHardcoded": true,
}, },
{
"name": "modified_date",
"attributeType": "DATE_TIME",
"isList": false,
"isVisible": true,
"isEditable": false,
"isHardcoded": true,
},
{ {
"name": "uuid", "name": "uuid",
"attributeType": "STRING", "attributeType": "STRING",
@@ -1411,8 +1365,6 @@ mod tests {
{"name": "creation_date"}, {"name": "creation_date"},
{"name": "display_name"}, {"name": "display_name"},
{"name": "mail"}, {"name": "mail"},
{"name": "modified_date"},
{"name": "password_modified_date"},
{"name": "user_id"}, {"name": "user_id"},
{"name": "uuid"}, {"name": "uuid"},
], ],
-2
View File
@@ -124,7 +124,6 @@ mod tests {
users: vec![UserId::new("bob")], users: vec![UserId::new("bob")],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
let ldap_handler = setup_bound_admin_handler(mock).await; let ldap_handler = setup_bound_admin_handler(mock).await;
@@ -219,7 +218,6 @@ mod tests {
users: vec![UserId::new("bob")], users: vec![UserId::new("bob")],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
let ldap_handler = setup_bound_admin_handler(mock).await; let ldap_handler = setup_bound_admin_handler(mock).await;
-10
View File
@@ -72,12 +72,6 @@ pub fn get_group_attribute(
.to_rfc3339() .to_rfc3339()
.into_bytes(), .into_bytes(),
], ],
GroupFieldType::ModifiedDate => vec![
chrono::Utc
.from_utc_datetime(&group.modified_date)
.to_rfc3339()
.into_bytes(),
],
GroupFieldType::Member => group GroupFieldType::Member => group
.users .users
.iter() .iter()
@@ -266,10 +260,6 @@ fn convert_group_filter(
code: LdapResultCode::UnwillingToPerform, code: LdapResultCode::UnwillingToPerform,
message: "Creation date filter for groups not supported".to_owned(), message: "Creation date filter for groups not supported".to_owned(),
}), }),
GroupFieldType::ModifiedDate => Err(LdapError {
code: LdapResultCode::UnwillingToPerform,
message: "Modified date filter for groups not supported".to_owned(),
}),
} }
} }
LdapFilter::And(filters) => Ok(GroupRequestFilter::And( LdapFilter::And(filters) => Ok(GroupRequestFilter::And(
-12
View File
@@ -93,18 +93,6 @@ pub fn get_user_attribute(
.to_rfc3339() .to_rfc3339()
.into_bytes(), .into_bytes(),
], ],
UserFieldType::PrimaryField(UserColumn::ModifiedDate) => vec![
chrono::Utc
.from_utc_datetime(&user.modified_date)
.to_rfc3339()
.into_bytes(),
],
UserFieldType::PrimaryField(UserColumn::PasswordModifiedDate) => vec![
chrono::Utc
.from_utc_datetime(&user.password_modified_date)
.to_rfc3339()
.into_bytes(),
],
UserFieldType::Attribute(attr, _, _) => get_custom_attribute(&user.attributes, &attr)?, UserFieldType::Attribute(attr, _, _) => get_custom_attribute(&user.attributes, &attr)?,
UserFieldType::NoMatch => match attribute.as_str() { UserFieldType::NoMatch => match attribute.as_str() {
"1.1" => return None, "1.1" => return None,
+4 -10
View File
@@ -239,15 +239,9 @@ pub fn map_user_field(field: &AttributeName, schema: &PublicSchema) -> UserField
AttributeType::JpegPhoto, AttributeType::JpegPhoto,
false, false,
), ),
"creationdate" | "createtimestamp" | "creation_date" => { "creationdate" | "createtimestamp" | "modifytimestamp" | "creation_date" => {
UserFieldType::PrimaryField(UserColumn::CreationDate) UserFieldType::PrimaryField(UserColumn::CreationDate)
} }
"modifytimestamp" | "modifydate" | "modified_date" => {
UserFieldType::PrimaryField(UserColumn::ModifiedDate)
}
"pwdchangedtime" | "passwordmodifydate" | "password_modified_date" => {
UserFieldType::PrimaryField(UserColumn::PasswordModifiedDate)
}
"entryuuid" | "uuid" => UserFieldType::PrimaryField(UserColumn::Uuid), "entryuuid" | "uuid" => UserFieldType::PrimaryField(UserColumn::Uuid),
_ => schema _ => schema
.get_schema() .get_schema()
@@ -263,7 +257,6 @@ pub enum GroupFieldType {
GroupId, GroupId,
DisplayName, DisplayName,
CreationDate, CreationDate,
ModifiedDate,
ObjectClass, ObjectClass,
Dn, Dn,
// Like Dn, but returned as part of the attributes. // Like Dn, but returned as part of the attributes.
@@ -279,8 +272,9 @@ pub fn map_group_field(field: &AttributeName, schema: &PublicSchema) -> GroupFie
"entrydn" => GroupFieldType::EntryDn, "entrydn" => GroupFieldType::EntryDn,
"objectclass" => GroupFieldType::ObjectClass, "objectclass" => GroupFieldType::ObjectClass,
"cn" | "displayname" | "uid" | "display_name" | "id" => GroupFieldType::DisplayName, "cn" | "displayname" | "uid" | "display_name" | "id" => GroupFieldType::DisplayName,
"creationdate" | "createtimestamp" | "creation_date" => GroupFieldType::CreationDate, "creationdate" | "createtimestamp" | "modifytimestamp" | "creation_date" => {
"modifytimestamp" | "modifydate" | "modified_date" => GroupFieldType::ModifiedDate, GroupFieldType::CreationDate
}
"member" | "uniquemember" => GroupFieldType::Member, "member" | "uniquemember" => GroupFieldType::Member,
"entryuuid" | "uuid" => GroupFieldType::Uuid, "entryuuid" | "uuid" => GroupFieldType::Uuid,
"group_id" | "groupid" => GroupFieldType::GroupId, "group_id" | "groupid" => GroupFieldType::GroupId,
-2
View File
@@ -154,7 +154,6 @@ mod tests {
uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
users: Vec::new(), users: Vec::new(),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
mock.expect_delete_group() mock.expect_delete_group()
@@ -285,7 +284,6 @@ mod tests {
uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
users: Vec::new(), users: Vec::new(),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
mock.expect_delete_group() mock.expect_delete_group()
-1
View File
@@ -398,7 +398,6 @@ pub mod tests {
creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(), creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}); });
Ok(set) Ok(set)
}); });
-1
View File
@@ -158,7 +158,6 @@ mod tests {
creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(), creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}); });
} }
Ok(g) Ok(g)
-2
View File
@@ -263,7 +263,6 @@ pub mod tests {
creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(), creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}); });
Ok(set) Ok(set)
}); });
@@ -521,7 +520,6 @@ pub mod tests {
creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(), creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}); });
mock.expect_get_user_groups() mock.expect_get_user_groups()
.with(eq(UserId::new("bob"))) .with(eq(UserId::new("bob")))
+14 -36
View File
@@ -236,7 +236,6 @@ pub fn make_ldap_subschema_entry(schema: PublicSchema) -> LdapOp {
vals: { vals: {
let hardcoded_attributes = [ let hardcoded_attributes = [
b"( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' 'user_id' ) DESC 'RFC4519: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} SINGLE-VALUE NO-USER-MODIFICATION )".to_vec(), b"( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' 'user_id' ) DESC 'RFC4519: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} SINGLE-VALUE NO-USER-MODIFICATION )".to_vec(),
b"( 1.2.840.113556.1.2.102 NAME 'memberOf' DESC 'Group that the entry belongs to' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'iPlanet Delegated Administrator' )".to_vec(),
b"( 1.3.6.1.1.16.4 NAME ( 'entryUUID' 'uuid' ) DESC 'UUID of the entry' EQUALITY UUIDMatch ORDERING UUIDOrderingMatch SYNTAX 1.3.6.1.1.16.1 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )".to_vec(), b"( 1.3.6.1.1.16.4 NAME ( 'entryUUID' 'uuid' ) DESC 'UUID of the entry' EQUALITY UUIDMatch ORDERING UUIDOrderingMatch SYNTAX 1.3.6.1.1.16.1 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )".to_vec(),
b"( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' DESC 'RFC4512: LDAP syntaxes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )".to_vec(), b"( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' DESC 'RFC4512: LDAP syntaxes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )".to_vec(),
b"( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )".to_vec(), b"( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )".to_vec(),
@@ -256,10 +255,11 @@ pub fn make_ldap_subschema_entry(schema: PublicSchema) -> LdapOp {
b"( 10.2 NAME 'JpegPhoto' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )".to_vec(), b"( 10.2 NAME 'JpegPhoto' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )".to_vec(),
b"( 10.3 NAME 'DateTime' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )".to_vec(), b"( 10.3 NAME 'DateTime' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )".to_vec(),
]; ];
let num_hardcoded_attributes = hardcoded_attributes.len();
hardcoded_attributes.into_iter().chain( hardcoded_attributes.into_iter().chain(
ldap_schema_description ldap_schema_description
.formatted_attribute_list( .formatted_attribute_list(
4, // The number of hardcoded attributes starting with "10." (LLDAP custom range) num_hardcoded_attributes,
vec!["creation_date", "display_name", "last_name", "user_id", "uuid"] vec!["creation_date", "display_name", "last_name", "user_id", "uuid"]
) )
).collect() ).collect()
@@ -291,12 +291,12 @@ pub fn make_ldap_subschema_entry(schema: PublicSchema) -> LdapOp {
} }
pub(crate) fn is_root_dse_request(request: &LdapSearchRequest) -> bool { pub(crate) fn is_root_dse_request(request: &LdapSearchRequest) -> bool {
if request.base.is_empty() if request.base.is_empty() && request.scope == LdapSearchScope::Base {
&& request.scope == LdapSearchScope::Base if let LdapFilter::Present(attribute) = &request.filter {
&& let LdapFilter::Present(attribute) = &request.filter if attribute.eq_ignore_ascii_case("objectclass") {
&& attribute.eq_ignore_ascii_case("objectclass") return true;
{ }
return true; }
} }
false false
} }
@@ -613,7 +613,6 @@ mod tests {
atype: "attributeTypes".to_owned(), atype: "attributeTypes".to_owned(),
vals: vec![ vals: vec![
b"( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' 'user_id' ) DESC 'RFC4519: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} SINGLE-VALUE NO-USER-MODIFICATION )".to_vec(), b"( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' 'user_id' ) DESC 'RFC4519: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} SINGLE-VALUE NO-USER-MODIFICATION )".to_vec(),
b"( 1.2.840.113556.1.2.102 NAME 'memberOf' DESC 'Group that the entry belongs to' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'iPlanet Delegated Administrator' )".to_vec(),
b"( 1.3.6.1.1.16.4 NAME ( 'entryUUID' 'uuid' ) DESC 'UUID of the entry' EQUALITY UUIDMatch ORDERING UUIDOrderingMatch SYNTAX 1.3.6.1.1.16.1 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )".to_vec(), b"( 1.3.6.1.1.16.4 NAME ( 'entryUUID' 'uuid' ) DESC 'UUID of the entry' EQUALITY UUIDMatch ORDERING UUIDOrderingMatch SYNTAX 1.3.6.1.1.16.1 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )".to_vec(),
b"( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' DESC 'RFC4512: LDAP syntaxes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )".to_vec(), b"( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' DESC 'RFC4512: LDAP syntaxes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )".to_vec(),
b"( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )".to_vec(), b"( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )".to_vec(),
@@ -632,15 +631,12 @@ mod tests {
b"( 10.1 NAME 'Integer' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )".to_vec(), b"( 10.1 NAME 'Integer' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )".to_vec(),
b"( 10.2 NAME 'JpegPhoto' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )".to_vec(), b"( 10.2 NAME 'JpegPhoto' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )".to_vec(),
b"( 10.3 NAME 'DateTime' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )".to_vec(), b"( 10.3 NAME 'DateTime' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )".to_vec(),
b"( 10.4 NAME 'avatar' DESC 'LLDAP: builtin attribute' SUP JpegPhoto )".to_vec(), b"( 10.19 NAME 'avatar' DESC 'LLDAP: builtin attribute' SUP JpegPhoto )".to_vec(),
b"( 10.5 NAME 'first_name' DESC 'LLDAP: builtin attribute' SUP String )" b"( 10.20 NAME 'first_name' DESC 'LLDAP: builtin attribute' SUP String )"
.to_vec(), .to_vec(),
b"( 10.6 NAME 'mail' DESC 'LLDAP: builtin attribute' SUP String )".to_vec(), b"( 10.21 NAME 'mail' DESC 'LLDAP: builtin attribute' SUP String )".to_vec(),
b"( 10.7 NAME 'modified_date' DESC 'LLDAP: builtin attribute' SUP DateTime )".to_vec(), b"( 10.22 NAME 'group_id' DESC 'LLDAP: builtin attribute' SUP Integer )"
b"( 10.8 NAME 'password_modified_date' DESC 'LLDAP: builtin attribute' SUP DateTime )".to_vec(),
b"( 10.9 NAME 'group_id' DESC 'LLDAP: builtin attribute' SUP Integer )"
.to_vec(), .to_vec(),
b"( 10.10 NAME 'modified_date' DESC 'LLDAP: builtin attribute' SUP DateTime )".to_vec(),
] ]
} }
); );
@@ -649,8 +645,8 @@ mod tests {
LdapPartialAttribute { LdapPartialAttribute {
atype: "objectClasses".to_owned(), atype: "objectClasses".to_owned(),
vals: vec![ vals: vec![
b"( 3.0 NAME ( 'inetOrgPerson' 'posixAccount' 'mailAccount' 'person' 'customUserClass' ) DESC 'LLDAP builtin: a person' STRUCTURAL MUST ( mail $ user_id ) MAY ( avatar $ creation_date $ display_name $ first_name $ last_name $ modified_date $ password_modified_date $ uuid ) )".to_vec(), b"( 3.0 NAME ( 'inetOrgPerson' 'posixAccount' 'mailAccount' 'person' 'customUserClass' ) DESC 'LLDAP builtin: a person' STRUCTURAL MUST ( mail $ user_id ) MAY ( avatar $ creation_date $ display_name $ first_name $ last_name $ uuid ) )".to_vec(),
b"( 3.1 NAME ( 'groupOfUniqueNames' 'groupOfNames' ) DESC 'LLDAP builtin: a group' STRUCTURAL MUST ( display_name ) MAY ( creation_date $ group_id $ modified_date $ uuid ) )".to_vec(), b"( 3.1 NAME ( 'groupOfUniqueNames' 'groupOfNames' ) DESC 'LLDAP builtin: a group' STRUCTURAL MUST ( display_name ) MAY ( creation_date $ group_id $ uuid ) )".to_vec(),
] ]
} }
); );
@@ -738,7 +734,6 @@ mod tests {
creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(), creation_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"), uuid: uuid!("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]), }]),
}]) }])
}); });
@@ -844,14 +839,6 @@ mod tests {
.with_ymd_and_hms(2014, 7, 8, 9, 10, 11) .with_ymd_and_hms(2014, 7, 8, 9, 10, 11)
.unwrap() .unwrap()
.naive_utc(), .naive_utc(),
modified_date: Utc
.with_ymd_and_hms(2014, 7, 8, 9, 10, 11)
.unwrap()
.naive_utc(),
password_modified_date: Utc
.with_ymd_and_hms(2014, 7, 8, 9, 10, 11)
.unwrap()
.naive_utc(),
}, },
groups: None, groups: None,
}, },
@@ -986,7 +973,6 @@ mod tests {
users: vec![UserId::new("bob"), UserId::new("john")], users: vec![UserId::new("bob"), UserId::new("john")],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}, },
Group { Group {
id: GroupId(3), id: GroupId(3),
@@ -995,7 +981,6 @@ mod tests {
users: vec![UserId::new("john")], users: vec![UserId::new("john")],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}, },
]) ])
}); });
@@ -1086,7 +1071,6 @@ mod tests {
users: vec![UserId::new("bob"), UserId::new("john")], users: vec![UserId::new("bob"), UserId::new("john")],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
let ldap_handler = setup_bound_admin_handler(mock).await; let ldap_handler = setup_bound_admin_handler(mock).await;
@@ -1137,7 +1121,6 @@ mod tests {
users: vec![], users: vec![],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
let ldap_handler = setup_bound_admin_handler(mock).await; let ldap_handler = setup_bound_admin_handler(mock).await;
@@ -1209,7 +1192,6 @@ mod tests {
users: vec![], users: vec![],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
let ldap_handler = setup_bound_admin_handler(mock).await; let ldap_handler = setup_bound_admin_handler(mock).await;
@@ -1261,7 +1243,6 @@ mod tests {
name: "Attr".into(), name: "Attr".into(),
value: "TEST".to_string().into(), value: "TEST".to_string().into(),
}], }],
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
mock.expect_get_schema().returning(|| { mock.expect_get_schema().returning(|| {
@@ -1719,7 +1700,6 @@ mod tests {
users: vec![UserId::new("bob"), UserId::new("john")], users: vec![UserId::new("bob"), UserId::new("john")],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
let ldap_handler = setup_bound_admin_handler(mock).await; let ldap_handler = setup_bound_admin_handler(mock).await;
@@ -1804,7 +1784,6 @@ mod tests {
users: vec![UserId::new("bob"), UserId::new("john")], users: vec![UserId::new("bob"), UserId::new("john")],
uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"), uuid: uuid!("04ac75e0-2900-3e21-926c-2f732c26b3fc"),
attributes: Vec::new(), attributes: Vec::new(),
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
let ldap_handler = setup_bound_admin_handler(mock).await; let ldap_handler = setup_bound_admin_handler(mock).await;
@@ -2065,7 +2044,6 @@ mod tests {
name: "club_name".into(), name: "club_name".into(),
value: "Breakfast Club".to_string().into(), value: "Breakfast Club".to_string().into(),
}], }],
modified_date: chrono::Utc.timestamp_opt(42, 42).unwrap().naive_utc(),
}]) }])
}); });
mock.expect_get_schema().returning(|| { mock.expect_get_schema().returning(|| {
@@ -206,7 +206,6 @@ impl GroupBackendHandler for SqlBackendHandler {
lowercase_display_name: Set(lower_display_name), lowercase_display_name: Set(lower_display_name),
creation_date: Set(now), creation_date: Set(now),
uuid: Set(uuid), uuid: Set(uuid),
modified_date: Set(now),
..Default::default() ..Default::default()
}; };
Ok(self Ok(self
@@ -269,12 +268,10 @@ impl SqlBackendHandler {
.display_name .display_name
.as_ref() .as_ref()
.map(|s| s.as_str().to_lowercase()); .map(|s| s.as_str().to_lowercase());
let now = chrono::Utc::now().naive_utc();
let update_group = model::groups::ActiveModel { let update_group = model::groups::ActiveModel {
group_id: Set(request.group_id), group_id: Set(request.group_id),
display_name: request.display_name.map(Set).unwrap_or_default(), display_name: request.display_name.map(Set).unwrap_or_default(),
lowercase_display_name: lower_display_name.map(Set).unwrap_or_default(), lowercase_display_name: lower_display_name.map(Set).unwrap_or_default(),
modified_date: Set(now),
..Default::default() ..Default::default()
}; };
update_group.update(transaction).await?; update_group.update(transaction).await?;
@@ -27,8 +27,6 @@ pub enum Users {
TotpSecret, TotpSecret,
MfaType, MfaType,
Uuid, Uuid,
ModifiedDate,
PasswordModifiedDate,
} }
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)] #[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
@@ -39,7 +37,6 @@ pub(crate) enum Groups {
LowercaseDisplayName, LowercaseDisplayName,
CreationDate, CreationDate,
Uuid, Uuid,
ModifiedDate,
} }
#[derive(DeriveIden, Clone, Copy)] #[derive(DeriveIden, Clone, Copy)]
@@ -1115,77 +1112,6 @@ async fn migrate_to_v10(transaction: DatabaseTransaction) -> Result<DatabaseTran
Ok(transaction) Ok(transaction)
} }
async fn migrate_to_v11(transaction: DatabaseTransaction) -> Result<DatabaseTransaction, DbErr> {
let builder = transaction.get_database_backend();
// Add modified_date to users table
transaction
.execute(
builder.build(
Table::alter().table(Users::Table).add_column(
ColumnDef::new(Users::ModifiedDate)
.date_time()
.not_null()
.default(chrono::Utc::now().naive_utc()),
),
),
)
.await?;
// Add password_modified_date to users table
transaction
.execute(
builder.build(
Table::alter().table(Users::Table).add_column(
ColumnDef::new(Users::PasswordModifiedDate)
.date_time()
.not_null()
.default(chrono::Utc::now().naive_utc()),
),
),
)
.await?;
// Add modified_date to groups table
transaction
.execute(
builder.build(
Table::alter().table(Groups::Table).add_column(
ColumnDef::new(Groups::ModifiedDate)
.date_time()
.not_null()
.default(chrono::Utc::now().naive_utc()),
),
),
)
.await?;
// Initialize existing users with modified_date and password_modified_date = now
let now = chrono::Utc::now().naive_utc();
transaction
.execute(
builder.build(
Query::update()
.table(Users::Table)
.value(Users::ModifiedDate, now)
.value(Users::PasswordModifiedDate, now),
),
)
.await?;
// Initialize existing groups with modified_date = now
transaction
.execute(
builder.build(
Query::update()
.table(Groups::Table)
.value(Groups::ModifiedDate, now),
),
)
.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) => {
@@ -1216,7 +1142,6 @@ pub(crate) async fn migrate_from_version(
to_sync!(migrate_to_v8), to_sync!(migrate_to_v8),
to_sync!(migrate_to_v9), to_sync!(migrate_to_v9),
to_sync!(migrate_to_v10), to_sync!(migrate_to_v10),
to_sync!(migrate_to_v11),
]; ];
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 {
@@ -197,12 +197,9 @@ impl OpaqueHandler for SqlOpaqueHandler {
let password_file = let password_file =
opaque::server::registration::get_password_file(request.registration_upload); opaque::server::registration::get_password_file(request.registration_upload);
// Set the user password to the new password. // Set the user password to the new password.
let now = chrono::Utc::now().naive_utc();
let user_update = model::users::ActiveModel { let user_update = model::users::ActiveModel {
user_id: ActiveValue::Set(username.clone()), user_id: ActiveValue::Set(username.clone()),
password_hash: ActiveValue::Set(Some(password_file.serialize())), password_hash: ActiveValue::Set(Some(password_file.serialize())),
password_modified_date: ActiveValue::Set(now),
modified_date: ActiveValue::Set(now),
..Default::default() ..Default::default()
}; };
user_update.update(&self.sql_pool).await?; user_update.update(&self.sql_pool).await?;
+1 -1
View File
@@ -9,7 +9,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(11); pub const LAST_SCHEMA_VERSION: SchemaVersion = SchemaVersion(10);
#[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]);
@@ -190,13 +190,11 @@ impl SqlBackendHandler {
request: UpdateUserRequest, request: UpdateUserRequest,
) -> Result<()> { ) -> Result<()> {
let lower_email = request.email.as_ref().map(|s| s.as_str().to_lowercase()); let lower_email = request.email.as_ref().map(|s| s.as_str().to_lowercase());
let now = chrono::Utc::now().naive_utc();
let update_user = model::users::ActiveModel { let update_user = model::users::ActiveModel {
user_id: ActiveValue::Set(request.user_id.clone()), user_id: ActiveValue::Set(request.user_id.clone()),
email: request.email.map(ActiveValue::Set).unwrap_or_default(), email: request.email.map(ActiveValue::Set).unwrap_or_default(),
lowercase_email: lower_email.map(ActiveValue::Set).unwrap_or_default(), lowercase_email: lower_email.map(ActiveValue::Set).unwrap_or_default(),
display_name: to_value(&request.display_name), display_name: to_value(&request.display_name),
modified_date: ActiveValue::Set(now),
..Default::default() ..Default::default()
}; };
let mut update_user_attributes = Vec::new(); let mut update_user_attributes = Vec::new();
@@ -327,8 +325,6 @@ impl UserBackendHandler for SqlBackendHandler {
display_name: to_value(&request.display_name), display_name: to_value(&request.display_name),
creation_date: ActiveValue::Set(now), creation_date: ActiveValue::Set(now),
uuid: ActiveValue::Set(uuid), uuid: ActiveValue::Set(uuid),
modified_date: ActiveValue::Set(now),
password_modified_date: ActiveValue::Set(now),
..Default::default() ..Default::default()
}; };
let mut new_user_attributes = Vec::new(); let mut new_user_attributes = Vec::new();
@@ -395,70 +391,24 @@ impl UserBackendHandler for SqlBackendHandler {
#[instrument(skip_all, level = "debug", err, fields(user_id = ?user_id.as_str(), group_id))] #[instrument(skip_all, level = "debug", err, fields(user_id = ?user_id.as_str(), group_id))]
async fn add_user_to_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> { async fn add_user_to_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> {
let user_id_owned = user_id.clone(); let new_membership = model::memberships::ActiveModel {
self.sql_pool user_id: ActiveValue::Set(user_id.clone()),
.transaction::<_, _, sea_orm::DbErr>(|transaction| { group_id: ActiveValue::Set(group_id),
Box::pin(async move { };
let new_membership = model::memberships::ActiveModel { new_membership.insert(&self.sql_pool).await?;
user_id: ActiveValue::Set(user_id_owned),
group_id: ActiveValue::Set(group_id),
};
new_membership.insert(transaction).await?;
// Update group modification time
let now = chrono::Utc::now().naive_utc();
let update_group = model::groups::ActiveModel {
group_id: Set(group_id),
modified_date: Set(now),
..Default::default()
};
update_group.update(transaction).await?;
Ok(())
})
})
.await?;
Ok(()) Ok(())
} }
#[instrument(skip_all, level = "debug", err, fields(user_id = ?user_id.as_str(), group_id))] #[instrument(skip_all, level = "debug", err, fields(user_id = ?user_id.as_str(), group_id))]
async fn remove_user_from_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> { async fn remove_user_from_group(&self, user_id: &UserId, group_id: GroupId) -> Result<()> {
let user_id_owned = user_id.clone(); let res = model::Membership::delete_by_id((user_id.clone(), group_id))
self.sql_pool .exec(&self.sql_pool)
.transaction::<_, _, sea_orm::DbErr>(|transaction| { .await?;
Box::pin(async move { if res.rows_affected == 0 {
let res = model::Membership::delete_by_id((user_id_owned.clone(), group_id)) return Err(DomainError::EntityNotFound(format!(
.exec(transaction) "No such membership: '{user_id}' -> {group_id:?}"
.await?; )));
if res.rows_affected == 0 { }
return Err(sea_orm::DbErr::Custom(format!(
"No such membership: '{user_id_owned}' -> {group_id:?}"
)));
}
// Update group modification time
let now = chrono::Utc::now().naive_utc();
let update_group = model::groups::ActiveModel {
group_id: Set(group_id),
modified_date: Set(now),
..Default::default()
};
update_group.update(transaction).await?;
Ok(())
})
})
.await
.map_err(|e| match e {
sea_orm::TransactionError::Connection(sea_orm::DbErr::Custom(msg)) => {
DomainError::EntityNotFound(msg)
}
sea_orm::TransactionError::Transaction(sea_orm::DbErr::Custom(msg)) => {
DomainError::EntityNotFound(msg)
}
sea_orm::TransactionError::Connection(e) => DomainError::DatabaseError(e),
sea_orm::TransactionError::Transaction(e) => DomainError::DatabaseError(e),
})?;
Ok(()) Ok(())
} }
} }