mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-23 17:33:57 +02:00
Compare commits
6 commits
bddb272b51
...
8c0de4cd01
Author | SHA1 | Date | |
---|---|---|---|
|
8c0de4cd01 | ||
|
82ca92cd4e | ||
|
0e0e8ff844 | ||
|
266dc77536 | ||
|
3edee485dd | ||
|
38c260214b |
|
@ -10,3 +10,4 @@ kanidmd/sampledata
|
|||
Makefile
|
||||
target
|
||||
test.db
|
||||
Dockerfile
|
||||
|
|
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -2978,10 +2978,12 @@ dependencies = [
|
|||
"hex",
|
||||
"kanidm-hsm-crypto",
|
||||
"kanidm_proto",
|
||||
"md-5",
|
||||
"openssl",
|
||||
"openssl-sys",
|
||||
"rand",
|
||||
"serde",
|
||||
"sha-crypt",
|
||||
"sha2",
|
||||
"sketching",
|
||||
"tracing",
|
||||
|
@ -3528,6 +3530,16 @@ dependencies = [
|
|||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
|
|
|
@ -204,6 +204,7 @@ libsqlite3-sys = "^0.25.2"
|
|||
lodepng = "3.11.0"
|
||||
lru = "^0.12.5"
|
||||
mathru = "^0.13.0"
|
||||
md-5 = "0.10.6"
|
||||
mimalloc = "0.1.43"
|
||||
notify-debouncer-full = { version = "0.1" }
|
||||
num_enum = "^0.5.11"
|
||||
|
|
|
@ -85,7 +85,7 @@ URL **(recommended)**
|
|||
|
||||
<dt>
|
||||
|
||||
[WebFinger URL **(discouraged)**](#webfinger)
|
||||
[WebFinger URL](#webfinger) **(discouraged)**
|
||||
|
||||
</dt>
|
||||
|
||||
|
@ -162,7 +162,7 @@ Token endpoint
|
|||
|
||||
<dt>
|
||||
|
||||
OpenID Connect issuer URI
|
||||
OpenID Connect Issuer URL
|
||||
|
||||
</dt>
|
||||
|
||||
|
@ -458,58 +458,83 @@ Each client has unique signing keys and access secrets, so this is limited to ea
|
|||
|
||||
## WebFinger
|
||||
|
||||
[WebFinger](https://datatracker.ietf.org/doc/html/rfc7033) provides a mechanism
|
||||
for discovering information about people or other entities. It can be used by an
|
||||
identity provider to supply OpenID Connect discovery information.
|
||||
[WebFinger][webfinger] provides a mechanism for discovering information about
|
||||
entities at a well-known URL (`https://{hostname}/.well-known/webfinger`).
|
||||
|
||||
Kanidm provides
|
||||
[an Identity Provider Discovery for OIDC URL](https://datatracker.ietf.org/doc/html/rfc7033#section-3.1)
|
||||
response to all incoming WebFinger requests, using a user's SPN as their account
|
||||
ID. This does not match on email addresses as they are not guaranteed to be
|
||||
unique.
|
||||
It can be used by a WebFinger client to
|
||||
[discover the OIDC Issuer URL][webfinger-oidc] of an identity provider from the
|
||||
hostname alone, and seems to be intended to support dynamic client registration
|
||||
flows for large public identity providers.
|
||||
|
||||
However, WebFinger has a number of flaws which make it difficult to use with
|
||||
Kanidm:
|
||||
Kanidm v1.5.1 and later can respond to WebFinger requests, using a user's SPN as
|
||||
part of [an `acct` URI][rfc7565] (eg: `acct:user@idm.example.com`). While SPNs
|
||||
and `acct` URIs look like email addresses, [as per RFC 7565][rfc7565s4], there
|
||||
is no guarantee that it is valid for any particular application protocol, unless
|
||||
an administrator explicitly provides for it.
|
||||
|
||||
* WebFinger assumes that the identity provider will give the same `iss`
|
||||
(Issuer) for every OAuth 2.0/OIDC client, and there is no standard way for a
|
||||
WebFinger client to report its client ID.
|
||||
When setting up an application to authenticate with Kanidm, WebFinger **does not
|
||||
add any security** over configuring an OIDC Discovery URL directly. In an OIDC
|
||||
context, the specification makes a number of flawed assumptions which make it
|
||||
difficult to use with Kanidm:
|
||||
|
||||
Kanidm uses a *different* `iss` (Issuer) value for each client.
|
||||
* WebFinger assumes that an identity provider will use the same Issuer URL and
|
||||
OIDC Discovery document (which contains endpoint URLs and token signing keys)
|
||||
for *all* OAuth 2.0/OIDC clients.
|
||||
|
||||
* WebFinger requires that this be served at the *root* of the domain of a user's
|
||||
Kanidm uses *client-specific* Issuer URLs, endpoint URLs and token signing
|
||||
keys. This ensures that tokens can only be used with their intended service.
|
||||
|
||||
* WebFinger endpoints must be served at the *root* of the domain of a user's
|
||||
SPN (ie: information about the user with SPN `user@idm.example.com` is at
|
||||
`https://idm.example.com/.well-known/webfinger`).
|
||||
`https://idm.example.com/.well-known/webfinger?resource=acct%3Auser%40idm.example.com`).
|
||||
|
||||
Kanidm *does not* provide a WebFinger endpoint at its root URL, because it has
|
||||
no way to know *which* OAuth 2.0/OIDC client a WebFinger request is associated
|
||||
with, so could report an incorrect `iss` (Issuer).
|
||||
Unlike OIDC Discovery, WebFinger clients do not report their OAuth 2.0/OIDC
|
||||
client ID in the request, so there is no way to tell them apart.
|
||||
|
||||
You will need a load balancer in front of Kanidm's HTTPS server to redirect
|
||||
requests to the appropriate `/oauth2/openid/:client_id:/.well-known/webfinger`
|
||||
URL. If the client does not follow redirects, you may need to rewrite the
|
||||
request in the load balancer instead.
|
||||
As a result, Kanidm *does not* provide a WebFinger endpoint at its root URL,
|
||||
because it could report an incorrect Issuer URL and lead the client to an
|
||||
incorrect OIDC Discovery document.
|
||||
|
||||
You will need a load balancer in front of Kanidm's HTTPS server to send a HTTP
|
||||
307 redirect to the appropriate
|
||||
`/oauth2/openid/:client_id:/.well-known/webfinger` URL, *while preserving all
|
||||
query parameters*. For example, with Caddy:
|
||||
|
||||
```caddy
|
||||
# Match on a prefix, and use {uri} to preserve all query parameters.
|
||||
# This only supports *one* client.
|
||||
example.com {
|
||||
redir /.well-known/webfinger https://idm.example.com/oauth2/openid/:client_id:{uri} 307
|
||||
}
|
||||
```
|
||||
|
||||
If you have *multiple* WebFinger clients, it will need to map some other
|
||||
property of the request (such as a source IP address or `User-Agent` header)
|
||||
to a client ID, and redirect to the appropriate WebFinger URL for that client.
|
||||
|
||||
* Kanidm responds to *all* WebFinger queries with
|
||||
[an Identity Provider Discovery for OIDC URL](https://datatracker.ietf.org/doc/html/rfc7033#section-3.1),
|
||||
**regardless** of what
|
||||
[`rel` parameter](https://datatracker.ietf.org/doc/html/rfc7033#section-4.4.4.1)
|
||||
was specified.
|
||||
|
||||
This is to work around
|
||||
[a broken client](https://tailscale.com/kb/1240/sso-custom-oidc) which doesn't
|
||||
send a `rel` parameter, but expects an Identity Provider Discovery issuer URL
|
||||
in response.
|
||||
[an Identity Provider Discovery for OIDC URL][webfinger-oidc], **ignoring**
|
||||
[`rel` parameter(s)][webfinger-rel].
|
||||
|
||||
If you want to use WebFinger in any *other* context on Kanidm's hostname,
|
||||
you'll need a load balancer in front of Kanidm which matches on some property
|
||||
of the request.
|
||||
|
||||
Because of the flaws of the WebFinger specification and the deployment
|
||||
difficulties they introduce, we recommend that applications use OpenID Connect
|
||||
Discovery or OAuth 2.0 Authorisation Server Metadata for client configuration
|
||||
instead of WebFinger.
|
||||
WebFinger clients *may* omit the `rel=` parameter, so if you host another
|
||||
service with relations for a Kanidm [`acct:` entity][rfc7565s4] and a client
|
||||
*does not* supply the `rel=` parameter, your load balancer will need to merge
|
||||
JSON responses from Kanidm and the other service(s).
|
||||
|
||||
Because of these issues, we recommend that applications support *directly*
|
||||
configuring OIDC using a Discovery URL or OAuth 2.0 Authorisation Server
|
||||
Metadata URL instead of WebFinger.
|
||||
|
||||
If a WebFinger client only checks WebFinger once during setup, you may wish to
|
||||
temporarily serve an appropriate static WebFinger document for that client
|
||||
instead.
|
||||
|
||||
[rfc7565]: https://datatracker.ietf.org/doc/html/rfc7565
|
||||
[rfc7565s4]: https://datatracker.ietf.org/doc/html/rfc7565#section-4
|
||||
[webfinger]: https://datatracker.ietf.org/doc/html/rfc7033
|
||||
[webfinger-oidc]: https://datatracker.ietf.org/doc/html/rfc7033#section-3.1
|
||||
[webfinger-rel]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.3
|
||||
|
|
|
@ -33,6 +33,9 @@ tracing = { workspace = true }
|
|||
uuid = { workspace = true }
|
||||
x509-cert = { workspace = true, features = ["pem"] }
|
||||
|
||||
md-5 = { workspace = true }
|
||||
sha-crypt = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
sketching = { workspace = true }
|
||||
|
||||
|
|
99
libs/crypto/src/crypt_md5.rs
Normal file
99
libs/crypto/src/crypt_md5.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use md5::{Digest, Md5};
|
||||
use std::cmp::min;
|
||||
|
||||
/// Maximium salt length.
|
||||
const MD5_MAGIC: &str = "$1$";
|
||||
const MD5_TRANSPOSE: &[u8] = b"\x0c\x06\x00\x0d\x07\x01\x0e\x08\x02\x0f\x09\x03\x05\x0a\x04\x0b";
|
||||
|
||||
const CRYPT_HASH64: &[u8] = b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
pub fn md5_sha2_hash64_encode(bs: &[u8]) -> String {
|
||||
let ngroups = (bs.len() + 2) / 3;
|
||||
let mut out = String::with_capacity(ngroups * 4);
|
||||
for g in 0..ngroups {
|
||||
let mut g_idx = g * 3;
|
||||
let mut enc = 0u32;
|
||||
for _ in 0..3 {
|
||||
let b = (if g_idx < bs.len() { bs[g_idx] } else { 0 }) as u32;
|
||||
enc >>= 8;
|
||||
enc |= b << 16;
|
||||
g_idx += 1;
|
||||
}
|
||||
for _ in 0..4 {
|
||||
out.push(char::from_u32(CRYPT_HASH64[(enc & 0x3F) as usize] as u32).unwrap_or('!'));
|
||||
enc >>= 6;
|
||||
}
|
||||
}
|
||||
match bs.len() % 3 {
|
||||
1 => {
|
||||
out.pop();
|
||||
out.pop();
|
||||
}
|
||||
2 => {
|
||||
out.pop();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn do_md5_crypt(pass: &[u8], salt: &[u8]) -> Vec<u8> {
|
||||
let mut dgst_b = Md5::new();
|
||||
dgst_b.update(pass);
|
||||
dgst_b.update(salt);
|
||||
dgst_b.update(pass);
|
||||
let mut hash_b = dgst_b.finalize();
|
||||
|
||||
let mut dgst_a = Md5::new();
|
||||
dgst_a.update(pass);
|
||||
dgst_a.update(MD5_MAGIC.as_bytes());
|
||||
dgst_a.update(salt);
|
||||
|
||||
let mut plen = pass.len();
|
||||
while plen > 0 {
|
||||
dgst_a.update(&hash_b[..min(plen, 16)]);
|
||||
if plen < 16 {
|
||||
break;
|
||||
}
|
||||
plen -= 16;
|
||||
}
|
||||
|
||||
plen = pass.len();
|
||||
while plen > 0 {
|
||||
if plen & 1 == 0 {
|
||||
dgst_a.update(&pass[..1])
|
||||
} else {
|
||||
dgst_a.update([0u8])
|
||||
}
|
||||
plen >>= 1;
|
||||
}
|
||||
|
||||
let mut hash_a = dgst_a.finalize();
|
||||
|
||||
for r in 0..1000 {
|
||||
let mut dgst_a = Md5::new();
|
||||
if r % 2 == 1 {
|
||||
dgst_a.update(pass);
|
||||
} else {
|
||||
dgst_a.update(hash_a);
|
||||
}
|
||||
if r % 3 > 0 {
|
||||
dgst_a.update(salt);
|
||||
}
|
||||
if r % 7 > 0 {
|
||||
dgst_a.update(pass);
|
||||
}
|
||||
if r % 2 == 0 {
|
||||
dgst_a.update(pass);
|
||||
} else {
|
||||
dgst_a.update(hash_a);
|
||||
}
|
||||
hash_a = dgst_a.finalize();
|
||||
}
|
||||
|
||||
for (i, &ti) in MD5_TRANSPOSE.iter().enumerate() {
|
||||
hash_b[i] = hash_a[ti as usize];
|
||||
}
|
||||
|
||||
md5_sha2_hash64_encode(&hash_b).into_bytes()
|
||||
}
|
|
@ -11,26 +11,24 @@
|
|||
#![deny(clippy::unreachable)]
|
||||
|
||||
use argon2::{Algorithm, Argon2, Params, PasswordHash, Version};
|
||||
use base64::engine::general_purpose;
|
||||
use base64::engine::GeneralPurpose;
|
||||
use base64::{alphabet, Engine};
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use base64::engine::general_purpose;
|
||||
use base64urlsafedata::Base64UrlSafeData;
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use kanidm_hsm_crypto::{HmacKey, Tpm};
|
||||
use kanidm_proto::internal::OperationError;
|
||||
use openssl::error::ErrorStack as OpenSSLErrorStack;
|
||||
use openssl::hash::{self, MessageDigest};
|
||||
use openssl::nid::Nid;
|
||||
use openssl::pkcs5::pbkdf2_hmac;
|
||||
use openssl::sha::{Sha1, Sha256, Sha512};
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::time::{Duration, Instant};
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use kanidm_hsm_crypto::{HmacKey, Tpm};
|
||||
|
||||
mod crypt_md5;
|
||||
pub mod mtls;
|
||||
pub mod prelude;
|
||||
pub mod serialise;
|
||||
|
@ -84,6 +82,7 @@ pub enum CryptoError {
|
|||
Argon2,
|
||||
Argon2Version,
|
||||
Argon2Parameters,
|
||||
Crypt,
|
||||
}
|
||||
|
||||
impl From<OpenSSLErrorStack> for CryptoError {
|
||||
|
@ -137,65 +136,15 @@ pub enum DbPasswordV1 {
|
|||
SHA512(Vec<u8>),
|
||||
SSHA512(Vec<u8>, Vec<u8>),
|
||||
NT_MD4(Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum ReplPasswordV1 {
|
||||
TPM_ARGON2ID {
|
||||
m_cost: u32,
|
||||
t_cost: u32,
|
||||
p_cost: u32,
|
||||
version: u32,
|
||||
salt: Base64UrlSafeData,
|
||||
key: Base64UrlSafeData,
|
||||
CRYPT_MD5 {
|
||||
s: Base64UrlSafeData,
|
||||
h: Base64UrlSafeData,
|
||||
},
|
||||
ARGON2ID {
|
||||
m_cost: u32,
|
||||
t_cost: u32,
|
||||
p_cost: u32,
|
||||
version: u32,
|
||||
salt: Base64UrlSafeData,
|
||||
key: Base64UrlSafeData,
|
||||
CRYPT_SHA256 {
|
||||
h: String,
|
||||
},
|
||||
PBKDF2 {
|
||||
cost: usize,
|
||||
salt: Base64UrlSafeData,
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
PBKDF2_SHA1 {
|
||||
cost: usize,
|
||||
salt: Base64UrlSafeData,
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
PBKDF2_SHA512 {
|
||||
cost: usize,
|
||||
salt: Base64UrlSafeData,
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
SHA1 {
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
SSHA1 {
|
||||
salt: Base64UrlSafeData,
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
SHA256 {
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
SSHA256 {
|
||||
salt: Base64UrlSafeData,
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
SHA512 {
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
SSHA512 {
|
||||
salt: Base64UrlSafeData,
|
||||
hash: Base64UrlSafeData,
|
||||
},
|
||||
NT_MD4 {
|
||||
hash: Base64UrlSafeData,
|
||||
CRYPT_SHA512 {
|
||||
h: String,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -214,6 +163,9 @@ impl fmt::Debug for DbPasswordV1 {
|
|||
DbPasswordV1::SHA512(_) => write!(f, "SHA512"),
|
||||
DbPasswordV1::SSHA512(_, _) => write!(f, "SSHA512"),
|
||||
DbPasswordV1::NT_MD4(_) => write!(f, "NT_MD4"),
|
||||
DbPasswordV1::CRYPT_MD5 { .. } => write!(f, "CRYPT_MD5"),
|
||||
DbPasswordV1::CRYPT_SHA256 { .. } => write!(f, "CRYPT_SHA256"),
|
||||
DbPasswordV1::CRYPT_SHA512 { .. } => write!(f, "CRYPT_SHA512"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -436,6 +388,16 @@ enum Kdf {
|
|||
SSHA512(Vec<u8>, Vec<u8>),
|
||||
// hash
|
||||
NT_MD4(Vec<u8>),
|
||||
CRYPT_MD5 {
|
||||
s: Vec<u8>,
|
||||
h: Vec<u8>,
|
||||
},
|
||||
CRYPT_SHA256 {
|
||||
h: String,
|
||||
},
|
||||
CRYPT_SHA512 {
|
||||
h: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -498,78 +460,17 @@ impl TryFrom<DbPasswordV1> for Password {
|
|||
DbPasswordV1::NT_MD4(h) => Ok(Password {
|
||||
material: Kdf::NT_MD4(h),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&ReplPasswordV1> for Password {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &ReplPasswordV1) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
ReplPasswordV1::TPM_ARGON2ID {
|
||||
m_cost,
|
||||
t_cost,
|
||||
p_cost,
|
||||
version,
|
||||
salt,
|
||||
key,
|
||||
} => Ok(Password {
|
||||
material: Kdf::TPM_ARGON2ID {
|
||||
m_cost: *m_cost,
|
||||
t_cost: *t_cost,
|
||||
p_cost: *p_cost,
|
||||
version: *version,
|
||||
salt: salt.to_vec(),
|
||||
key: key.to_vec(),
|
||||
DbPasswordV1::CRYPT_MD5 { s, h } => Ok(Password {
|
||||
material: Kdf::CRYPT_MD5 {
|
||||
s: s.into(),
|
||||
h: h.into(),
|
||||
},
|
||||
}),
|
||||
ReplPasswordV1::ARGON2ID {
|
||||
m_cost,
|
||||
t_cost,
|
||||
p_cost,
|
||||
version,
|
||||
salt,
|
||||
key,
|
||||
} => Ok(Password {
|
||||
material: Kdf::ARGON2ID {
|
||||
m_cost: *m_cost,
|
||||
t_cost: *t_cost,
|
||||
p_cost: *p_cost,
|
||||
version: *version,
|
||||
salt: salt.to_vec(),
|
||||
key: key.to_vec(),
|
||||
},
|
||||
DbPasswordV1::CRYPT_SHA256 { h } => Ok(Password {
|
||||
material: Kdf::CRYPT_SHA256 { h },
|
||||
}),
|
||||
ReplPasswordV1::PBKDF2 { cost, salt, hash } => Ok(Password {
|
||||
material: Kdf::PBKDF2(*cost, salt.to_vec(), hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::PBKDF2_SHA1 { cost, salt, hash } => Ok(Password {
|
||||
material: Kdf::PBKDF2_SHA1(*cost, salt.to_vec(), hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::PBKDF2_SHA512 { cost, salt, hash } => Ok(Password {
|
||||
material: Kdf::PBKDF2_SHA512(*cost, salt.to_vec(), hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::SHA1 { hash } => Ok(Password {
|
||||
material: Kdf::SHA1(hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::SSHA1 { salt, hash } => Ok(Password {
|
||||
material: Kdf::SSHA1(salt.to_vec(), hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::SHA256 { hash } => Ok(Password {
|
||||
material: Kdf::SHA256(hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::SSHA256 { salt, hash } => Ok(Password {
|
||||
material: Kdf::SSHA256(salt.to_vec(), hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::SHA512 { hash } => Ok(Password {
|
||||
material: Kdf::SHA512(hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::SSHA512 { salt, hash } => Ok(Password {
|
||||
material: Kdf::SSHA512(salt.to_vec(), hash.to_vec()),
|
||||
}),
|
||||
ReplPasswordV1::NT_MD4 { hash } => Ok(Password {
|
||||
material: Kdf::NT_MD4(hash.to_vec()),
|
||||
DbPasswordV1::CRYPT_SHA512 { h } => Ok(Password {
|
||||
material: Kdf::CRYPT_SHA256 { h },
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -665,6 +566,40 @@ impl TryFrom<&str> for Password {
|
|||
// Test 389ds/openldap formats. Shout outs openldap which sometimes makes these
|
||||
// lowercase.
|
||||
|
||||
if let Some(crypt) = value
|
||||
.strip_prefix("{crypt}")
|
||||
.or_else(|| value.strip_prefix("{CRYPT}"))
|
||||
{
|
||||
if let Some(crypt_md5_phc) = crypt.strip_prefix("$1$") {
|
||||
let (salt, hash) = crypt_md5_phc.split_once('$').ok_or(())?;
|
||||
|
||||
// These are a hash64 format, so leave them as bytes, don't try
|
||||
// to decode.
|
||||
let s = salt.as_bytes().to_vec();
|
||||
let h = hash.as_bytes().to_vec();
|
||||
|
||||
return Ok(Password {
|
||||
material: Kdf::CRYPT_MD5 { s, h },
|
||||
});
|
||||
}
|
||||
|
||||
if crypt.starts_with("$5$") {
|
||||
return Ok(Password {
|
||||
material: Kdf::CRYPT_SHA256 {
|
||||
h: crypt.to_string(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if crypt.starts_with("$6$") {
|
||||
return Ok(Password {
|
||||
material: Kdf::CRYPT_SHA512 {
|
||||
h: crypt.to_string(),
|
||||
},
|
||||
});
|
||||
}
|
||||
} // End crypt
|
||||
|
||||
if let Some(ds_ssha1) = value
|
||||
.strip_prefix("{SHA}")
|
||||
.or_else(|| value.strip_prefix("{sha}"))
|
||||
|
@ -1242,6 +1177,20 @@ impl Password {
|
|||
})
|
||||
.map(|chal_key| chal_key.as_ref() == key)
|
||||
}
|
||||
(Kdf::CRYPT_MD5 { s, h }, _) => {
|
||||
let chal_key = crypt_md5::do_md5_crypt(cleartext.as_bytes(), s);
|
||||
Ok(chal_key == *h)
|
||||
}
|
||||
(Kdf::CRYPT_SHA256 { h }, _) => {
|
||||
let is_valid = sha_crypt::sha256_check(cleartext, h.as_str()).is_ok();
|
||||
|
||||
Ok(is_valid)
|
||||
}
|
||||
(Kdf::CRYPT_SHA512 { h }, _) => {
|
||||
let is_valid = sha_crypt::sha512_check(cleartext, h.as_str()).is_ok();
|
||||
|
||||
Ok(is_valid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1293,80 +1242,12 @@ impl Password {
|
|||
Kdf::SHA512(hash) => DbPasswordV1::SHA512(hash.clone()),
|
||||
Kdf::SSHA512(salt, hash) => DbPasswordV1::SSHA512(salt.clone(), hash.clone()),
|
||||
Kdf::NT_MD4(hash) => DbPasswordV1::NT_MD4(hash.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_repl_v1(&self) -> ReplPasswordV1 {
|
||||
match &self.material {
|
||||
Kdf::TPM_ARGON2ID {
|
||||
m_cost,
|
||||
t_cost,
|
||||
p_cost,
|
||||
version,
|
||||
salt,
|
||||
key,
|
||||
} => ReplPasswordV1::TPM_ARGON2ID {
|
||||
m_cost: *m_cost,
|
||||
t_cost: *t_cost,
|
||||
p_cost: *p_cost,
|
||||
version: *version,
|
||||
salt: salt.clone().into(),
|
||||
key: key.clone().into(),
|
||||
},
|
||||
Kdf::ARGON2ID {
|
||||
m_cost,
|
||||
t_cost,
|
||||
p_cost,
|
||||
version,
|
||||
salt,
|
||||
key,
|
||||
} => ReplPasswordV1::ARGON2ID {
|
||||
m_cost: *m_cost,
|
||||
t_cost: *t_cost,
|
||||
p_cost: *p_cost,
|
||||
version: *version,
|
||||
salt: salt.clone().into(),
|
||||
key: key.clone().into(),
|
||||
},
|
||||
Kdf::PBKDF2(cost, salt, hash) => ReplPasswordV1::PBKDF2 {
|
||||
cost: *cost,
|
||||
salt: salt.clone().into(),
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::PBKDF2_SHA1(cost, salt, hash) => ReplPasswordV1::PBKDF2_SHA1 {
|
||||
cost: *cost,
|
||||
salt: salt.clone().into(),
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::PBKDF2_SHA512(cost, salt, hash) => ReplPasswordV1::PBKDF2_SHA512 {
|
||||
cost: *cost,
|
||||
salt: salt.clone().into(),
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::SHA1(hash) => ReplPasswordV1::SHA1 {
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::SSHA1(salt, hash) => ReplPasswordV1::SSHA1 {
|
||||
salt: salt.clone().into(),
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::SHA256(hash) => ReplPasswordV1::SHA256 {
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::SSHA256(salt, hash) => ReplPasswordV1::SSHA256 {
|
||||
salt: salt.clone().into(),
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::SHA512(hash) => ReplPasswordV1::SHA512 {
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::SSHA512(salt, hash) => ReplPasswordV1::SSHA512 {
|
||||
salt: salt.clone().into(),
|
||||
hash: hash.clone().into(),
|
||||
},
|
||||
Kdf::NT_MD4(hash) => ReplPasswordV1::NT_MD4 {
|
||||
hash: hash.clone().into(),
|
||||
Kdf::CRYPT_MD5 { s, h } => DbPasswordV1::CRYPT_MD5 {
|
||||
s: s.clone().into(),
|
||||
h: h.clone().into(),
|
||||
},
|
||||
Kdf::CRYPT_SHA256 { h } => DbPasswordV1::CRYPT_SHA256 { h: h.clone() },
|
||||
Kdf::CRYPT_SHA512 { h } => DbPasswordV1::CRYPT_SHA512 { h: h.clone() },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1402,7 +1283,10 @@ impl Password {
|
|||
| Kdf::SSHA256(_, _)
|
||||
| Kdf::SHA512(_)
|
||||
| Kdf::SSHA512(_, _)
|
||||
| Kdf::NT_MD4(_) => true,
|
||||
| Kdf::NT_MD4(_)
|
||||
| Kdf::CRYPT_MD5 { .. }
|
||||
| Kdf::CRYPT_SHA256 { .. }
|
||||
| Kdf::CRYPT_SHA512 { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1660,6 +1544,39 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_password_from_crypt_md5() {
|
||||
sketching::test_init();
|
||||
let im_pw = "{crypt}$1$zaRIAsoe$7887GzjDTrst0XbDPpF5m.";
|
||||
let password = "password";
|
||||
let r = Password::try_from(im_pw).expect("Failed to parse");
|
||||
|
||||
assert!(r.requires_upgrade());
|
||||
assert!(r.verify(password).unwrap_or(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_password_from_crypt_sha256() {
|
||||
sketching::test_init();
|
||||
let im_pw = "{crypt}$5$3UzV7Sut8EHCUxlN$41V.jtMQmFAOucqI4ImFV43r.bRLjPlN.hyfoCdmGE2";
|
||||
let password = "password";
|
||||
let r = Password::try_from(im_pw).expect("Failed to parse");
|
||||
|
||||
assert!(r.requires_upgrade());
|
||||
assert!(r.verify(password).unwrap_or(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_password_from_crypt_sha512() {
|
||||
sketching::test_init();
|
||||
let im_pw = "{crypt}$6$aXn8azL8DXUyuMvj$9aJJC/KEUwygIpf2MTqjQa.f0MEXNg2cGFc62Fet8XpuDVDedM05CweAlxW6GWxnmHqp14CRf6zU7OQoE/bCu0";
|
||||
let password = "password";
|
||||
let r = Password::try_from(im_pw).expect("Failed to parse");
|
||||
|
||||
assert!(r.requires_upgrade());
|
||||
assert!(r.verify(password).unwrap_or(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_password_argon2id_hsm_bind() {
|
||||
sketching::test_init();
|
||||
|
|
180
pykanidm/poetry.lock
generated
180
pykanidm/poetry.lock
generated
|
@ -427,68 +427,74 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.6.11"
|
||||
version = "7.6.12"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "coverage-7.6.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eafea49da254a8289bed3fab960f808b322eda5577cb17a3733014928bbfbebd"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a3f7cbbcb4ad95067a6525f83a6fc78d9cbc1e70f8abaeeaeaa72ef34f48fc3"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6b079b39246a7da9a40cfa62d5766bd52b4b7a88cf5a82ec4c45bf6e152306"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60d4ad09dfc8c36c4910685faafcb8044c84e4dae302e86c585b3e2e7778726c"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e433b6e3a834a43dae2889adc125f3fa4c66668df420d8e49bc4ee817dd7a70"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ac5d92e2cc121a13270697e4cb37e1eb4511ac01d23fe1b6c097facc3b46489e"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5128f3ba694c0a1bde55fc480090392c336236c3e1a10dad40dc1ab17c7675ff"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:397489c611b76302dfa1d9ea079e138dddc4af80fc6819d5f5119ec8ca6c0e47"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-win32.whl", hash = "sha256:c7719a5e1dc93883a6b319bc0374ecd46fb6091ed659f3fbe281ab991634b9b0"},
|
||||
{file = "coverage-7.6.11-cp310-cp310-win_amd64.whl", hash = "sha256:c27df03730059118b8a923cfc8b84b7e9976742560af528242f201880879c1da"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:532fe139691af134aa8b54ed60dd3c806aa81312d93693bd2883c7b61592c840"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0b0f272901a5172090c0802053fbc503cdc3fa2612720d2669a98a7384a7bec"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4bda710139ea646890d1c000feb533caff86904a0e0638f85e967c28cb8eec50"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a165b09e7d5f685bf659063334a9a7b1a2d57b531753d3e04bd442b3cfe5845b"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ff136607689c1c87f43d24203b6d2055b42030f352d5176f9c8b204d4235ef27"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:050172741de03525290e67f0161ae5f7f387c88fca50d47fceb4724ceaa591d2"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-win32.whl", hash = "sha256:27700d859be68e4fb2e7bf774cf49933dcac6f81a9bc4c13bd41735b8d26a53b"},
|
||||
{file = "coverage-7.6.11-cp311-cp311-win_amd64.whl", hash = "sha256:cd4839813b09ab1dd1be1bbc74f9a7787615f931f83952b6a9af1b2d3f708bf7"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dbb1a822fd858d9853333a7c95d4e70dde9a79e65893138ce32c2ec6457d7a36"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61c834cbb80946d6ebfddd9b393a4c46bec92fcc0fa069321fcb8049117f76ea"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a46d56e99a31d858d6912d31ffa4ede6a325c86af13139539beefca10a1234ce"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b48db06f53d1864fea6dbd855e6d51d41c0f06c212c3004511c0bdc6847b297"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6ff5be3b1853e0862da9d349fe87f869f68e63a25f7c37ce1130b321140f963"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be05bde21d5e6eefbc3a6de6b9bee2b47894b8945342e8663192809c4d1f08ce"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-win32.whl", hash = "sha256:e3b746fa0ffc5b6b8856529de487da8b9aeb4fb394bb58de6502ef45f3434f12"},
|
||||
{file = "coverage-7.6.11-cp312-cp312-win_amd64.whl", hash = "sha256:ac476e6d0128fb7919b3fae726de72b28b5c9644cb4b579e4a523d693187c551"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c86f4c7a6d1a54a24d804d9684d96e36a62d3ef7c0d7745ae2ea39e3e0293251"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7eb0504bb307401fd08bc5163a351df301438b3beb88a4fa044681295bbefc67"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca95d40900cf614e07f00cee8c2fad0371df03ca4d7a80161d84be2ec132b7a4"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db4b1a69976b1b02acda15937538a1d3fe10b185f9d99920b17a740a0a102e06"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf96beb05d004e4c51cd846fcdf9eee9eb2681518524b66b2e7610507944c2f"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:08e5fb93576a6b054d3d326242af5ef93daaac9bb52bc25f12ccbc3fa94227cd"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25575cd5a7d2acc46b42711e8aff826027c0e4f80fb38028a74f31ac22aae69d"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8fa4fffd90ee92f62ff7404b4801b59e8ea8502e19c9bf2d3241ce745b52926c"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-win32.whl", hash = "sha256:0d03c9452d9d1ccfe5d3a5df0427705022a49b356ac212d529762eaea5ef97b4"},
|
||||
{file = "coverage-7.6.11-cp313-cp313-win_amd64.whl", hash = "sha256:fd2fffc8ce8692ce540103dff26279d2af22d424516ddebe2d7e4d6dbb3816b2"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:5e7ac966ab110bd94ee844f2643f196d78fde1cd2450399116d3efdd706e19f5"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ba27a0375c5ef4d2a7712f829265102decd5ff78b96d342ac2fa555742c4f4f"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2778be4f574b39ec9dcd9e5e13644f770351ee0990a0ecd27e364aba95af89b"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5edc16712187139ab635a2e644cc41fc239bc6d245b16124045743130455c652"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6ff122a0a10a30121d9f0cb3fbd03a6fe05861e4ec47adb9f25e9245aabc19"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff562952f15eff27247a4c4b03e45ce8a82e3fb197de6a7c54080f9d4ba07845"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4f21e3617f48d683f30cf2a6c8b739c838e600cb1454fe6b2eb486ac2bce8fbd"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6d60577673ba48d8ae8e362e61fd4ad1a640293ffe8991d11c86f195479100b7"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-win32.whl", hash = "sha256:13100f98497086b359bf56fc035a762c674de8ef526daa389ac8932cb9bff1e0"},
|
||||
{file = "coverage-7.6.11-cp313-cp313t-win_amd64.whl", hash = "sha256:2c81e53782043b323bd34c7de711ed9b4673414eb517eaf35af92185b873839c"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff52b4e2ac0080c96e506819586c4b16cdbf46724bda90d308a7330a73cc8521"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f4679fcc9eb9004fdd1b00231ef1ec7167168071bebc4d66327e28c1979b4449"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90de4e9ca4489e823138bd13098af9ac8028cc029f33f60098b5c08c675c7bda"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c96a142057d83ee993eaf71629ca3fb952cda8afa9a70af4132950c2bd3deb9"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:476f29a258b9cd153f2be5bf5f119d670d2806363595263917bddc167d6e5cce"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:09d03f48d9025b8a6a116cddcb6c7b8ce80e4fb4c31dd2e124a7c377036ad58e"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-win32.whl", hash = "sha256:bb35ae9f134fbd9cf7302a9654d5a1e597c974202678082dcc569eb39a8cde03"},
|
||||
{file = "coverage-7.6.11-cp39-cp39-win_amd64.whl", hash = "sha256:f382004fa4c93c01016d9226b9d696a08c53f6818b7ad59b4e96cb67e863353a"},
|
||||
{file = "coverage-7.6.11-pp39.pp310-none-any.whl", hash = "sha256:adc2d941c0381edfcf3897f94b9f41b1e504902fab78a04b1677f2f72afead4b"},
|
||||
{file = "coverage-7.6.11-py3-none-any.whl", hash = "sha256:f0f334ae844675420164175bf32b04e18a81fe57ad8eb7e0cfd4689d681ffed7"},
|
||||
{file = "coverage-7.6.11.tar.gz", hash = "sha256:e642e6a46a04e992ebfdabed79e46f478ec60e2c528e1e1a074d63800eda4286"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425"},
|
||||
{file = "coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f"},
|
||||
{file = "coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95"},
|
||||
{file = "coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3"},
|
||||
{file = "coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3"},
|
||||
{file = "coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31"},
|
||||
{file = "coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57"},
|
||||
{file = "coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf"},
|
||||
{file = "coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953"},
|
||||
{file = "coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
@ -986,13 +992,13 @@ pyyaml = ">=5.1"
|
|||
|
||||
[[package]]
|
||||
name = "mkdocs-material"
|
||||
version = "9.6.3"
|
||||
version = "9.6.5"
|
||||
description = "Documentation that simply works"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mkdocs_material-9.6.3-py3-none-any.whl", hash = "sha256:1125622067e26940806701219303b27c0933e04533560725d97ec26fd16a39cf"},
|
||||
{file = "mkdocs_material-9.6.3.tar.gz", hash = "sha256:c87f7d1c39ce6326da5e10e232aed51bae46252e646755900f4b0fc9192fa832"},
|
||||
{file = "mkdocs_material-9.6.5-py3-none-any.whl", hash = "sha256:aad3e6fb860c20870f75fb2a69ef901f1be727891e41adb60b753efcae19453b"},
|
||||
{file = "mkdocs_material-9.6.5.tar.gz", hash = "sha256:b714679a8c91b0ffe2188e11ed58c44d2523e9c2ae26a29cc652fa7478faa21f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1026,13 +1032,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "mkdocstrings"
|
||||
version = "0.28.0"
|
||||
version = "0.28.1"
|
||||
description = "Automatic documentation from sources, for MkDocs."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "mkdocstrings-0.28.0-py3-none-any.whl", hash = "sha256:84cf3dc910614781fe0fee46ce8006fde7df6cc7cca2e3f799895fb8a9170b39"},
|
||||
{file = "mkdocstrings-0.28.0.tar.gz", hash = "sha256:df20afef1eafe36ba466ae20732509ecb74237653a585f5061937e54b553b4e0"},
|
||||
{file = "mkdocstrings-0.28.1-py3-none-any.whl", hash = "sha256:a5878ae5cd1e26f491ff084c1f9ab995687d52d39a5c558e9b7023d0e4e0b740"},
|
||||
{file = "mkdocstrings-0.28.1.tar.gz", hash = "sha256:fb64576906771b7701e8e962fd90073650ff689e95eb86e86751a66d65ab4489"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1053,13 +1059,13 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
|
|||
|
||||
[[package]]
|
||||
name = "mkdocstrings-python"
|
||||
version = "1.14.6"
|
||||
version = "1.16.1"
|
||||
description = "A Python handler for mkdocstrings."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "mkdocstrings_python-1.14.6-py3-none-any.whl", hash = "sha256:e0ca11b49ac0f23070afb566245f4ff80ea1700e03c9dbc143d70dbf1cae074e"},
|
||||
{file = "mkdocstrings_python-1.14.6.tar.gz", hash = "sha256:3fb6589491614422d781dacca085c0c5a53a7063af37a2fea5864e24e378b03e"},
|
||||
{file = "mkdocstrings_python-1.16.1-py3-none-any.whl", hash = "sha256:b88ff6fc6a293cee9cb42313f1cba37a2c5cdf37bcc60b241ec7ab66b5d41b58"},
|
||||
{file = "mkdocstrings_python-1.16.1.tar.gz", hash = "sha256:d7152d17da74d3616a0f17df5d2da771ecf7340518c158650e5a64a0a95973f4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -2074,29 +2080,29 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.9.5"
|
||||
version = "0.9.7"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.9.5-py3-none-linux_armv6l.whl", hash = "sha256:d466d2abc05f39018d53f681fa1c0ffe9570e6d73cde1b65d23bb557c846f442"},
|
||||
{file = "ruff-0.9.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38840dbcef63948657fa7605ca363194d2fe8c26ce8f9ae12eee7f098c85ac8a"},
|
||||
{file = "ruff-0.9.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d56ba06da53536b575fbd2b56517f6f95774ff7be0f62c80b9e67430391eeb36"},
|
||||
{file = "ruff-0.9.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7cb2a01da08244c50b20ccfaeb5972e4228c3c3a1989d3ece2bc4b1f996001"},
|
||||
{file = "ruff-0.9.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:96d5c76358419bc63a671caac70c18732d4fd0341646ecd01641ddda5c39ca0b"},
|
||||
{file = "ruff-0.9.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:deb8304636ed394211f3a6d46c0e7d9535b016f53adaa8340139859b2359a070"},
|
||||
{file = "ruff-0.9.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df455000bf59e62b3e8c7ba5ed88a4a2bc64896f900f311dc23ff2dc38156440"},
|
||||
{file = "ruff-0.9.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de92170dfa50c32a2b8206a647949590e752aca8100a0f6b8cefa02ae29dce80"},
|
||||
{file = "ruff-0.9.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d28532d73b1f3f627ba88e1456f50748b37f3a345d2be76e4c653bec6c3e393"},
|
||||
{file = "ruff-0.9.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c746d7d1df64f31d90503ece5cc34d7007c06751a7a3bbeee10e5f2463d52d2"},
|
||||
{file = "ruff-0.9.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11417521d6f2d121fda376f0d2169fb529976c544d653d1d6044f4c5562516ee"},
|
||||
{file = "ruff-0.9.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b9d71c3879eb32de700f2f6fac3d46566f644a91d3130119a6378f9312a38e1"},
|
||||
{file = "ruff-0.9.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2e36c61145e70febcb78483903c43444c6b9d40f6d2f800b5552fec6e4a7bb9a"},
|
||||
{file = "ruff-0.9.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2f71d09aeba026c922aa7aa19a08d7bd27c867aedb2f74285a2639644c1c12f5"},
|
||||
{file = "ruff-0.9.5-py3-none-win32.whl", hash = "sha256:134f958d52aa6fdec3b294b8ebe2320a950d10c041473c4316d2e7d7c2544723"},
|
||||
{file = "ruff-0.9.5-py3-none-win_amd64.whl", hash = "sha256:78cc6067f6d80b6745b67498fb84e87d32c6fc34992b52bffefbdae3442967d6"},
|
||||
{file = "ruff-0.9.5-py3-none-win_arm64.whl", hash = "sha256:18a29f1a005bddb229e580795627d297dfa99f16b30c7039e73278cf6b5f9fa9"},
|
||||
{file = "ruff-0.9.5.tar.gz", hash = "sha256:11aecd7a633932875ab3cb05a484c99970b9d52606ce9ea912b690b02653d56c"},
|
||||
{file = "ruff-0.9.7-py3-none-linux_armv6l.whl", hash = "sha256:99d50def47305fe6f233eb8dabfd60047578ca87c9dcb235c9723ab1175180f4"},
|
||||
{file = "ruff-0.9.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d59105ae9c44152c3d40a9c40d6331a7acd1cdf5ef404fbe31178a77b174ea66"},
|
||||
{file = "ruff-0.9.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f313b5800483770bd540cddac7c90fc46f895f427b7820f18fe1822697f1fec9"},
|
||||
{file = "ruff-0.9.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042ae32b41343888f59c0a4148f103208bf6b21c90118d51dc93a68366f4e903"},
|
||||
{file = "ruff-0.9.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87862589373b33cc484b10831004e5e5ec47dc10d2b41ba770e837d4f429d721"},
|
||||
{file = "ruff-0.9.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a17e1e01bee0926d351a1ee9bc15c445beae888f90069a6192a07a84af544b6b"},
|
||||
{file = "ruff-0.9.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7c1f880ac5b2cbebd58b8ebde57069a374865c73f3bf41f05fe7a179c1c8ef22"},
|
||||
{file = "ruff-0.9.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e63fc20143c291cab2841dbb8260e96bafbe1ba13fd3d60d28be2c71e312da49"},
|
||||
{file = "ruff-0.9.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91ff963baed3e9a6a4eba2a02f4ca8eaa6eba1cc0521aec0987da8d62f53cbef"},
|
||||
{file = "ruff-0.9.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88362e3227c82f63eaebf0b2eff5b88990280fb1ecf7105523883ba8c3aaf6fb"},
|
||||
{file = "ruff-0.9.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0372c5a90349f00212270421fe91874b866fd3626eb3b397ede06cd385f6f7e0"},
|
||||
{file = "ruff-0.9.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d76b8ab60e99e6424cd9d3d923274a1324aefce04f8ea537136b8398bbae0a62"},
|
||||
{file = "ruff-0.9.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0c439bdfc8983e1336577f00e09a4e7a78944fe01e4ea7fe616d00c3ec69a3d0"},
|
||||
{file = "ruff-0.9.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:115d1f15e8fdd445a7b4dc9a30abae22de3f6bcabeb503964904471691ef7606"},
|
||||
{file = "ruff-0.9.7-py3-none-win32.whl", hash = "sha256:e9ece95b7de5923cbf38893f066ed2872be2f2f477ba94f826c8defdd6ec6b7d"},
|
||||
{file = "ruff-0.9.7-py3-none-win_amd64.whl", hash = "sha256:3770fe52b9d691a15f0b87ada29c45324b2ace8f01200fb0c14845e499eb0c2c"},
|
||||
{file = "ruff-0.9.7-py3-none-win_arm64.whl", hash = "sha256:b075a700b2533feb7a01130ff656a4ec0d5f340bb540ad98759b8401c32c2037"},
|
||||
{file = "ruff-0.9.7.tar.gz", hash = "sha256:643757633417907510157b206e490c3aa11cab0c087c912f60e07fbafa87a4c6"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2367,4 +2373,4 @@ type = ["pytest-mypy"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "f614f512e5cfcf28eb72369010b689568f2736563e498bd6cf252292eb8bf08c"
|
||||
content-hash = "72e84e903994ae033467c589c6506cc54fc9c3075df75795b4d0f748ff91dfeb"
|
||||
|
|
|
@ -29,7 +29,7 @@ Authlib = "^1.2.0"
|
|||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = ">=0.5.1,<0.9.6"
|
||||
ruff = ">=0.5.1,<0.9.8"
|
||||
pytest = "^8.3.4"
|
||||
mypy = "^1.14.1"
|
||||
types-requests = "^2.32.0.20241016"
|
||||
|
|
|
@ -54,20 +54,43 @@ RUN --mount=type=cache,id=cargo,target=/cargo \
|
|||
--release; \
|
||||
sccache -s
|
||||
|
||||
# Find and copy dynamically linked libraries using ldd
|
||||
# caveat: this actually partially runs the binary, so it doesn't work for cross-compilation
|
||||
RUN <<EOF
|
||||
mkdir -p /out/libs
|
||||
mkdir -p /out/libs-root
|
||||
ldd /usr/src/kanidm/target/release/kanidmd
|
||||
ldd /usr/src/kanidm/target/release/kanidmd | grep -v 'linux-vdso.so' | awk '{print $(NF-1) " " $1}' | sort -u -k 1,1 | awk '{print "install", "-D", $1, (($2 ~ /^\//) ? "/out/libs-root" $2 : "/out/libs/" $2)}' | xargs -I {} sh -c {}
|
||||
ls -Rla /out/libs
|
||||
ls -Rla /out/libs-root
|
||||
EOF
|
||||
|
||||
# ======================
|
||||
|
||||
FROM repos
|
||||
RUN \
|
||||
--mount=type=cache,id=zypp,target=/var/cache/zypp \
|
||||
zypper install -y \
|
||||
timezone \
|
||||
openssl-3 \
|
||||
sqlite3 \
|
||||
pam
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /usr/src/kanidm/target/release/kanidmd /sbin/
|
||||
COPY --from=builder /usr/src/kanidm/server/core/static /hpkg
|
||||
RUN chmod +x /sbin/kanidmd
|
||||
WORKDIR /
|
||||
|
||||
# Copy root certs for tls into image
|
||||
# You can also mount the certs from the host
|
||||
# --volume /etc/ssl/certs:/etc/ssl/certs:ro
|
||||
COPY --from=repos /etc/ssl/certs /etc/ssl/certs
|
||||
|
||||
# Copy our build
|
||||
COPY --from=builder --chmod=0755 /usr/src/kanidm/target/release/kanidmd /sbin/
|
||||
# Web assets
|
||||
COPY --from=builder /usr/src/kanidm/server/core/static /hpkg/
|
||||
|
||||
# Copy fixed-path dynamic libraries to their position
|
||||
COPY --from=builder /out/libs-root/ /
|
||||
COPY --from=builder /out/libs/ /lib/
|
||||
|
||||
# Inform loader where to find libraries
|
||||
# This is necessary because opensuse searches for libraries in /lib64 or /lib depending on the architecture, but we don't know which one we're on.
|
||||
# Alternatively, we could symlink /lib64 to /lib, and /usr/lib64 to /usr/lib, etc.
|
||||
# We could always fix this by invoking the loader on the host (which works in a cross build it seems), but this is easier.
|
||||
# On debian, it always searches for libraries in /lib.
|
||||
ENV LD_LIBRARY_PATH=/lib
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
|
|
16
server/testkit/tests/testkit/ldap_basic.rs
Normal file
16
server/testkit/tests/testkit/ldap_basic.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use kanidmd_testkit::AsyncTestEnvironment;
|
||||
use ldap3_client::LdapClientBuilder;
|
||||
|
||||
#[kanidmd_testkit::test(ldap = true)]
|
||||
async fn test_ldap_basic_unix_bind(test_env: &AsyncTestEnvironment) {
|
||||
let ldap_url = test_env.ldap_url.as_ref().unwrap();
|
||||
|
||||
let mut ldap_client = LdapClientBuilder::new(ldap_url).build().await.unwrap();
|
||||
|
||||
// Bind as anonymous
|
||||
ldap_client.bind("".into(), "".into()).await.unwrap();
|
||||
|
||||
let whoami = ldap_client.whoami().await.unwrap();
|
||||
|
||||
assert_eq!(whoami, Some("u: anonymous@localhost".to_string()));
|
||||
}
|
Loading…
Reference in a new issue