Restrict workspace settings to administrators, add demo countries, Helm chart and instance event log
Employees keep schedule editing but can no longer change workspace preferences or existing scheduling-directory entries (enforced by the server, hidden in the UI). Demo accounts live in Cyprus, the United Kingdom and Armenia with matching bank holidays. A Helm chart deploys the service with a pre-upgrade migrate hook. Refused SSO sign-ins are stored with their support reference as instance events and searchable on the instance administration page. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
7afdc4be97
commit
548494f12e
11 files changed
+413
-18
No files matched your search
@@ -15,6 +15,7 @@ use meguri::{
|
||||
service,
|
||||
store::Store,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::collections::BTreeSet;
|
||||
use support::{IsolatedDatabase, account, config_with};
|
||||
use uuid::Uuid;
|
||||
@@ -1357,3 +1358,82 @@ async fn employees_plan_schedules_but_only_administrators_change_preferences_and
|
||||
assert_eq!(changed.document.horizon_days, 45);
|
||||
assert_eq!(changed.document.people[0].display_name, "Dana Renamed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "requires MEGURI_TEST_DATABASE_URL and CREATEDB"]
|
||||
async fn the_instance_event_log_is_searchable_newest_first_and_failed_logins_are_capped() {
|
||||
let iso = IsolatedDatabase::new().await;
|
||||
let db = &iso.store;
|
||||
let mut config = config_with(vec![ws("alpha", "Alpha")]);
|
||||
config.instance_admin_groups = vec!["root".into()];
|
||||
sync(db, &config, at(9, 0)).await;
|
||||
let root = account(db, &config, "Root", &["root".into()]).await;
|
||||
let plain = account(db, &config, "Plain", &["alpha-staff".into()]).await;
|
||||
assert!(matches!(
|
||||
admin::events(db, plain.id, "").await,
|
||||
Err(Error::Forbidden)
|
||||
));
|
||||
db.instance_event(
|
||||
None,
|
||||
"login.failed",
|
||||
json!({"reference":"deadbeef","code":"no_access","groups":["/unknown"]}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
admin::create_user(
|
||||
db,
|
||||
&config,
|
||||
root.id,
|
||||
NewUser {
|
||||
username: "casey".into(),
|
||||
display_name: "Casey".into(),
|
||||
password: "long-enough-password".into(),
|
||||
email: None,
|
||||
groups: vec![],
|
||||
country: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let all = admin::events(db, root.id, "").await.unwrap();
|
||||
let actions: Vec<_> = all["events"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|e| e["action"].as_str().unwrap().to_owned())
|
||||
.collect();
|
||||
assert_eq!(actions[0], "user.created", "newest first");
|
||||
assert!(actions.contains(&"workspace.created".to_owned()));
|
||||
assert_eq!(all["events"][0]["actor"], root.username);
|
||||
let by_reference = admin::events(db, root.id, " DEADBEEF ").await.unwrap();
|
||||
assert_eq!(by_reference["events"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(
|
||||
by_reference["events"][0]["details"]["groups"][0],
|
||||
"/unknown"
|
||||
);
|
||||
assert!(
|
||||
admin::events(db, root.id, "no-such-text").await.unwrap()["events"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
// Anonymous sign-in failures cannot grow the table without bound, nor push out other events.
|
||||
for n in 0..2010 {
|
||||
db.instance_event(None, "login.failed", json!({"reference":format!("{n:08}")}))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
let remaining = InstanceAuditRow::objects()
|
||||
.all(&db.db)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter(|e| e.action == "login.failed")
|
||||
.count();
|
||||
assert!((2000..=2002).contains(&remaining), "{remaining}");
|
||||
assert!(
|
||||
!events(db, "workspace.created").await.is_empty()
|
||||
&& !events(db, "user.created").await.is_empty()
|
||||
);
|
||||
iso.drop().await;
|
||||
}
|
||||
Reference in new issue
Block a user