Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kadmin: support for setting API version #69

Merged
merged 14 commits into from
Dec 4, 2024
67 changes: 56 additions & 11 deletions kadmin/src/kadmin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use std::{
};

use kadmin_sys::*;
#[cfg(feature = "python")]
use pyo3::prelude::*;

use crate::{
context::Context,
Expand All @@ -24,6 +26,41 @@ use crate::{
/// Lock acquired when creating or dropping a [`KAdmin`] instance
static KADMIN_INIT_LOCK: Mutex<()> = Mutex::new(());

/// kadm5 API version
///
/// MIT krb5 supports up to version 4. Heimdal supports up to version 2.
///
/// This changes which fields will be available in the [`Policy`] and [`Principal`] structs. If the
/// version is too low, some fields may not be populated. We try our best to document those in the
/// fields documentation themselves.
///
/// If no version is provided during the KAdmin initialization, it defaults to the most
/// conservative one, currently version 2.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[allow(clippy::exhaustive_enums)]
#[repr(u32)]
#[cfg_attr(feature = "python", pyclass(eq, eq_int))]
pub enum KAdminApiVersion {
/// Version 2
Version2 = KADM5_API_VERSION_2,
/// Version 3
Version3 = KADM5_API_VERSION_3,
/// Version 4
Version4 = KADM5_API_VERSION_4,
}

impl From<KAdminApiVersion> for u32 {
fn from(api_version: KAdminApiVersion) -> Self {
api_version as Self
}
}

impl Default for KAdminApiVersion {
fn default() -> Self {
Self::Version2
}
}

/// Interface to kadm5
///
/// This interface is not thread safe. Consider creating one per thread where needed, or using the
Expand Down Expand Up @@ -363,6 +400,7 @@ pub struct KAdminBuilder {
context: Option<Context>,
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: KAdminApiVersion,
}

impl KAdminBuilder {
Expand All @@ -384,16 +422,23 @@ impl KAdminBuilder {
self
}

/// Set the kadm5 API version to use. See [`KAdminApiVersion`] for details
pub fn api_version(mut self, api_version: KAdminApiVersion) -> Self {
self.api_version = api_version;
self
}

/// Construct a [`KAdmin`] object that isn't initialized yet from the builder inputs
fn get_kadmin(self) -> Result<(KAdmin, Params, DbArgs)> {
fn get_kadmin(self) -> Result<(KAdmin, Params, DbArgs, KAdminApiVersion)> {
let params = self.params.unwrap_or_default();
let db_args = self.db_args.unwrap_or_default();
let context = self.context.unwrap_or(Context::new()?);
let api_version = self.api_version;
let kadmin = KAdmin {
context,
server_handle: null_mut(),
};
Ok((kadmin, params, db_args))
Ok((kadmin, params, db_args, api_version))
}

/// Construct a [`KAdmin`] object from this builder using a client name (usually a principal
Expand All @@ -405,7 +450,7 @@ impl KAdminBuilder {
.lock()
.expect("Failed to lock context initialization.");

let (mut kadmin, params, db_args) = self.get_kadmin()?;
let (mut kadmin, params, db_args, api_version) = self.get_kadmin()?;

let client_name = CString::new(client_name)?;
let password = CString::new(password)?;
Expand All @@ -421,7 +466,7 @@ impl KAdminBuilder {
service_name.as_ptr().cast_mut(),
&mut params.params,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_2,
api_version.into(),
db_args.db_args,
&mut kadmin.server_handle,
)
Expand All @@ -447,7 +492,7 @@ impl KAdminBuilder {
.lock()
.expect("Failed to lock context initialization.");

let (mut kadmin, params, db_args) = self.get_kadmin()?;
let (mut kadmin, params, db_args, api_version) = self.get_kadmin()?;

let client_name = if let Some(client_name) = client_name {
CString::new(client_name)?
Expand Down Expand Up @@ -494,7 +539,7 @@ impl KAdminBuilder {
service_name.as_ptr().cast_mut(),
&mut params.params,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_2,
api_version.into(),
db_args.db_args,
&mut kadmin.server_handle,
)
Expand Down Expand Up @@ -525,7 +570,7 @@ impl KAdminBuilder {
.lock()
.expect("Failed to lock context initialization.");

let (mut kadmin, params, db_args) = self.get_kadmin()?;
let (mut kadmin, params, db_args, api_version) = self.get_kadmin()?;

let ccache = {
let mut ccache: MaybeUninit<krb5_ccache> = MaybeUninit::zeroed();
Expand Down Expand Up @@ -579,7 +624,7 @@ impl KAdminBuilder {
service_name.as_ptr().cast_mut(),
&mut params.params,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_2,
api_version.into(),
db_args.db_args,
&mut kadmin.server_handle,
)
Expand All @@ -604,7 +649,7 @@ impl KAdminBuilder {
.lock()
.expect("Failed to lock context initialization.");

let (mut _kadmin, _params, _db_args) = self.get_kadmin()?;
let (mut _kadmin, _params, _db_args, _api_version) = self.get_kadmin()?;

unimplemented!();
}
Expand All @@ -617,7 +662,7 @@ impl KAdminBuilder {
.lock()
.expect("Failed to lock context initialization.");

let (mut kadmin, params, db_args) = self.get_kadmin()?;
let (mut kadmin, params, db_args, api_version) = self.get_kadmin()?;

let client_name = if let Some(default_realm) = &kadmin.context.default_realm {
let mut concat = CString::new("root/admin@")?.into_bytes();
Expand All @@ -638,7 +683,7 @@ impl KAdminBuilder {
service_name.as_ptr().cast_mut(),
&mut params.params,
KADM5_STRUCT_VERSION,
KADM5_API_VERSION_2,
api_version.into(),
db_args.db_args,
&mut kadmin.server_handle,
)
Expand Down
2 changes: 1 addition & 1 deletion kadmin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub mod keysalt_list;
pub use keysalt_list::EncryptionType;

pub mod kadmin;
pub use kadmin::{KAdmin, KAdminImpl};
pub use kadmin::{KAdmin, KAdminApiVersion, KAdminImpl};

pub mod sync;

Expand Down
16 changes: 16 additions & 0 deletions kadmin/src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,41 @@ pub struct Policy {
/// are only tracked for principals which require preauthentication. The counter of failed
/// attempts resets to 0 after a successful attempt to authenticate. A value of 0 disables
/// lock‐out
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 3 and above
password_max_fail: u32,
/// Allowable time between authentication failures. If an authentication failure happens after
/// this duration has elapsed since the previous failure, the number of authentication failures
/// is reset to 1. A value of `None` means forever
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 3 and above
password_failcount_interval: Option<Duration>,
/// Duration for which the principal is locked from authenticating if too many authentication
/// failures occur without the specified failure count interval elapsing. A duration of `None`
/// means the principal remains locked out until it is administratively unlocked
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 3 and above
password_lockout_duration: Option<Duration>,
/// Policy attributes
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 4 and above
attributes: i32,
/// Maximum ticket life
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 4 and above
max_life: Option<Duration>,
/// Maximum renewable ticket life
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 4 and above
max_renewable_life: Option<Duration>,
/// Allowed keysalts
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 4 and above
#[getset(skip)]
allowed_keysalts: Option<KeySaltList>,
/// TL-data
///
/// Only available in [version][`crate::kadmin::KAdminApiVersion`] 4 and above
#[getset(skip)]
tl_data: TlData,
}
Expand Down
42 changes: 29 additions & 13 deletions kadmin/src/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use pyo3::{
use crate::{
db_args::DbArgs,
error::Result,
kadmin::KAdminImpl,
kadmin::{KAdminApiVersion, KAdminImpl},
keysalt_list::{EncryptionType, KeySalt, KeySaltList, SaltType},
params::Params,
policy::Policy,
Expand All @@ -23,6 +23,7 @@ use crate::{
#[pymodule(name = "_lib")]
fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
m.add_class::<KAdminApiVersion>()?;
m.add_class::<Params>()?;
m.add_class::<DbArgs>()?;
m.add_class::<TlDataEntry>()?;
Expand Down Expand Up @@ -181,14 +182,21 @@ impl TlData {
}

impl KAdmin {
fn py_get_builder(params: Option<Params>, db_args: Option<DbArgs>) -> KAdminBuilder {
fn py_get_builder(
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: Option<KAdminApiVersion>,
) -> KAdminBuilder {
let mut builder = KAdminBuilder::default();
if let Some(params) = params {
builder = builder.params(params);
}
if let Some(db_args) = db_args {
builder = builder.db_args(db_args);
}
if let Some(api_version) = api_version {
builder = builder.api_version(api_version);
}
builder
}
}
Expand Down Expand Up @@ -302,56 +310,64 @@ impl KAdmin {

#[cfg(feature = "client")]
#[staticmethod]
#[pyo3(name = "with_password", signature = (client_name, password, params=None, db_args=None))]
#[pyo3(name = "with_password", signature = (client_name, password, params=None, db_args=None, api_version=None))]
fn py_with_password(
client_name: &str,
password: &str,
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: Option<KAdminApiVersion>,
) -> Result<Self> {
Self::py_get_builder(params, db_args).with_password(client_name, password)
Self::py_get_builder(params, db_args, api_version).with_password(client_name, password)
}

#[cfg(feature = "client")]
#[staticmethod]
#[pyo3(name = "with_keytab", signature = (client_name=None, keytab=None, params=None, db_args=None))]
#[pyo3(name = "with_keytab", signature = (client_name=None, keytab=None, params=None, db_args=None, api_version=None))]
fn py_with_keytab(
client_name: Option<&str>,
keytab: Option<&str>,
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: Option<KAdminApiVersion>,
) -> Result<Self> {
Self::py_get_builder(params, db_args).with_keytab(client_name, keytab)
Self::py_get_builder(params, db_args, api_version).with_keytab(client_name, keytab)
}

#[cfg(feature = "client")]
#[staticmethod]
#[pyo3(name = "with_ccache", signature = (client_name=None, ccache_name=None, params=None, db_args=None))]
#[pyo3(name = "with_ccache", signature = (client_name=None, ccache_name=None, params=None, db_args=None, api_version=None))]
fn py_with_ccache(
client_name: Option<&str>,
ccache_name: Option<&str>,
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: Option<KAdminApiVersion>,
) -> Result<Self> {
Self::py_get_builder(params, db_args).with_ccache(client_name, ccache_name)
Self::py_get_builder(params, db_args, api_version).with_ccache(client_name, ccache_name)
}

#[cfg(feature = "client")]
#[staticmethod]
#[pyo3(name = "with_anonymous", signature = (client_name, params=None, db_args=None))]
#[pyo3(name = "with_anonymous", signature = (client_name, params=None, db_args=None, api_version=None))]
fn py_with_anonymous(
client_name: &str,
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: Option<KAdminApiVersion>,
) -> Result<Self> {
Self::py_get_builder(params, db_args).with_anonymous(client_name)
Self::py_get_builder(params, db_args, api_version).with_anonymous(client_name)
}

#[cfg(feature = "local")]
#[staticmethod]
#[pyo3(name = "with_local", signature = (params=None, db_args=None))]
fn py_with_local(params: Option<Params>, db_args: Option<DbArgs>) -> Result<Self> {
Self::py_get_builder(params, db_args).with_local()
#[pyo3(name = "with_local", signature = (params=None, db_args=None, api_version=None))]
fn py_with_local(
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: Option<KAdminApiVersion>,
) -> Result<Self> {
Self::py_get_builder(params, db_args, api_version).with_local()
}
}

Expand Down
10 changes: 9 additions & 1 deletion kadmin/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use pyo3::prelude::*;
use crate::{
db_args::DbArgs,
error::Result,
kadmin::KAdminImpl,
kadmin::{KAdminApiVersion, KAdminImpl},
params::Params,
policy::{Policy, PolicyBuilder, PolicyModifier},
principal::Principal,
Expand Down Expand Up @@ -193,6 +193,7 @@ impl KAdminImpl for KAdmin {
pub struct KAdminBuilder {
params: Option<Params>,
db_args: Option<DbArgs>,
api_version: KAdminApiVersion,
}

impl KAdminBuilder {
Expand All @@ -210,6 +211,12 @@ impl KAdminBuilder {
self
}

/// Set the kadm5 API version to use. See [`KAdminApiVersion`] for details
pub fn api_version(mut self, api_version: KAdminApiVersion) -> Self {
self.api_version = api_version;
self
}

/// Construct a [`crate::kadmin::KAdminBuilder`] object that isn't initialized yet from the
/// builder inputs
fn get_builder(self) -> Result<crate::kadmin::KAdminBuilder> {
Expand All @@ -220,6 +227,7 @@ impl KAdminBuilder {
if let Some(db_args) = self.db_args {
builder = builder.db_args(db_args);
}
builder = builder.api_version(self.api_version);
Ok(builder)
}

Expand Down
Loading
Loading