v3 : points de la relecture indépendante — une personne une voix, ordre de déploiement, durcissements
- Dans Monnaie libre (électorat = toile de confiance), seuls les membres de la toile votent (trustin_access::votes_in) ; une identité d'invitation y lit, propose et signale. L'interface le dit à la place des boutons (fr/en/es). - Un lien d'invitation ne vise jamais une identité Ğ1 ; le lien est réservé avant de créer l'identité (deux acceptations simultanées : une seule passe). - Un nombre n'est jamais une coquille (« 50 » → « 5,0 », « 1 5 » → « 15 »). - Redirection après connexion : caractères de contrôle refusés. - En-têtes : X-Frame-Options DENY, frame-ancestors 'none', nosniff, Referrer-Policy. - Déploiement : le produit passe en premier ; la maquette attend son succès. Les conteneurs, même arrêtés, qui revendiquent encore la racine sortent de « restart: always » (ils reviendraient au redémarrage de Docker). - Tests : 57 Rust (dont 14 scénarios d'API), e2e 12 / 12 avec C11. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,9 @@ async fn anonymous_sees_nothing_and_csrf_is_enforced() {
|
||||
.expect("request");
|
||||
let res = c.router.clone().oneshot(req).await.expect("response");
|
||||
assert_eq!(res.status(), StatusCode::FORBIDDEN);
|
||||
// No neighbour may frame the product.
|
||||
assert_eq!(res.headers().get(header::X_FRAME_OPTIONS).map(|v| v.as_bytes()), Some(&b"DENY"[..]));
|
||||
assert!(res.headers().contains_key(header::X_CONTENT_TYPE_OPTIONS));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -377,6 +380,38 @@ async fn access_management_is_for_administrators_only() {
|
||||
assert!(ov["identities"].as_array().expect("i").iter().any(|i| i["displayName"] == "Chiara"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn in_monnaie_libre_only_web_of_trust_members_vote() {
|
||||
let (mut admin, _) = Client::new().await;
|
||||
admin.login("Yvv", true).await;
|
||||
let (_, v) = admin
|
||||
.call("POST", "/api/access/invitations", Some(json!({ "label": "Invitée", "groups": ["monnaie-libre"] })))
|
||||
.await;
|
||||
let token = v["link"].as_str().expect("link").split('#').nth(1).expect("token").to_owned();
|
||||
let mut invited = admin.with(None);
|
||||
let (s, _) = invited.call("POST", "/api/auth/invitation", Some(json!({ "token": token }))).await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
// She reads the text and may propose, but her vote would not be a web-of-trust vote.
|
||||
let id = doc_id(&mut invited, "monnaie-libre").await;
|
||||
let (s, doc) = invited.call("GET", &format!("/api/docs/{id}"), None).await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
assert_eq!(doc["canVote"], false);
|
||||
let p = unit(&doc, "E3")["proposals"][0]["id"].as_str().expect("p").to_owned();
|
||||
let (s, v) = invited.call("POST", &format!("/api/proposals/{p}/vote"), Some(json!({ "choice": "for" }))).await;
|
||||
assert_eq!(s, StatusCode::CONFLICT, "{v}");
|
||||
assert_eq!(v["error"], "wot_members_only");
|
||||
// In the test group, every member votes.
|
||||
let essai = doc_id(&mut admin, "essai").await;
|
||||
let (_, d) = admin.call("GET", &format!("/api/docs/{essai}"), None).await;
|
||||
assert_eq!(d["canVote"], true);
|
||||
// A new link may only target an invitation identity, never another kind.
|
||||
let (_, me) = admin.call("GET", "/api/me", None).await;
|
||||
let dev_id = me["me"]["id"].as_str().expect("id").to_owned();
|
||||
let (s, _) =
|
||||
admin.call("POST", "/api/access/invitations", Some(json!({ "label": "Yvv", "identityId": dev_id }))).await;
|
||||
assert_eq!(s, StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn feedback_reaches_administrators() {
|
||||
let (mut user, _) = Client::new().await;
|
||||
|
||||
Reference in New Issue
Block a user