Skip to content

Commit ac463a7

Browse files
kanidm: init at 1.6.2, make 1.6.2 default (#405335)
2 parents 0719ce0 + 55e6dbd commit ac463a7

File tree

5 files changed

+294
-2
lines changed

5 files changed

+294
-2
lines changed

pkgs/by-name/ka/kanidm/1_6.nix

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import ./generic.nix {
2+
version = "1.6.2";
3+
hash = "sha256-rfQNx6yAj1mDW7UL8mz01TqMAET9D5fL02JhHeN5zV4=";
4+
cargoHash = "sha256-3XUAwuRKtdnMNhH92lgwgeN2rMmzgqir1+OZNaTGmks=";
5+
patchDir = ./patches/1_6;
6+
}

pkgs/by-name/ka/kanidm/package.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
import ./1_5.nix
1+
import ./1_6.nix
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
From fc26fe5ac9e9cd65af82609c5a4966c8f756ea0f Mon Sep 17 00:00:00 2001
2+
From: oddlama <[email protected]>
3+
Date: Fri, 21 Mar 2025 16:07:54 +0100
4+
Subject: [PATCH 1/2] oauth2 basic secret modify
5+
6+
---
7+
server/core/src/actors/v1_write.rs | 42 +++++++++++++++++++++++++++++
8+
server/core/src/https/v1.rs | 6 ++++-
9+
server/core/src/https/v1_oauth2.rs | 29 ++++++++++++++++++++
10+
server/lib/src/server/migrations.rs | 16 +++++++++++
11+
4 files changed, 92 insertions(+), 1 deletion(-)
12+
13+
diff --git a/server/core/src/actors/v1_write.rs b/server/core/src/actors/v1_write.rs
14+
index 732e826c8..a2b8e503f 100644
15+
--- a/server/core/src/actors/v1_write.rs
16+
+++ b/server/core/src/actors/v1_write.rs
17+
@@ -324,6 +324,48 @@ impl QueryServerWriteV1 {
18+
.and_then(|_| idms_prox_write.commit().map(|_| ()))
19+
}
20+
21+
+ #[instrument(
22+
+ level = "info",
23+
+ skip_all,
24+
+ fields(uuid = ?eventid)
25+
+ )]
26+
+ pub async fn handle_oauth2_basic_secret_write(
27+
+ &self,
28+
+ client_auth_info: ClientAuthInfo,
29+
+ filter: Filter<FilterInvalid>,
30+
+ new_secret: String,
31+
+ eventid: Uuid,
32+
+ ) -> Result<(), OperationError> {
33+
+ // Given a protoEntry, turn this into a modification set.
34+
+ let ct = duration_from_epoch_now();
35+
+ let mut idms_prox_write = self.idms.proxy_write(ct).await?;
36+
+ let ident = idms_prox_write
37+
+ .validate_client_auth_info_to_ident(client_auth_info, ct)
38+
+ .map_err(|e| {
39+
+ admin_error!(err = ?e, "Invalid identity");
40+
+ e
41+
+ })?;
42+
+
43+
+ let modlist = ModifyList::new_purge_and_set(
44+
+ Attribute::OAuth2RsBasicSecret,
45+
+ Value::SecretValue(new_secret),
46+
+ );
47+
+
48+
+ let mdf =
49+
+ ModifyEvent::from_internal_parts(ident, &modlist, &filter, &idms_prox_write.qs_write)
50+
+ .map_err(|e| {
51+
+ admin_error!(err = ?e, "Failed to begin modify during handle_oauth2_basic_secret_write");
52+
+ e
53+
+ })?;
54+
+
55+
+ trace!(?mdf, "Begin modify event");
56+
+
57+
+ idms_prox_write
58+
+ .qs_write
59+
+ .modify(&mdf)
60+
+ .and_then(|_| idms_prox_write.commit())
61+
+ }
62+
+
63+
#[instrument(
64+
level = "info",
65+
skip_all,
66+
diff --git a/server/core/src/https/v1.rs b/server/core/src/https/v1.rs
67+
index 30de387b8..a11aa8ecd 100644
68+
--- a/server/core/src/https/v1.rs
69+
+++ b/server/core/src/https/v1.rs
70+
@@ -4,7 +4,7 @@ use axum::extract::{Path, State};
71+
use axum::http::{HeaderMap, HeaderValue};
72+
use axum::middleware::from_fn;
73+
use axum::response::{IntoResponse, Response};
74+
-use axum::routing::{delete, get, post, put};
75+
+use axum::routing::{delete, get, post, put, patch};
76+
use axum::{Extension, Json, Router};
77+
use axum_extra::extract::cookie::{Cookie, CookieJar, SameSite};
78+
use compact_jwt::{Jwk, Jws, JwsSigner};
79+
@@ -3129,6 +3129,10 @@ pub(crate) fn route_setup(state: ServerState) -> Router<ServerState> {
80+
"/v1/oauth2/:rs_name/_basic_secret",
81+
get(super::v1_oauth2::oauth2_id_get_basic_secret),
82+
)
83+
+ .route(
84+
+ "/v1/oauth2/:rs_name/_basic_secret",
85+
+ patch(super::v1_oauth2::oauth2_id_patch_basic_secret),
86+
+ )
87+
.route(
88+
"/v1/oauth2/:rs_name/_scopemap/:group",
89+
post(super::v1_oauth2::oauth2_id_scopemap_post)
90+
diff --git a/server/core/src/https/v1_oauth2.rs b/server/core/src/https/v1_oauth2.rs
91+
index f399539bc..ffad9921e 100644
92+
--- a/server/core/src/https/v1_oauth2.rs
93+
+++ b/server/core/src/https/v1_oauth2.rs
94+
@@ -151,6 +151,35 @@ pub(crate) async fn oauth2_id_get_basic_secret(
95+
.map_err(WebError::from)
96+
}
97+
98+
+#[utoipa::path(
99+
+ patch,
100+
+ path = "/v1/oauth2/{rs_name}/_basic_secret",
101+
+ request_body=ProtoEntry,
102+
+ responses(
103+
+ DefaultApiResponse,
104+
+ ),
105+
+ security(("token_jwt" = [])),
106+
+ tag = "v1/oauth2",
107+
+ operation_id = "oauth2_id_patch_basic_secret"
108+
+)]
109+
+/// Overwrite the basic secret for a given OAuth2 Resource Server.
110+
+#[instrument(level = "info", skip(state, new_secret))]
111+
+pub(crate) async fn oauth2_id_patch_basic_secret(
112+
+ State(state): State<ServerState>,
113+
+ Extension(kopid): Extension<KOpId>,
114+
+ VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
115+
+ Path(rs_name): Path<String>,
116+
+ Json(new_secret): Json<String>,
117+
+) -> Result<Json<()>, WebError> {
118+
+ let filter = oauth2_id(&rs_name);
119+
+ state
120+
+ .qe_w_ref
121+
+ .handle_oauth2_basic_secret_write(client_auth_info, filter, new_secret, kopid.eventid)
122+
+ .await
123+
+ .map(Json::from)
124+
+ .map_err(WebError::from)
125+
+}
126+
+
127+
#[utoipa::path(
128+
patch,
129+
path = "/v1/oauth2/{rs_name}",
130+
diff --git a/server/lib/src/server/migrations.rs b/server/lib/src/server/migrations.rs
131+
index fd0bca8db..8621714f2 100644
132+
--- a/server/lib/src/server/migrations.rs
133+
+++ b/server/lib/src/server/migrations.rs
134+
@@ -171,6 +171,22 @@ impl QueryServer {
135+
reload_required = true;
136+
};
137+
138+
+ // secret provisioning: allow idm_admin to modify OAuth2RsBasicSecret.
139+
+ write_txn.internal_modify_uuid(
140+
+ UUID_IDM_ACP_OAUTH2_MANAGE_V1,
141+
+ &ModifyList::new_append(
142+
+ Attribute::AcpCreateAttr,
143+
+ Attribute::OAuth2RsBasicSecret.into(),
144+
+ ),
145+
+ )?;
146+
+ write_txn.internal_modify_uuid(
147+
+ UUID_IDM_ACP_OAUTH2_MANAGE_V1,
148+
+ &ModifyList::new_append(
149+
+ Attribute::AcpModifyPresentAttr,
150+
+ Attribute::OAuth2RsBasicSecret.into(),
151+
+ ),
152+
+ )?;
153+
+
154+
// Execute whatever operations we have batched up and ready to go. This is needed
155+
// to preserve ordering of the operations - if we reloaded after a remigrate then
156+
// we would have skipped the patch level fix which needs to have occurred *first*.
157+
--
158+
2.49.0
159+
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
From 229165abe5be596fc2be8e285884813a1b5a38c8 Mon Sep 17 00:00:00 2001
2+
From: oddlama <[email protected]>
3+
Date: Fri, 21 Mar 2025 16:08:15 +0100
4+
Subject: [PATCH 2/2] recover account
5+
6+
---
7+
server/core/src/actors/internal.rs | 5 +++--
8+
server/core/src/admin.rs | 6 +++---
9+
server/daemon/src/main.rs | 23 ++++++++++++++++++++++-
10+
server/daemon/src/opt.rs | 7 +++++++
11+
4 files changed, 35 insertions(+), 6 deletions(-)
12+
13+
diff --git a/server/core/src/actors/internal.rs b/server/core/src/actors/internal.rs
14+
index 420e72c6c..e252bca51 100644
15+
--- a/server/core/src/actors/internal.rs
16+
+++ b/server/core/src/actors/internal.rs
17+
@@ -172,17 +172,18 @@ impl QueryServerWriteV1 {
18+
19+
#[instrument(
20+
level = "info",
21+
- skip(self, eventid),
22+
+ skip(self, password, eventid),
23+
fields(uuid = ?eventid)
24+
)]
25+
pub(crate) async fn handle_admin_recover_account(
26+
&self,
27+
name: String,
28+
+ password: Option<String>,
29+
eventid: Uuid,
30+
) -> Result<String, OperationError> {
31+
let ct = duration_from_epoch_now();
32+
let mut idms_prox_write = self.idms.proxy_write(ct).await?;
33+
- let pw = idms_prox_write.recover_account(name.as_str(), None)?;
34+
+ let pw = idms_prox_write.recover_account(name.as_str(), password.as_deref())?;
35+
36+
idms_prox_write.commit().map(|()| pw)
37+
}
38+
diff --git a/server/core/src/admin.rs b/server/core/src/admin.rs
39+
index 90ccb1927..85e31ddef 100644
40+
--- a/server/core/src/admin.rs
41+
+++ b/server/core/src/admin.rs
42+
@@ -24,7 +24,7 @@ pub use kanidm_proto::internal::{
43+
44+
#[derive(Serialize, Deserialize, Debug)]
45+
pub enum AdminTaskRequest {
46+
- RecoverAccount { name: String },
47+
+ RecoverAccount { name: String, password: Option<String> },
48+
ShowReplicationCertificate,
49+
RenewReplicationCertificate,
50+
RefreshReplicationConsumer,
51+
@@ -309,8 +309,8 @@ async fn handle_client(
52+
53+
let resp = async {
54+
match req {
55+
- AdminTaskRequest::RecoverAccount { name } => {
56+
- match server_rw.handle_admin_recover_account(name, eventid).await {
57+
+ AdminTaskRequest::RecoverAccount { name, password } => {
58+
+ match server_rw.handle_admin_recover_account(name, password, eventid).await {
59+
Ok(password) => AdminTaskResponse::RecoverAccount { password },
60+
Err(e) => {
61+
error!(err = ?e, "error during recover-account");
62+
diff --git a/server/daemon/src/main.rs b/server/daemon/src/main.rs
63+
index c3b40faa0..2a57a307c 100644
64+
--- a/server/daemon/src/main.rs
65+
+++ b/server/daemon/src/main.rs
66+
@@ -923,13 +923,34 @@ async fn kanidm_main(config: Configuration, opt: KanidmdParser) -> ExitCode {
67+
.await;
68+
}
69+
}
70+
- KanidmdOpt::RecoverAccount { name, commonopts } => {
71+
+ KanidmdOpt::RecoverAccount { name, from_environment, commonopts } => {
72+
info!("Running account recovery ...");
73+
let output_mode: ConsoleOutputMode = commonopts.output_mode.to_owned().into();
74+
+ let password = if *from_environment {
75+
+ match std::env::var("KANIDM_RECOVER_ACCOUNT_PASSWORD_FILE") {
76+
+ Ok(path) => match tokio::fs::read_to_string(&path).await {
77+
+ Ok(contents) => Some(contents),
78+
+ Err(e) => {
79+
+ error!("Failed to read password file '{}': {}", path, e);
80+
+ return ExitCode::FAILURE;
81+
+ }
82+
+ },
83+
+ Err(_) => match std::env::var("KANIDM_RECOVER_ACCOUNT_PASSWORD") {
84+
+ Ok(val) => Some(val),
85+
+ Err(_) => {
86+
+ error!("Neither KANIDM_RECOVER_ACCOUNT_PASSWORD_FILE nor KANIDM_RECOVER_ACCOUNT_PASSWORD was set");
87+
+ return ExitCode::FAILURE;
88+
+ }
89+
+ }
90+
+ }
91+
+ } else {
92+
+ None
93+
+ };
94+
submit_admin_req(
95+
config.adminbindpath.as_str(),
96+
AdminTaskRequest::RecoverAccount {
97+
name: name.to_owned(),
98+
+ password,
99+
},
100+
output_mode,
101+
)
102+
diff --git a/server/daemon/src/opt.rs b/server/daemon/src/opt.rs
103+
index f1b45a5b3..ca19fb6a5 100644
104+
--- a/server/daemon/src/opt.rs
105+
+++ b/server/daemon/src/opt.rs
106+
@@ -236,6 +236,13 @@ enum KanidmdOpt {
107+
#[clap(value_parser)]
108+
/// The account name to recover credentials for.
109+
name: String,
110+
+ /// Use a password given via an environment variable.
111+
+ /// - `KANIDM_RECOVER_ACCOUNT_PASSWORD_FILE` takes precedence and reads the desired
112+
+ /// password from the given file
113+
+ /// - `KANIDM_RECOVER_ACCOUNT_PASSWORD` directly takes a
114+
+ /// password - beware that this will leave the password in the environment
115+
+ #[clap(long = "from-environment")]
116+
+ from_environment: bool,
117+
#[clap(flatten)]
118+
commonopts: CommonOpt,
119+
},
120+
--
121+
2.49.0
122+

pkgs/top-level/all-packages.nix

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10455,8 +10455,9 @@ with pkgs;
1045510455

1045610456
kanidm_1_4 = callPackage ../by-name/ka/kanidm/1_4.nix { kanidm = kanidm_1_4; };
1045710457
kanidm_1_5 = callPackage ../by-name/ka/kanidm/1_5.nix { kanidm = kanidm_1_5; };
10458+
kanidm_1_6 = callPackage ../by-name/ka/kanidm/1_6.nix { kanidm = kanidm_1_6; };
1045810459

10459-
kanidmWithSecretProvisioning = kanidmWithSecretProvisioning_1_5;
10460+
kanidmWithSecretProvisioning = kanidmWithSecretProvisioning_1_6;
1046010461

1046110462
kanidmWithSecretProvisioning_1_4 = callPackage ../by-name/ka/kanidm/1_4.nix {
1046210463
enableSecretProvisioning = true;
@@ -10466,6 +10467,10 @@ with pkgs;
1046610467
enableSecretProvisioning = true;
1046710468
};
1046810469

10470+
kanidmWithSecretProvisioning_1_6 = callPackage ../by-name/ka/kanidm/1_6.nix {
10471+
enableSecretProvisioning = true;
10472+
};
10473+
1046910474
knot-resolver = callPackage ../servers/dns/knot-resolver {
1047010475
systemd = systemdMinimal; # in closure already anyway
1047110476
};

0 commit comments

Comments
 (0)