Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d60f8b9a3 | ||
|
|
f36d88a69c | ||
|
|
ef46b463e4 | ||
|
|
4818c5a26f |
@@ -147,6 +147,7 @@ jobs:
|
||||
auth="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
|
||||
# The release notes are this version's section of CHANGELOG.md.
|
||||
notes=$(awk -v v="${tag#v}" '/^## /{p = ($2 == v)} p' CHANGELOG.md | tail -n +2)
|
||||
[ -n "$(printf "%s" "$notes" | tr -d "[:space:]")" ] || { echo "CHANGELOG.md has no section for $tag - write it and re-tag"; exit 1; }
|
||||
body=$(jq -n --arg tag "$tag" --arg notes "$notes" '{tag_name: $tag, name: $tag, body: $notes}')
|
||||
id=$(curl -sf -X POST -H "$auth" -H 'Content-Type: application/json' \
|
||||
-d "$body" "$api/releases" | jq .id) \
|
||||
|
||||
+21
-3
@@ -4,6 +4,24 @@ One line per change, grouped by the release that shipped it. Newest
|
||||
first. A release is a Cargo.toml bump plus a `v<version>` tag; the
|
||||
content repos pin the tag they run (see the end of this file).
|
||||
|
||||
## 0.3.42 (2026-09-23)
|
||||
|
||||
- people: the Kanidm person lookup reads the body, since Kanidm 1.11 answers a missing person with 200 and `null` rather than 404; before this every invite skipped account creation and failed on the group step
|
||||
|
||||
## 0.3.41 (2026-09-23)
|
||||
|
||||
- publish: the iris release step retries on a fresh clone when its push loses a race against a push to iris main (the v0.3.40 tag never published)
|
||||
- iris: `check --access` prints the access matrix, and every check validates the compiled policy against the schema
|
||||
|
||||
## 0.3.40 (2026-09-23)
|
||||
|
||||
- One access policy, compiled from content into Cedar on every load (`src/access.rs`): pages, resource reads, desk transitions, self transitions and the automation endpoint all decide through it; decisions carry rule ids, refusals and mutations publish on `portal.access.decided`, and `portal.access.<site>.may` answers the same question for other apps over NATS
|
||||
- `invite: {group}` on an alternative onboards a person into Kanidm from a desk (`src/people.rs`, `KANIDM_API_TOKEN`), recording the one-time credential link in `portal_invites` and on `portal.people.invited`
|
||||
|
||||
## 0.3.39 (2026-09-23)
|
||||
|
||||
- publish: releases the matching uhhm/iris by proxy - re-pins, builds, tests and tags it as this version (needs the `IRIS_PUSH_USER` variable and `IRIS_PUSH_TOKEN` secret); iris carries portal's version number from here on
|
||||
|
||||
## 0.3.38 (2026-09-22)
|
||||
|
||||
- publish: the iris build step gets a writable cargo home; the v0.3.37 tag never published
|
||||
@@ -287,6 +305,6 @@ tarball deploy on kasse.
|
||||
|---|---|---|---|
|
||||
| uhhm.no | ergo | `app@uhhm-portal` | v0.3.36 |
|
||||
| redoal.com | ergo | `app@redoal-portal` | v0.3.36 |
|
||||
| westra.klingenbergbygg.no | kasse | `app@westra-portal` | v0.3.36 |
|
||||
| portal.klingenbergbygg.no | kasse | `app@klingenberg-portal` | v0.3.36 |
|
||||
| vel.klingenbergbygg.no | kasse | `app@tomtervel-portal` | v0.3.36 |
|
||||
| westra.klingenbergbygg.no | kasse | `app@westra-portal` | v0.3.41 |
|
||||
| portal.klingenbergbygg.no | kasse | `app@klingenberg-portal` | v0.3.41 |
|
||||
| vel.klingenbergbygg.no | kasse | `app@tomtervel-portal` | v0.3.41 |
|
||||
|
||||
Generated
+1
-1
@@ -3265,7 +3265,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "portal"
|
||||
version = "0.3.41"
|
||||
version = "0.3.42"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "portal"
|
||||
version = "0.3.41"
|
||||
version = "0.3.42"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
+24
-6
@@ -31,6 +31,19 @@ pub struct Invited {
|
||||
pub created: bool,
|
||||
}
|
||||
|
||||
/// Whether a `GET /v1/person/<name>` answer means the person exists.
|
||||
/// Kanidm 1.11 answers a missing person with 200 and the body `null`
|
||||
/// rather than 404, so the status alone would report everyone as
|
||||
/// existing and no account would ever get created. `None` for an
|
||||
/// answer that is neither.
|
||||
fn person_lookup_found(status: u16, body: &str) -> Option<bool> {
|
||||
match status {
|
||||
200 => Some(body.trim() != "null" && !body.trim().is_empty()),
|
||||
404 => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Kanidm {
|
||||
base: String,
|
||||
token: String,
|
||||
@@ -60,12 +73,8 @@ impl Kanidm {
|
||||
}
|
||||
|
||||
async fn person_exists(&self, username: &str) -> anyhow::Result<bool> {
|
||||
let (status, _) = self.call(openidconnect::reqwest::Method::GET, &format!("/v1/person/{username}"), None).await?;
|
||||
match status {
|
||||
200 => Ok(true),
|
||||
404 => Ok(false),
|
||||
other => anyhow::bail!("kanidm: looking up {username}: http {other}"),
|
||||
}
|
||||
let (status, text) = self.call(openidconnect::reqwest::Method::GET, &format!("/v1/person/{username}"), None).await?;
|
||||
person_lookup_found(status, &text).ok_or_else(|| anyhow::anyhow!("kanidm: looking up {username}: http {status}"))
|
||||
}
|
||||
|
||||
/// The whole onboarding, in the order that leaves the least behind
|
||||
@@ -161,4 +170,13 @@ mod tests {
|
||||
assert_eq!(username_for("42@x.no"), "person");
|
||||
assert_eq!(username_for("Ærlig@x.no"), "rlig");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_null_person_answer_means_nobody_there() {
|
||||
assert_eq!(person_lookup_found(200, "null"), Some(false));
|
||||
assert_eq!(person_lookup_found(200, "null\n"), Some(false));
|
||||
assert_eq!(person_lookup_found(200, r#"{"attrs":{"name":["bl"]}}"#), Some(true));
|
||||
assert_eq!(person_lookup_found(404, ""), Some(false));
|
||||
assert_eq!(person_lookup_found(401, "unauthorized"), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user