mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
2023 orca improve (#1342)
* Improve orca * Add improved data generator, add some basic ipa support * (least) humble brag (ever) on readme
This commit is contained in:
parent
8255c937e5
commit
ec5e7abe8d
2
Makefile
2
Makefile
|
@ -4,8 +4,6 @@ CONTAINER_TOOL_ARGS ?=
|
||||||
IMAGE_ARCH ?= "linux/amd64,linux/arm64"
|
IMAGE_ARCH ?= "linux/amd64,linux/arm64"
|
||||||
CONTAINER_BUILD_ARGS ?=
|
CONTAINER_BUILD_ARGS ?=
|
||||||
MARKDOWN_FORMAT_ARGS ?= --options-line-width=100
|
MARKDOWN_FORMAT_ARGS ?= --options-line-width=100
|
||||||
# Example of using redis with sccache
|
|
||||||
# --build-arg "SCCACHE_REDIS=redis://redis.dev.blackhats.net.au:6379"
|
|
||||||
CONTAINER_TOOL ?= docker
|
CONTAINER_TOOL ?= docker
|
||||||
|
|
||||||
BOOK_VERSION ?= master
|
BOOK_VERSION ?= master
|
||||||
|
|
|
@ -99,7 +99,9 @@ of resource overhead and difficulty for administration.
|
||||||
|
|
||||||
Kanidm aims to have the features richness of FreeIPA, but without the resource and administration
|
Kanidm aims to have the features richness of FreeIPA, but without the resource and administration
|
||||||
overheads. If you want a complete IDM package, but in a lighter footprint and easier to manage, then
|
overheads. If you want a complete IDM package, but in a lighter footprint and easier to manage, then
|
||||||
Kanidm is probably for you.
|
Kanidm is probably for you. In testing with 3000 users + 1500 groups, Kanidm is 3 times faster
|
||||||
|
for search operations and 5 times faster for modification and addition of entries (your results may
|
||||||
|
differ however, but generally Kanidm is much faster than FreeIPA).
|
||||||
|
|
||||||
## Developer Getting Started
|
## Developer Getting Started
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ RUN zypper install -y \
|
||||||
pam-devel \
|
pam-devel \
|
||||||
libudev-devel \
|
libudev-devel \
|
||||||
sqlite3-devel \
|
sqlite3-devel \
|
||||||
sccache \
|
|
||||||
rsync
|
rsync
|
||||||
RUN zypper clean -a
|
RUN zypper clean -a
|
||||||
RUN rustup default stable
|
RUN rustup default stable
|
||||||
|
@ -34,17 +33,6 @@ RUN echo Features $KANIDM_FEATURES
|
||||||
ENV CARGO_HOME=/scratch/.cargo
|
ENV CARGO_HOME=/scratch/.cargo
|
||||||
ENV RUSTFLAGS="-Clinker=clang -Clink-arg=-fuse-ld=/usr/bin/ld.lld"
|
ENV RUSTFLAGS="-Clinker=clang -Clink-arg=-fuse-ld=/usr/bin/ld.lld"
|
||||||
|
|
||||||
# set up sccache if you've done the thing
|
|
||||||
RUN if [ "${SCCACHE_REDIS}" != "" ]; \
|
|
||||||
then \
|
|
||||||
export CARGO_INCREMENTAL=false && \
|
|
||||||
export CC="/usr/bin/sccache /usr/bin/clang" && \
|
|
||||||
export RUSTC_WRAPPER=sccache && \
|
|
||||||
sccache --start-server; \
|
|
||||||
else \
|
|
||||||
export CC="/usr/bin/clang"; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
WORKDIR /usr/src/kanidm/
|
WORKDIR /usr/src/kanidm/
|
||||||
# build the CLI
|
# build the CLI
|
||||||
RUN if [ -z "${KANIDM_FEATURES}" ]; then \
|
RUN if [ -z "${KANIDM_FEATURES}" ]; then \
|
||||||
|
@ -65,8 +53,6 @@ else \
|
||||||
--release; \
|
--release; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RUN if [ "${SCCACHE_REDIS}" != "" ]; then sccache -s; fi
|
|
||||||
|
|
||||||
RUN ls -al /usr/src/kanidm/target/release
|
RUN ls -al /usr/src/kanidm/target/release
|
||||||
|
|
||||||
# == Construct the tools container
|
# == Construct the tools container
|
||||||
|
|
|
@ -13,7 +13,6 @@ RUN zypper install -y \
|
||||||
make automake autoconf \
|
make automake autoconf \
|
||||||
libopenssl-devel pam-devel \
|
libopenssl-devel pam-devel \
|
||||||
sqlite3-devel \
|
sqlite3-devel \
|
||||||
sccache \
|
|
||||||
gcc \
|
gcc \
|
||||||
rsync \
|
rsync \
|
||||||
findutils \
|
findutils \
|
||||||
|
@ -23,7 +22,6 @@ RUN rustup default stable
|
||||||
|
|
||||||
COPY . /usr/src/kanidm
|
COPY . /usr/src/kanidm
|
||||||
|
|
||||||
ARG SCCACHE_REDIS=""
|
|
||||||
ARG KANIDM_FEATURES
|
ARG KANIDM_FEATURES
|
||||||
ARG KANIDM_BUILD_PROFILE="container_generic"
|
ARG KANIDM_BUILD_PROFILE="container_generic"
|
||||||
ARG KANIDM_BUILD_OPTIONS=""
|
ARG KANIDM_BUILD_OPTIONS=""
|
||||||
|
@ -32,21 +30,14 @@ RUN mkdir /scratch
|
||||||
RUN echo $KANIDM_BUILD_PROFILE
|
RUN echo $KANIDM_BUILD_PROFILE
|
||||||
RUN echo $KANIDM_FEATURES
|
RUN echo $KANIDM_FEATURES
|
||||||
|
|
||||||
|
|
||||||
ENV CARGO_HOME=/scratch/.cargo
|
ENV CARGO_HOME=/scratch/.cargo
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
|
|
||||||
# WORKDIR /usr/src/kanidm/kanidmd_web_ui
|
WORKDIR /usr/src/kanidm/kanidmd_web_ui
|
||||||
# # This can't be used in the wasm build for now.
|
# # This can't be used in the wasm build for now.
|
||||||
# # ENV RUSTFLAGS="-Clinker=clang"
|
# # ENV RUSTFLAGS="-Clinker=clang"
|
||||||
# RUN if [ "${SCCACHE_REDIS}" != "" ]; \
|
RUN ./build_wasm.sh
|
||||||
# then \
|
|
||||||
# export CARGO_INCREMENTAL=false && \
|
|
||||||
# export RUSTC_WRAPPER=sccache && \
|
|
||||||
# sccache --start-server; \
|
|
||||||
# fi && \
|
|
||||||
# ./build_wasm.sh
|
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
|
|
||||||
|
@ -57,15 +48,7 @@ ENV KANIDM_BUILD_PROFILE="${KANIDM_BUILD_PROFILE}"
|
||||||
ENV RUSTFLAGS="-Clinker=clang -Clink-arg=-fuse-ld=/usr/bin/ld.lld"
|
ENV RUSTFLAGS="-Clinker=clang -Clink-arg=-fuse-ld=/usr/bin/ld.lld"
|
||||||
|
|
||||||
# Exports don't persist through RUN statements.
|
# Exports don't persist through RUN statements.
|
||||||
RUN if [ "${SCCACHE_REDIS}" != "" ]; \
|
RUN export CC="/usr/bin/clang"; \
|
||||||
then \
|
|
||||||
export CARGO_INCREMENTAL=false && \
|
|
||||||
export CC="/usr/bin/sccache /usr/bin/clang" && \
|
|
||||||
export RUSTC_WRAPPER=sccache && \
|
|
||||||
sccache --start-server; \
|
|
||||||
else \
|
|
||||||
export CC="/usr/bin/clang"; \
|
|
||||||
fi && \
|
|
||||||
if [ -z "${KANIDM_FEATURES}" ]; then \
|
if [ -z "${KANIDM_FEATURES}" ]; then \
|
||||||
cargo build -p daemon ${KANIDM_BUILD_OPTIONS} \
|
cargo build -p daemon ${KANIDM_BUILD_OPTIONS} \
|
||||||
--target-dir="/usr/src/kanidm/target/" \
|
--target-dir="/usr/src/kanidm/target/" \
|
||||||
|
@ -75,8 +58,7 @@ else \
|
||||||
--target-dir="/usr/src/kanidm/target/" \
|
--target-dir="/usr/src/kanidm/target/" \
|
||||||
--features="${KANIDM_FEATURES}" \
|
--features="${KANIDM_FEATURES}" \
|
||||||
--release; \
|
--release; \
|
||||||
fi && \
|
fi
|
||||||
if [ "${SCCACHE_REDIS}" != "" ]; then sccache -s; fi
|
|
||||||
|
|
||||||
RUN ls -al /usr/src/kanidm/target/release
|
RUN ls -al /usr/src/kanidm/target/release
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@ impl Account {
|
||||||
format!("uid={},ou=people,{}", self.name.as_str(), basedn)
|
format!("uid={},ou=people,{}", self.name.as_str(), basedn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ipa_ldap_dn(&self, basedn: &str) -> String {
|
||||||
|
format!("uid={},cn=users,cn=accounts,{}", self.name.as_str(), basedn)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate(uuid: Uuid) -> Self {
|
pub fn generate(uuid: Uuid) -> Self {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let id: u64 = rng.gen();
|
let id: u64 = rng.gen();
|
||||||
|
@ -73,6 +77,10 @@ impl Group {
|
||||||
format!("cn={},ou=groups,{}", self.name.as_str(), basedn)
|
format!("cn={},ou=groups,{}", self.name.as_str(), basedn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ipa_ldap_dn(&self, basedn: &str) -> String {
|
||||||
|
format!("cn={},cn=groups,cn=accounts,{}", self.name.as_str(), basedn)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate(uuid: Uuid, members: Vec<Uuid>) -> Self {
|
pub fn generate(uuid: Uuid, members: Vec<Uuid>) -> Self {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
@ -115,6 +123,13 @@ impl Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ipa_ldap_dn(&self, basedn: &str) -> String {
|
||||||
|
match self {
|
||||||
|
Entity::Account(a) => a.get_ipa_ldap_dn(basedn),
|
||||||
|
Entity::Group(g) => g.get_ipa_ldap_dn(basedn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_entity_type(&self) -> EntityType {
|
pub fn get_entity_type(&self) -> EntityType {
|
||||||
match self {
|
match self {
|
||||||
Entity::Account(a) => EntityType::Account(a.uuid),
|
Entity::Account(a) => EntityType::Account(a.uuid),
|
||||||
|
|
163
orca/src/generate.rs
Normal file
163
orca/src/generate.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::time::Duration;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use crate::data::*;
|
||||||
|
|
||||||
|
const N_USERS: usize = 3000;
|
||||||
|
const N_GROUPS: usize = 1500;
|
||||||
|
const N_MEMBERSHIPS: usize = 10;
|
||||||
|
const N_NEST: usize = 4;
|
||||||
|
|
||||||
|
pub(crate) fn doit(output: &Path) -> () {
|
||||||
|
info!(
|
||||||
|
"Performing data generation into {}",
|
||||||
|
output.to_str().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
if N_MEMBERSHIPS >= N_GROUPS {
|
||||||
|
error!("Too many memberships per group. Memberships must be less that n-groups");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open before we start so we have it ready to go.
|
||||||
|
let out_file = match File::create(output) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to open {} - {:?}", output.to_str().unwrap(), e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Number of users
|
||||||
|
let accounts: Vec<_> = (0..N_USERS)
|
||||||
|
.map(|i| Account {
|
||||||
|
name: format!("testuser{}", i),
|
||||||
|
display_name: format!("Test User {}", i),
|
||||||
|
password: readable_password_from_random(),
|
||||||
|
uuid: Uuid::new_v4(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Number of groups.
|
||||||
|
let mut groups: Vec<_> = (0..N_GROUPS)
|
||||||
|
.map(|i| Group {
|
||||||
|
name: format!("testgroup{}", i),
|
||||||
|
uuid: Uuid::new_v4(),
|
||||||
|
members: Vec::new(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Should groups be randomly nested?
|
||||||
|
// The way this is done is we split the array based on nest level. If it's 1, we split
|
||||||
|
// in 2, 2 we split in 3 and so on.
|
||||||
|
if N_NEST > 0 {
|
||||||
|
debug!("Nesting Groups");
|
||||||
|
|
||||||
|
let chunk_size = N_GROUPS / (N_NEST + 1);
|
||||||
|
if chunk_size == 0 {
|
||||||
|
error!("Unable to chunk groups, need (N_GROUPS / (N_NEST + 1)) > 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut chunk_iter = groups.chunks_mut(chunk_size);
|
||||||
|
// Can't fail due to above checks.
|
||||||
|
let mut p_chunk = chunk_iter.next().unwrap();
|
||||||
|
while let Some(w_chunk) = chunk_iter.next() {
|
||||||
|
// add items from work chunk to parent chunk
|
||||||
|
p_chunk
|
||||||
|
.iter_mut()
|
||||||
|
.zip(w_chunk.iter())
|
||||||
|
.for_each(|(p, w): (&mut _, &_)| p.members.push(w.uuid));
|
||||||
|
|
||||||
|
// swap w_chunk to p_chunk
|
||||||
|
p_chunk = w_chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of memberships per user.
|
||||||
|
// We use rand for this to sample random numbers of
|
||||||
|
for acc in accounts.iter() {
|
||||||
|
// Sample randomly.
|
||||||
|
for idx in rand::seq::index::sample(&mut rng, N_GROUPS, N_MEMBERSHIPS).iter() {
|
||||||
|
groups[idx].members.push(acc.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build from the generated data above.
|
||||||
|
let all_entities: HashMap<Uuid, Entity> = accounts
|
||||||
|
.into_iter()
|
||||||
|
.map(|acc| (acc.uuid, Entity::Account(acc)))
|
||||||
|
.chain(groups.into_iter().map(|grp| (grp.uuid, Entity::Group(grp))))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Define the entries that should exist "at the start of the test". For now, we just
|
||||||
|
// create everything. Maybe when we start to add mod tests we need to retain a pool
|
||||||
|
// of things to retain here for those ops.
|
||||||
|
let precreate: HashSet<_> = all_entities.keys().copied().collect();
|
||||||
|
|
||||||
|
// The set of accounts in all_entities.
|
||||||
|
let accounts: HashSet<Uuid> = all_entities
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(uuid, ent)| match ent {
|
||||||
|
Entity::Account(_) => Some(*uuid),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// This defines a map of "entity" to "what can it manipulate". This
|
||||||
|
// is used to create access controls in some cases for mod tests.
|
||||||
|
//
|
||||||
|
// For example, if we have user with uuid X and it changes Group with
|
||||||
|
// uuid Y, then we need to ensure that X has group-mod permissions over
|
||||||
|
// Y in some capacity.
|
||||||
|
let access: HashMap<Uuid, Vec<EntityType>> = HashMap::new();
|
||||||
|
|
||||||
|
// The set of operations to simulate. We pre-calc these so tests can randomly
|
||||||
|
// sample and perform the searches as needed.
|
||||||
|
|
||||||
|
// We don't have original times, so we can fudge these.
|
||||||
|
let orig_etime = Duration::from_secs(1);
|
||||||
|
let rtime = Duration::from_secs(1);
|
||||||
|
// Needed for random sampling.
|
||||||
|
let all_ids: Vec<_> = all_entities.keys().copied().collect();
|
||||||
|
let all_ids_len = all_ids.len();
|
||||||
|
|
||||||
|
let connections: Vec<_> = (0..all_ids_len)
|
||||||
|
.map(|id| {
|
||||||
|
// Could be rand?
|
||||||
|
let n_search = 1;
|
||||||
|
|
||||||
|
let mut search_ids = Vec::new();
|
||||||
|
for idx in rand::seq::index::sample(&mut rng, all_ids_len, n_search).iter() {
|
||||||
|
search_ids.push(all_ids[idx]);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
Conn {
|
||||||
|
id: id as i32,
|
||||||
|
ops: vec![Op {
|
||||||
|
orig_etime,
|
||||||
|
rtime,
|
||||||
|
op_type: OpType::Search(search_ids),
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let td = TestData {
|
||||||
|
all_entities,
|
||||||
|
access,
|
||||||
|
accounts,
|
||||||
|
precreate,
|
||||||
|
connections,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = serde_json::to_writer_pretty(out_file, &td) {
|
||||||
|
error!("Writing to file -> {:?}", e);
|
||||||
|
};
|
||||||
|
}
|
295
orca/src/ipa.rs
Normal file
295
orca/src/ipa.rs
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
use ldap3_proto::proto::*;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::data::*;
|
||||||
|
use crate::ldap::{LdapClient, LdapSchema};
|
||||||
|
use crate::profile::IpaConfig;
|
||||||
|
use crate::{TargetServer, TargetServerBuilder};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IpaServer {
|
||||||
|
ldap: LdapClient,
|
||||||
|
realm: String,
|
||||||
|
admin_pw: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IpaServer {
|
||||||
|
fn construct(uri: String, realm: String, admin_pw: String) -> Result<Self, ()> {
|
||||||
|
// explode the realm to basedn.
|
||||||
|
// dev.kanidm.com
|
||||||
|
// dc=dev,dc=kanidm,dc=com
|
||||||
|
let basedn = format!("dc={}", realm.replace('.', ",dc="));
|
||||||
|
|
||||||
|
let ldap = LdapClient::new(uri, basedn, LdapSchema::Rfc2307bis)?;
|
||||||
|
|
||||||
|
Ok(IpaServer {
|
||||||
|
ldap,
|
||||||
|
realm,
|
||||||
|
admin_pw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(uri: String, realm: String, admin_pw: String) -> Result<TargetServer, ()> {
|
||||||
|
Self::construct(uri, realm, admin_pw).map(TargetServer::Ipa)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::new_ret_no_self)]
|
||||||
|
pub fn new(lconfig: &IpaConfig) -> Result<TargetServer, ()> {
|
||||||
|
Self::construct(
|
||||||
|
lconfig.uri.clone(),
|
||||||
|
lconfig.realm.clone(),
|
||||||
|
lconfig.admin_pw.clone(),
|
||||||
|
)
|
||||||
|
.map(TargetServer::Ipa)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn info(&self) -> String {
|
||||||
|
format!("Ipa Server Connection: {} @ {}", self.realm, self.ldap.uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder(&self) -> TargetServerBuilder {
|
||||||
|
TargetServerBuilder::Ipa(
|
||||||
|
self.ldap.uri.clone(),
|
||||||
|
self.realm.clone(),
|
||||||
|
self.admin_pw.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn open_admin_connection(&self) -> Result<(), ()> {
|
||||||
|
self.ldap.open_ipa_admin_connection(&self.admin_pw).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_admin_delete_uuids(&self, _targets: &[Uuid]) -> Result<(), ()> {
|
||||||
|
// todo!();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_admin_precreate_entities(
|
||||||
|
&self,
|
||||||
|
targets: &HashSet<Uuid>,
|
||||||
|
all_entities: &HashMap<Uuid, Entity>,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
for u in targets {
|
||||||
|
let e = all_entities.get(u).unwrap();
|
||||||
|
// does it already exist?
|
||||||
|
let res = self
|
||||||
|
.ldap
|
||||||
|
.search(LdapFilter::Equality(
|
||||||
|
"cn".to_string(),
|
||||||
|
e.get_name().to_string(),
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !res.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dn = e.get_ipa_ldap_dn(&self.ldap.basedn);
|
||||||
|
match e {
|
||||||
|
Entity::Account(a) => {
|
||||||
|
let account = LdapAddRequest {
|
||||||
|
dn,
|
||||||
|
attributes: vec![
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "objectClass".to_string(),
|
||||||
|
vals: vec![
|
||||||
|
"ipaobject".as_bytes().into(),
|
||||||
|
"person".as_bytes().into(),
|
||||||
|
"top".as_bytes().into(),
|
||||||
|
"ipasshuser".as_bytes().into(),
|
||||||
|
"inetorgperson".as_bytes().into(),
|
||||||
|
"organizationalperson".as_bytes().into(),
|
||||||
|
"krbticketpolicyaux".as_bytes().into(),
|
||||||
|
"krbprincipalaux".as_bytes().into(),
|
||||||
|
"inetuser".as_bytes().into(),
|
||||||
|
"posixaccount".as_bytes().into(),
|
||||||
|
"meporiginentry".as_bytes().into(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "ipauniqueid".to_string(),
|
||||||
|
vals: vec!["autogenerate".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "uid".to_string(),
|
||||||
|
vals: vec![a.name.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "cn".to_string(),
|
||||||
|
vals: vec![a.name.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "givenName".to_string(),
|
||||||
|
vals: vec![a.name.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "sn".to_string(),
|
||||||
|
vals: vec![a.name.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "displayName".to_string(),
|
||||||
|
vals: vec![a.display_name.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "gecos".to_string(),
|
||||||
|
vals: vec![a.display_name.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "userPassword".to_string(),
|
||||||
|
vals: vec![a.password.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "initials".to_string(),
|
||||||
|
vals: vec!["tu".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "homeDirectory".to_string(),
|
||||||
|
vals: vec![format!("/home/{}", a.name).as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "mail".to_string(),
|
||||||
|
vals: vec![format!("{}@{}", a.name, self.realm).as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "loginshell".to_string(),
|
||||||
|
vals: vec!["/bin/zsh".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "uidNumber".to_string(),
|
||||||
|
vals: vec!["-1".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "gidNumber".to_string(),
|
||||||
|
vals: vec!["-1".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "krbextradata".to_string(),
|
||||||
|
vals: vec!["placeholder".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "krblastpwdchange".to_string(),
|
||||||
|
vals: vec!["20230119053224Z".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "krbPasswordExpiration".to_string(),
|
||||||
|
vals: vec!["20380119053224Z".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "krbPrincipalName".to_string(),
|
||||||
|
vals: vec![format!("{}@{}", a.name, self.realm.to_uppercase())
|
||||||
|
.as_bytes()
|
||||||
|
.into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "krbCanonicalName".to_string(),
|
||||||
|
vals: vec![format!("{}@{}", a.name, self.realm.to_uppercase())
|
||||||
|
.as_bytes()
|
||||||
|
.into()],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
self.ldap.add(account).await?;
|
||||||
|
}
|
||||||
|
Entity::Group(g) => {
|
||||||
|
let group = LdapAddRequest {
|
||||||
|
dn,
|
||||||
|
attributes: vec![
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "objectClass".to_string(),
|
||||||
|
vals: vec![
|
||||||
|
"top".as_bytes().into(),
|
||||||
|
"groupofnames".as_bytes().into(),
|
||||||
|
"nestedgroup".as_bytes().into(),
|
||||||
|
"ipausergroup".as_bytes().into(),
|
||||||
|
"ipaobject".as_bytes().into(),
|
||||||
|
"posixgroup".as_bytes().into(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "cn".to_string(),
|
||||||
|
vals: vec![g.name.as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "ipauniqueid".to_string(),
|
||||||
|
vals: vec!["autogenerate".as_bytes().into()],
|
||||||
|
},
|
||||||
|
LdapAttribute {
|
||||||
|
atype: "gidNumber".to_string(),
|
||||||
|
vals: vec!["-1".as_bytes().into()],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
self.ldap.add(group).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all the members.
|
||||||
|
for g in targets.iter().filter_map(|u| {
|
||||||
|
let e = all_entities.get(u).unwrap();
|
||||||
|
match e {
|
||||||
|
Entity::Group(g) => Some(g),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
// List of dns
|
||||||
|
let vals: Vec<Vec<u8>> = g
|
||||||
|
.members
|
||||||
|
.iter()
|
||||||
|
.map(|id| {
|
||||||
|
all_entities
|
||||||
|
.get(id)
|
||||||
|
.unwrap()
|
||||||
|
.get_ipa_ldap_dn(&self.ldap.basedn)
|
||||||
|
.as_bytes()
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let req = LdapModifyRequest {
|
||||||
|
dn: g.get_ipa_ldap_dn(&self.ldap.basedn),
|
||||||
|
changes: vec![LdapModify {
|
||||||
|
operation: LdapModifyType::Replace,
|
||||||
|
modification: LdapPartialAttribute {
|
||||||
|
atype: "member".to_string(),
|
||||||
|
vals,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
self.ldap.modify(req).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_access_controls(
|
||||||
|
&self,
|
||||||
|
_access: &HashMap<Uuid, Vec<EntityType>>,
|
||||||
|
_all_entities: &HashMap<Uuid, Entity>,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
// todo!();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn open_user_connection(
|
||||||
|
&self,
|
||||||
|
test_start: Instant,
|
||||||
|
name: &str,
|
||||||
|
pw: &str,
|
||||||
|
) -> Result<(Duration, Duration), ()> {
|
||||||
|
self.ldap.open_user_connection(test_start, name, pw).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn close_connection(&self) {
|
||||||
|
self.ldap.close_connection().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn search(
|
||||||
|
&self,
|
||||||
|
test_start: Instant,
|
||||||
|
ids: &[String],
|
||||||
|
) -> Result<(Duration, Duration, usize), ()> {
|
||||||
|
self.ldap.search_name(test_start, ids).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -291,7 +291,12 @@ impl KaniHttpServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn close_connection(&self) {
|
pub async fn close_connection(&self) {
|
||||||
assert!(self.client.logout().await.is_ok());
|
assert!(self
|
||||||
|
.client
|
||||||
|
.logout()
|
||||||
|
.await
|
||||||
|
.map_err(|e| error!("close_connection {:?}", e))
|
||||||
|
.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search(
|
pub async fn search(
|
||||||
|
|
|
@ -135,6 +135,11 @@ impl LdapClient {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn open_ipa_admin_connection(&self, pw: &str) -> Result<(), ()> {
|
||||||
|
let admin_dn = format!("uid=admin,cn=users,cn=accounts,{}", self.basedn);
|
||||||
|
self.bind(admin_dn, pw.to_string()).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn open_user_connection(
|
pub async fn open_user_connection(
|
||||||
&self,
|
&self,
|
||||||
test_start: Instant,
|
test_start: Instant,
|
||||||
|
|
|
@ -16,17 +16,21 @@ static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||||
extern crate tracing;
|
extern crate tracing;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::ds::DirectoryServer;
|
use crate::ds::DirectoryServer;
|
||||||
|
use crate::ipa::IpaServer;
|
||||||
use crate::kani::{KaniHttpServer, KaniLdapServer};
|
use crate::kani::{KaniHttpServer, KaniLdapServer};
|
||||||
|
use crate::setup::config;
|
||||||
|
|
||||||
mod data;
|
mod data;
|
||||||
mod ds;
|
mod ds;
|
||||||
|
mod generate;
|
||||||
|
mod ipa;
|
||||||
mod kani;
|
mod kani;
|
||||||
mod ldap;
|
mod ldap;
|
||||||
mod preprocess;
|
mod preprocess;
|
||||||
|
@ -39,6 +43,8 @@ include!("./opt.rs");
|
||||||
impl OrcaOpt {
|
impl OrcaOpt {
|
||||||
pub fn debug(&self) -> bool {
|
pub fn debug(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
OrcaOpt::TestConnection(opt) => opt.copt.debug,
|
||||||
|
OrcaOpt::Generate(opt) => opt.copt.debug,
|
||||||
OrcaOpt::PreProc(opt) => opt.copt.debug,
|
OrcaOpt::PreProc(opt) => opt.copt.debug,
|
||||||
OrcaOpt::Setup(opt) => opt.copt.debug,
|
OrcaOpt::Setup(opt) => opt.copt.debug,
|
||||||
OrcaOpt::Run(opt) => opt.copt.debug,
|
OrcaOpt::Run(opt) => opt.copt.debug,
|
||||||
|
@ -50,6 +56,7 @@ pub enum TargetServerBuilder {
|
||||||
Kanidm(String, String),
|
Kanidm(String, String),
|
||||||
KanidmLdap(String, String, String, String),
|
KanidmLdap(String, String, String, String),
|
||||||
DirSrv(String, String, String),
|
DirSrv(String, String, String),
|
||||||
|
Ipa(String, String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TargetServerBuilder {
|
impl TargetServerBuilder {
|
||||||
|
@ -59,6 +66,7 @@ impl TargetServerBuilder {
|
||||||
TargetServerBuilder::Kanidm(a, b) => KaniHttpServer::build(a, b),
|
TargetServerBuilder::Kanidm(a, b) => KaniHttpServer::build(a, b),
|
||||||
TargetServerBuilder::KanidmLdap(a, b, c, d) => KaniLdapServer::build(a, b, c, d),
|
TargetServerBuilder::KanidmLdap(a, b, c, d) => KaniLdapServer::build(a, b, c, d),
|
||||||
TargetServerBuilder::DirSrv(a, b, c) => DirectoryServer::build(a, b, c),
|
TargetServerBuilder::DirSrv(a, b, c) => DirectoryServer::build(a, b, c),
|
||||||
|
TargetServerBuilder::Ipa(a, b, c) => IpaServer::build(a, b, c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +76,7 @@ pub enum TargetServer {
|
||||||
Kanidm(KaniHttpServer),
|
Kanidm(KaniHttpServer),
|
||||||
KanidmLdap(Box<KaniLdapServer>),
|
KanidmLdap(Box<KaniLdapServer>),
|
||||||
DirSrv(DirectoryServer),
|
DirSrv(DirectoryServer),
|
||||||
|
Ipa(IpaServer),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TargetServer {
|
impl TargetServer {
|
||||||
|
@ -76,6 +85,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.info(),
|
TargetServer::Kanidm(k) => k.info(),
|
||||||
TargetServer::KanidmLdap(k) => k.info(),
|
TargetServer::KanidmLdap(k) => k.info(),
|
||||||
TargetServer::DirSrv(k) => k.info(),
|
TargetServer::DirSrv(k) => k.info(),
|
||||||
|
TargetServer::Ipa(k) => k.info(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +94,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(_) => "kanidm_http",
|
TargetServer::Kanidm(_) => "kanidm_http",
|
||||||
TargetServer::KanidmLdap(_) => "kanidm_ldap",
|
TargetServer::KanidmLdap(_) => "kanidm_ldap",
|
||||||
TargetServer::DirSrv(_) => "directory_server",
|
TargetServer::DirSrv(_) => "directory_server",
|
||||||
|
TargetServer::Ipa(_) => "ipa",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +103,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.builder(),
|
TargetServer::Kanidm(k) => k.builder(),
|
||||||
TargetServer::KanidmLdap(k) => k.builder(),
|
TargetServer::KanidmLdap(k) => k.builder(),
|
||||||
TargetServer::DirSrv(k) => k.builder(),
|
TargetServer::DirSrv(k) => k.builder(),
|
||||||
|
TargetServer::Ipa(k) => k.builder(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +112,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.open_admin_connection().await,
|
TargetServer::Kanidm(k) => k.open_admin_connection().await,
|
||||||
TargetServer::KanidmLdap(k) => k.open_admin_connection().await,
|
TargetServer::KanidmLdap(k) => k.open_admin_connection().await,
|
||||||
TargetServer::DirSrv(k) => k.open_admin_connection().await,
|
TargetServer::DirSrv(k) => k.open_admin_connection().await,
|
||||||
|
TargetServer::Ipa(k) => k.open_admin_connection().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +121,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.setup_admin_delete_uuids(targets).await,
|
TargetServer::Kanidm(k) => k.setup_admin_delete_uuids(targets).await,
|
||||||
TargetServer::KanidmLdap(k) => k.setup_admin_delete_uuids(targets).await,
|
TargetServer::KanidmLdap(k) => k.setup_admin_delete_uuids(targets).await,
|
||||||
TargetServer::DirSrv(k) => k.setup_admin_delete_uuids(targets).await,
|
TargetServer::DirSrv(k) => k.setup_admin_delete_uuids(targets).await,
|
||||||
|
TargetServer::Ipa(k) => k.setup_admin_delete_uuids(targets).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +143,10 @@ impl TargetServer {
|
||||||
k.setup_admin_precreate_entities(targets, all_entities)
|
k.setup_admin_precreate_entities(targets, all_entities)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
TargetServer::Ipa(k) => {
|
||||||
|
k.setup_admin_precreate_entities(targets, all_entities)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +159,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.setup_access_controls(access, all_entities).await,
|
TargetServer::Kanidm(k) => k.setup_access_controls(access, all_entities).await,
|
||||||
TargetServer::KanidmLdap(k) => k.setup_access_controls(access, all_entities).await,
|
TargetServer::KanidmLdap(k) => k.setup_access_controls(access, all_entities).await,
|
||||||
TargetServer::DirSrv(k) => k.setup_access_controls(access, all_entities).await,
|
TargetServer::DirSrv(k) => k.setup_access_controls(access, all_entities).await,
|
||||||
|
TargetServer::Ipa(k) => k.setup_access_controls(access, all_entities).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +173,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.open_user_connection(test_start, name, pw).await,
|
TargetServer::Kanidm(k) => k.open_user_connection(test_start, name, pw).await,
|
||||||
TargetServer::KanidmLdap(k) => k.open_user_connection(test_start, name, pw).await,
|
TargetServer::KanidmLdap(k) => k.open_user_connection(test_start, name, pw).await,
|
||||||
TargetServer::DirSrv(k) => k.open_user_connection(test_start, name, pw).await,
|
TargetServer::DirSrv(k) => k.open_user_connection(test_start, name, pw).await,
|
||||||
|
TargetServer::Ipa(k) => k.open_user_connection(test_start, name, pw).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +182,7 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.close_connection().await,
|
TargetServer::Kanidm(k) => k.close_connection().await,
|
||||||
TargetServer::KanidmLdap(k) => k.close_connection().await,
|
TargetServer::KanidmLdap(k) => k.close_connection().await,
|
||||||
TargetServer::DirSrv(k) => k.close_connection().await,
|
TargetServer::DirSrv(k) => k.close_connection().await,
|
||||||
|
TargetServer::Ipa(k) => k.close_connection().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,10 +195,27 @@ impl TargetServer {
|
||||||
TargetServer::Kanidm(k) => k.search(test_start, ids).await,
|
TargetServer::Kanidm(k) => k.search(test_start, ids).await,
|
||||||
TargetServer::KanidmLdap(k) => k.search(test_start, ids).await,
|
TargetServer::KanidmLdap(k) => k.search(test_start, ids).await,
|
||||||
TargetServer::DirSrv(k) => k.search(test_start, ids).await,
|
TargetServer::DirSrv(k) => k.search(test_start, ids).await,
|
||||||
|
TargetServer::Ipa(k) => k.search(test_start, ids).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn conntest(target: &TargetOpt, profile_path: &Path) -> Result<(), ()> {
|
||||||
|
info!(
|
||||||
|
"Performing conntest of {:?} from {}",
|
||||||
|
target,
|
||||||
|
profile_path.to_str().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (_data, _profile, server) = config(target, profile_path)?;
|
||||||
|
|
||||||
|
server
|
||||||
|
.open_admin_connection()
|
||||||
|
.await
|
||||||
|
.map(|_| info!("success"))
|
||||||
|
.map_err(|_| error!("connection test failed"))
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let opt = OrcaOpt::parse();
|
let opt = OrcaOpt::parse();
|
||||||
|
@ -193,6 +231,10 @@ async fn main() {
|
||||||
info!("Orca - the Kanidm Load Testing Utility.");
|
info!("Orca - the Kanidm Load Testing Utility.");
|
||||||
debug!("cli -> {:?}", opt);
|
debug!("cli -> {:?}", opt);
|
||||||
match opt {
|
match opt {
|
||||||
|
OrcaOpt::TestConnection(opt) => {
|
||||||
|
let _ = conntest(&opt.target, &opt.profile_path).await;
|
||||||
|
}
|
||||||
|
OrcaOpt::Generate(opt) => generate::doit(&opt.output_path),
|
||||||
OrcaOpt::PreProc(opt) => preprocess::doit(&opt.input_path, &opt.output_path),
|
OrcaOpt::PreProc(opt) => preprocess::doit(&opt.input_path, &opt.output_path),
|
||||||
OrcaOpt::Setup(opt) => {
|
OrcaOpt::Setup(opt) => {
|
||||||
let _ = setup::doit(&opt.target, &opt.profile_path).await;
|
let _ = setup::doit(&opt.target, &opt.profile_path).await;
|
||||||
|
@ -204,5 +246,5 @@ async fn main() {
|
||||||
// run the test!
|
// run the test!
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
info!("Success");
|
debug!("Exit");
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,20 @@ struct PreProcOpt {
|
||||||
pub output_path: PathBuf,
|
pub output_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
struct GenerateOpt {
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub copt: CommonOpt,
|
||||||
|
#[clap(parse(from_os_str), short, long = "output")]
|
||||||
|
/// Path to write the generated output.
|
||||||
|
pub output_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct SetupOpt {
|
struct SetupOpt {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub copt: CommonOpt,
|
pub copt: CommonOpt,
|
||||||
#[clap(name = "target")]
|
#[clap(name = "target")]
|
||||||
/// Which service to target during this operation.
|
|
||||||
/// Valid values are "ds" or "kanidm"
|
|
||||||
pub target: TargetOpt,
|
pub target: TargetOpt,
|
||||||
#[clap(parse(from_os_str), short, long = "profile")]
|
#[clap(parse(from_os_str), short, long = "profile")]
|
||||||
/// Path to the test profile.
|
/// Path to the test profile.
|
||||||
|
@ -37,8 +44,6 @@ struct RunOpt {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub copt: CommonOpt,
|
pub copt: CommonOpt,
|
||||||
#[clap(name = "target")]
|
#[clap(name = "target")]
|
||||||
/// Which service to target during this operation.
|
|
||||||
/// Valid values are "ds" or "kanidm"
|
|
||||||
pub target: TargetOpt,
|
pub target: TargetOpt,
|
||||||
#[clap(name = "test_type")]
|
#[clap(name = "test_type")]
|
||||||
/// Which type of test to run against this system
|
/// Which type of test to run against this system
|
||||||
|
@ -49,10 +54,14 @@ struct RunOpt {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
|
/// The target to run against
|
||||||
pub(crate) enum TargetOpt {
|
pub(crate) enum TargetOpt {
|
||||||
#[clap(name = "ds")]
|
#[clap(name = "ds")]
|
||||||
/// Run against the ldap/ds profile
|
/// Run against the ldap/ds profile
|
||||||
Ds,
|
Ds,
|
||||||
|
#[clap(name = "ipa")]
|
||||||
|
/// Run against the ipa profile
|
||||||
|
Ipa,
|
||||||
#[clap(name = "kanidm")]
|
#[clap(name = "kanidm")]
|
||||||
/// Run against the kanidm http profile
|
/// Run against the kanidm http profile
|
||||||
Kanidm,
|
Kanidm,
|
||||||
|
@ -67,9 +76,10 @@ impl FromStr for TargetOpt {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"ds" => Ok(TargetOpt::Ds),
|
"ds" => Ok(TargetOpt::Ds),
|
||||||
|
"ipa" => Ok(TargetOpt::Ipa),
|
||||||
"kanidm" => Ok(TargetOpt::Kanidm),
|
"kanidm" => Ok(TargetOpt::Kanidm),
|
||||||
"kanidm_ldap" => Ok(TargetOpt::KanidmLdap),
|
"kanidm_ldap" => Ok(TargetOpt::KanidmLdap),
|
||||||
_ => Err("Invalid target type. Must be ds, kanidm, or kanidm_ldap"),
|
_ => Err("Invalid target type. Must be ds, ipa, kanidm, or kanidm_ldap"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,6 +132,13 @@ Orca works in a few steps.
|
||||||
"
|
"
|
||||||
)]
|
)]
|
||||||
enum OrcaOpt {
|
enum OrcaOpt {
|
||||||
|
#[clap(name = "conntest")]
|
||||||
|
/// Perform a connection test against the specified target
|
||||||
|
TestConnection(SetupOpt),
|
||||||
|
#[clap(name = "generate")]
|
||||||
|
/// Generate a new dataset that can be used for testing. Parameters can be provided
|
||||||
|
/// to affect the type and quantity of data created.
|
||||||
|
Generate(GenerateOpt),
|
||||||
#[clap(name = "preprocess")]
|
#[clap(name = "preprocess")]
|
||||||
/// Preprocess a dataset that can be used for testing
|
/// Preprocess a dataset that can be used for testing
|
||||||
PreProc(PreProcOpt),
|
PreProc(PreProcOpt),
|
||||||
|
|
|
@ -7,6 +7,13 @@ pub struct DsConfig {
|
||||||
pub base_dn: String,
|
pub base_dn: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct IpaConfig {
|
||||||
|
pub uri: String,
|
||||||
|
pub realm: String,
|
||||||
|
pub admin_pw: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct KaniHttpConfig {
|
pub struct KaniHttpConfig {
|
||||||
pub uri: String,
|
pub uri: String,
|
||||||
|
@ -43,6 +50,7 @@ pub struct Profile {
|
||||||
pub data: String,
|
pub data: String,
|
||||||
pub results: String,
|
pub results: String,
|
||||||
pub ds_config: Option<DsConfig>,
|
pub ds_config: Option<DsConfig>,
|
||||||
|
pub ipa_config: Option<IpaConfig>,
|
||||||
pub kani_http_config: Option<KaniHttpConfig>,
|
pub kani_http_config: Option<KaniHttpConfig>,
|
||||||
pub kani_ldap_config: Option<KaniLdapConfig>,
|
pub kani_ldap_config: Option<KaniLdapConfig>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
@ -76,7 +76,6 @@ async fn basic_arbiter(
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match raw_results_rx.recv_deadline(end_of_test) {
|
match raw_results_rx.recv_deadline(end_of_test) {
|
||||||
// We are currently discarding results.
|
|
||||||
Ok(datum) => results.push(datum),
|
Ok(datum) => results.push(datum),
|
||||||
Err(RecvTimeoutError::Timeout) => {
|
Err(RecvTimeoutError::Timeout) => {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::data::TestData;
|
use crate::data::TestData;
|
||||||
use crate::ds::DirectoryServer;
|
use crate::ds::DirectoryServer;
|
||||||
|
use crate::ipa::IpaServer;
|
||||||
use crate::kani::{KaniHttpServer, KaniLdapServer};
|
use crate::kani::{KaniHttpServer, KaniLdapServer};
|
||||||
use crate::profile::Profile;
|
use crate::profile::Profile;
|
||||||
use crate::{TargetOpt, TargetServer};
|
use crate::{TargetOpt, TargetServer};
|
||||||
|
@ -54,6 +55,14 @@ pub(crate) fn config(
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TargetOpt::Ipa => {
|
||||||
|
if let Some(ipaconfig) = profile.ipa_config.as_ref() {
|
||||||
|
IpaServer::new(ipaconfig)?
|
||||||
|
} else {
|
||||||
|
error!("To use ipa, you must have the ipa_config section in your profile");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
TargetOpt::KanidmLdap => {
|
TargetOpt::KanidmLdap => {
|
||||||
if let Some(klconfig) = profile.kani_ldap_config.as_ref() {
|
if let Some(klconfig) = profile.kani_ldap_config.as_ref() {
|
||||||
KaniLdapServer::new(klconfig)?
|
KaniLdapServer::new(klconfig)?
|
||||||
|
|
Loading…
Reference in a new issue