mirror of
https://github.com/lldap/lldap.git
synced 2026-07-25 17:58:44 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e248ab3bb | |||
| 5df92fbb16 | |||
| e92088a7aa |
@@ -0,0 +1 @@
|
|||||||
|
* @nitnelave
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
# LLDAP - Light LDAP implementation for authentication
|
|
||||||
|
|
||||||
LLDAP is a lightweight LDAP authentication server written in Rust with a WebAssembly frontend. It provides an opinionated, simplified LDAP interface for authentication and integrates with many popular services.
|
|
||||||
|
|
||||||
**ALWAYS reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.**
|
|
||||||
|
|
||||||
## Working Effectively
|
|
||||||
|
|
||||||
### Bootstrap and Build the Repository
|
|
||||||
- Install dependencies: `sudo apt-get update && sudo apt-get install -y curl gzip binaryen`
|
|
||||||
- Install Rust if not available: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` then `source ~/.cargo/env`
|
|
||||||
- Install wasm-pack for frontend: `cargo install wasm-pack` -- takes 90 seconds. NEVER CANCEL. Set timeout to 180+ seconds.
|
|
||||||
- Build entire workspace: `cargo build --workspace` -- takes 3-4 minutes. NEVER CANCEL. Set timeout to 300+ seconds.
|
|
||||||
- Build release server binary: `cargo build --release -p lldap` -- takes 5-6 minutes. NEVER CANCEL. Set timeout to 420+ seconds.
|
|
||||||
- Build frontend WASM: `./app/build.sh` -- takes 3-4 minutes including wasm-pack installation. NEVER CANCEL. Set timeout to 300+ seconds.
|
|
||||||
|
|
||||||
### Testing and Validation
|
|
||||||
- Run all tests: `cargo test --workspace` -- takes 2-3 minutes. NEVER CANCEL. Set timeout to 240+ seconds.
|
|
||||||
- Check formatting: `cargo fmt --all --check` -- takes <5 seconds.
|
|
||||||
- Run linting: `cargo clippy --tests --all -- -D warnings` -- takes 60-90 seconds. NEVER CANCEL. Set timeout to 120+ seconds.
|
|
||||||
- Export GraphQL schema: `./export_schema.sh` -- takes 70-80 seconds. NEVER CANCEL. Set timeout to 120+ seconds.
|
|
||||||
|
|
||||||
### Running the Application
|
|
||||||
- **ALWAYS run the build steps first before starting the server.**
|
|
||||||
- Start development server: `cargo run -- run --config-file <config_file>`
|
|
||||||
- **CRITICAL**: Server requires a valid configuration file. Use `lldap_config.docker_template.toml` as reference.
|
|
||||||
- **CRITICAL**: Avoid key conflicts by removing existing `server_key*` files when testing with `key_seed` in config.
|
|
||||||
- Server binds to:
|
|
||||||
- LDAP: port 3890 (configurable)
|
|
||||||
- Web interface: port 17170 (configurable)
|
|
||||||
- LDAPS: port 6360 (optional, disabled by default)
|
|
||||||
|
|
||||||
### Manual Validation Requirements
|
|
||||||
- **ALWAYS test both LDAP and web interfaces after making changes.**
|
|
||||||
- Test web interface: `curl -s http://localhost:17170/` should return HTML with "LLDAP Administration" title.
|
|
||||||
- Test GraphQL API: `curl -s -X POST -H "Content-Type: application/json" -d '{"query": "query { __schema { queryType { name } } }"}' http://localhost:17170/api/graphql`
|
|
||||||
- Run healthcheck: `cargo run -- healthcheck --config-file <config_file>` (requires running server)
|
|
||||||
- **ALWAYS ensure server starts without errors and serves the web interface before considering changes complete.**
|
|
||||||
|
|
||||||
## Validation Scenarios
|
|
||||||
|
|
||||||
After making code changes, ALWAYS:
|
|
||||||
1. **Build validation**: Run `cargo build --workspace` to ensure compilation succeeds.
|
|
||||||
2. **Test validation**: Run `cargo test --workspace` to ensure existing functionality works.
|
|
||||||
3. **Lint validation**: Run `cargo clippy --tests --all -- -D warnings` to catch potential issues.
|
|
||||||
4. **Format validation**: Run `cargo fmt --all --check` to ensure code style compliance.
|
|
||||||
5. **Frontend validation**: Run `./app/build.sh` to ensure WASM compilation succeeds.
|
|
||||||
6. **Runtime validation**: Start the server and verify web interface accessibility.
|
|
||||||
7. **Schema validation**: If GraphQL changes made, run `./export_schema.sh` to update schema.
|
|
||||||
|
|
||||||
### Test User Scenarios
|
|
||||||
- **Login flow**: Access web interface at `http://localhost:17170`, attempt login with admin/password (default).
|
|
||||||
- **LDAP binding**: Test LDAP connection on port 3890 with appropriate LDAP tools if available.
|
|
||||||
- **Configuration changes**: Test with different configuration files to validate config parsing.
|
|
||||||
|
|
||||||
## Project Structure and Key Components
|
|
||||||
|
|
||||||
### Backend (Rust)
|
|
||||||
- **Server**: `/server` - Main application binary
|
|
||||||
- **Crates**: `/crates/*` - Modularized components:
|
|
||||||
- `auth` - Authentication and OPAQUE protocol
|
|
||||||
- `domain*` - Domain models and handlers
|
|
||||||
- `ldap` - LDAP protocol implementation
|
|
||||||
- `graphql-server` - GraphQL API server
|
|
||||||
- `sql-backend-handler` - Database operations
|
|
||||||
- `validation` - Input validation utilities
|
|
||||||
|
|
||||||
### Frontend (Rust + WASM)
|
|
||||||
- **App**: `/app` - Yew-based WebAssembly frontend
|
|
||||||
- **Build**: `./app/build.sh` - Compiles Rust to WASM using wasm-pack
|
|
||||||
- **Assets**: `/app/static` - Static web assets
|
|
||||||
|
|
||||||
### Configuration and Deployment
|
|
||||||
- **Config template**: `lldap_config.docker_template.toml` - Reference configuration
|
|
||||||
- **Docker**: `Dockerfile` - Container build definition
|
|
||||||
- **Scripts**:
|
|
||||||
- `prepare-release.sh` - Cross-platform release builds
|
|
||||||
- `export_schema.sh` - GraphQL schema export
|
|
||||||
- `generate_secrets.sh` - Random secret generation
|
|
||||||
- `scripts/bootstrap.sh` - User/group management script
|
|
||||||
|
|
||||||
## Common Development Workflows
|
|
||||||
|
|
||||||
### Making Backend Changes
|
|
||||||
1. Edit Rust code in `/server` or `/crates`
|
|
||||||
2. Run `cargo build --workspace` to test compilation
|
|
||||||
3. Run `cargo test --workspace` to ensure tests pass
|
|
||||||
4. Run `cargo clippy --tests --all -- -D warnings` to check for warnings
|
|
||||||
5. If GraphQL schema affected, run `./export_schema.sh`
|
|
||||||
6. Test by running server and validating functionality
|
|
||||||
|
|
||||||
### Making Frontend Changes
|
|
||||||
1. Edit code in `/app/src`
|
|
||||||
2. Run `./app/build.sh` to rebuild WASM package
|
|
||||||
3. Start server and test web interface functionality
|
|
||||||
4. Verify no JavaScript errors in browser console
|
|
||||||
|
|
||||||
### Adding New Dependencies
|
|
||||||
- Backend: Add to appropriate `Cargo.toml` in `/server` or `/crates/*`
|
|
||||||
- Frontend: Add to `/app/Cargo.toml`
|
|
||||||
- **Always rebuild after dependency changes**
|
|
||||||
|
|
||||||
## CI/CD Integration
|
|
||||||
|
|
||||||
The repository uses GitHub Actions (`.github/workflows/rust.yml`):
|
|
||||||
- **Build job**: Validates workspace compilation
|
|
||||||
- **Test job**: Runs full test suite
|
|
||||||
- **Clippy job**: Linting with warnings as errors
|
|
||||||
- **Format job**: Code formatting validation
|
|
||||||
- **Coverage job**: Code coverage analysis
|
|
||||||
|
|
||||||
**ALWAYS ensure your changes pass all CI checks by running equivalent commands locally.**
|
|
||||||
|
|
||||||
## Timing Expectations and Timeouts
|
|
||||||
|
|
||||||
| Command | Expected Time | Timeout Setting |
|
|
||||||
|---------|---------------|-----------------|
|
|
||||||
| `cargo build --workspace` | 3-4 minutes | 300+ seconds |
|
|
||||||
| `cargo build --release -p lldap` | 5-6 minutes | 420+ seconds |
|
|
||||||
| `cargo test --workspace` | 2-3 minutes | 240+ seconds |
|
|
||||||
| `./app/build.sh` | 3-4 minutes | 300+ seconds |
|
|
||||||
| `cargo clippy --tests --all -- -D warnings` | 60-90 seconds | 120+ seconds |
|
|
||||||
| `./export_schema.sh` | 70-80 seconds | 120+ seconds |
|
|
||||||
| `cargo install wasm-pack` | 90 seconds | 180+ seconds |
|
|
||||||
|
|
||||||
**NEVER CANCEL** any of these commands. Builds may take longer on slower systems.
|
|
||||||
|
|
||||||
## Troubleshooting Common Issues
|
|
||||||
|
|
||||||
### Build Issues
|
|
||||||
- **Missing wasm-pack**: Run `cargo install wasm-pack`
|
|
||||||
- **Missing binaryen**: Run `sudo apt-get install -y binaryen` or disable wasm-opt
|
|
||||||
- **Clippy warnings**: Fix all warnings as they are treated as errors in CI
|
|
||||||
- **GraphQL schema mismatch**: Run `./export_schema.sh` to update schema
|
|
||||||
|
|
||||||
### Runtime Issues
|
|
||||||
- **Key conflicts**: Remove `server_key*` files when using `key_seed` in config
|
|
||||||
- **Port conflicts**: Check if ports 3890/17170 are available
|
|
||||||
- **Database issues**: Ensure database URL in config is valid and accessible
|
|
||||||
- **Asset missing**: Ensure frontend is built with `./app/build.sh`
|
|
||||||
|
|
||||||
### Development Environment
|
|
||||||
- **Rust version**: Use stable Rust toolchain (2024 edition)
|
|
||||||
- **System dependencies**: curl, gzip, build tools
|
|
||||||
- **Database**: SQLite (default), MySQL, or PostgreSQL supported
|
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
Essential configuration parameters:
|
|
||||||
- `ldap_base_dn`: LDAP base DN (e.g., "dc=example,dc=com")
|
|
||||||
- `ldap_user_dn`: Admin user DN
|
|
||||||
- `ldap_user_pass`: Admin password
|
|
||||||
- `jwt_secret`: Secret for JWT tokens (generate with `./generate_secrets.sh`)
|
|
||||||
- `key_seed`: Encryption key seed
|
|
||||||
- `database_url`: Database connection string
|
|
||||||
- `http_port`: Web interface port (default: 17170)
|
|
||||||
- `ldap_port`: LDAP server port (default: 3890)
|
|
||||||
|
|
||||||
**Always use the provided config template as starting point for new configurations.**
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
name: Copilot Setup Steps for LLDAP Development
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Update package list
|
|
||||||
run: sudo apt-get update
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: sudo apt-get install -y curl gzip binaryen build-essential
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
run: |
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
||||||
source ~/.cargo/env
|
|
||||||
echo 'source ~/.cargo/env' >> ~/.bashrc
|
|
||||||
|
|
||||||
- name: Install wasm-pack for frontend builds
|
|
||||||
run: |
|
|
||||||
source ~/.cargo/env
|
|
||||||
cargo install wasm-pack
|
|
||||||
|
|
||||||
- name: Verify installations
|
|
||||||
run: |
|
|
||||||
source ~/.cargo/env
|
|
||||||
rustc --version
|
|
||||||
cargo --version
|
|
||||||
wasm-pack --version
|
|
||||||
+16
-22
@@ -8,7 +8,6 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
MSRV: 1.85.0
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre_job:
|
pre_job:
|
||||||
@@ -35,18 +34,13 @@ 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
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: "${{ env.MSRV }}"
|
|
||||||
- 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 +53,14 @@ 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
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: "${{ env.MSRV }}"
|
|
||||||
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 +70,14 @@ 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
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: "${{ env.MSRV }}"
|
|
||||||
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
|
||||||
|
|||||||
Generated
-1
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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"},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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")))
|
||||||
|
|||||||
+16
-36
@@ -236,7 +236,7 @@ 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.2.840.113556.1.2.102 NAME 'memberOf' DESC 'The groups this user is a member of' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION )".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 +256,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 +292,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 +614,7 @@ 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.2.840.113556.1.2.102 NAME 'memberOf' DESC 'The groups this user is a member of' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION )".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 +633,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.20 NAME 'avatar' DESC 'LLDAP: builtin attribute' SUP JpegPhoto )".to_vec(),
|
||||||
b"( 10.5 NAME 'first_name' DESC 'LLDAP: builtin attribute' SUP String )"
|
b"( 10.21 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.22 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.23 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 +647,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 +736,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 +841,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 +975,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 +983,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 +1073,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 +1123,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 +1194,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 +1245,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 +1702,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 +1786,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 +2046,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?;
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user