mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-07-10 10:14:51 -06:00
Merge 283467f90e into 169aa5efcc
This commit is contained in:
commit
12a5a8deaa
@ -125,7 +125,7 @@ async fn get_user_events(
|
||||
})))
|
||||
}
|
||||
|
||||
fn get_continuation_token(events_json: &[Value]) -> Option<&str> {
|
||||
pub(crate) fn get_continuation_token(events_json: &[Value]) -> Option<&str> {
|
||||
// When the length of the vec equals the max page_size there probably is more data
|
||||
// When it is less, then all events are loaded.
|
||||
#[expect(clippy::cast_possible_truncation, reason = "PAGE_SIZE fits within usize")]
|
||||
|
||||
@ -1,28 +1,72 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use chrono::Utc;
|
||||
use chrono::{TimeDelta, Utc};
|
||||
use rocket::{
|
||||
Request, Route,
|
||||
form::FromForm,
|
||||
request::{FromRequest, Outcome},
|
||||
serde::json::Json,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
CONFIG,
|
||||
api::EmptyResult,
|
||||
api::{EmptyResult, JsonResult, core::events::get_continuation_token},
|
||||
auth,
|
||||
db::{
|
||||
DbConn,
|
||||
models::{
|
||||
Group, GroupUser, Invitation, Membership, MembershipStatus, MembershipType, Organization,
|
||||
Event, Group, GroupUser, Invitation, Membership, MembershipStatus, MembershipType, Organization,
|
||||
OrganizationApiKey, OrganizationId, User,
|
||||
},
|
||||
},
|
||||
mail,
|
||||
util::parse_date,
|
||||
};
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![ldap_import]
|
||||
routes![ldap_import, get_org_events]
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct EventRangePublic {
|
||||
start: Option<String>,
|
||||
end: Option<String>,
|
||||
#[field(name = "continuationToken")]
|
||||
continuation_token: Option<String>,
|
||||
}
|
||||
|
||||
// Upstream: https://github.com/bitwarden/server/blob/main/src/Api/AdminConsole/Public/Controllers/EventsController.cs
|
||||
#[get("/public/events?<data..>")]
|
||||
async fn get_org_events(data: EventRangePublic, token: PublicToken, conn: DbConn) -> JsonResult {
|
||||
let org_id = token.0;
|
||||
|
||||
// Return an empty list when org events are disabled, to mirror the internal endpoints.
|
||||
let events_json: Vec<Value> = if CONFIG.org_events_enabled() {
|
||||
let now = Utc::now().naive_utc();
|
||||
// Defaults match upstream ApiHelpers.GetDateRange: end = now, start = end - 1 day.
|
||||
let start_date = data.start.as_deref().map_or_else(|| now - TimeDelta::try_days(1).unwrap(), parse_date);
|
||||
// The continuation token, when present, becomes the new `end` cursor (same as the internal endpoints).
|
||||
let end_date = match (&data.continuation_token, &data.end) {
|
||||
(Some(token), _) => parse_date(token),
|
||||
(None, Some(end)) => parse_date(end),
|
||||
(None, None) => now,
|
||||
};
|
||||
|
||||
Event::find_by_organization_uuid(&org_id, &start_date, &end_date, &conn)
|
||||
.await
|
||||
.iter()
|
||||
.map(Event::to_json_public)
|
||||
.collect()
|
||||
} else {
|
||||
Vec::with_capacity(0)
|
||||
};
|
||||
|
||||
Ok(Json(json!({
|
||||
"data": events_json,
|
||||
"object": "list",
|
||||
"continuationToken": get_continuation_token(&events_json),
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
@ -191,6 +191,26 @@ impl Event {
|
||||
// "installationId": null, // Not supported
|
||||
})
|
||||
}
|
||||
|
||||
// Upstream: https://github.com/bitwarden/server/blob/main/src/Api/AdminConsole/Public/Models/Response/EventResponseModel.cs
|
||||
pub fn to_json_public(&self) -> Value {
|
||||
use crate::util::format_date;
|
||||
|
||||
json!({
|
||||
"object": "event",
|
||||
"type": self.event_type,
|
||||
"itemId": self.cipher_uuid,
|
||||
"collectionId": self.collection_uuid,
|
||||
"groupId": self.group_uuid,
|
||||
"policyId": self.policy_uuid,
|
||||
"memberId": self.org_user_uuid,
|
||||
"actingUserId": self.act_user_uuid,
|
||||
"installationId": null, // Not supported
|
||||
"date": format_date(&self.event_date),
|
||||
"deviceType": self.device_type,
|
||||
"ipAddress": self.ip_address,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Database methods
|
||||
@ -350,3 +370,35 @@ impl Event {
|
||||
|
||||
#[derive(Clone, Debug, DieselNewType, FromForm, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct EventId(String);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn to_json_public_uses_public_api_field_names() {
|
||||
// OrganizationUserUpdated event with a member + cipher reference
|
||||
let mut event = Event::new(EventType::OrganizationUserUpdated as i32, None);
|
||||
event.org_uuid = Some("11111111-1111-1111-1111-111111111111".to_string().into());
|
||||
event.org_user_uuid = Some("22222222-2222-2222-2222-222222222222".to_string().into());
|
||||
event.cipher_uuid = Some("33333333-3333-3333-3333-333333333333".to_string().into());
|
||||
event.act_user_uuid = Some("44444444-4444-4444-4444-444444444444".to_string().into());
|
||||
|
||||
let json = event.to_json_public();
|
||||
|
||||
// Public API field names
|
||||
assert_eq!(json["object"], "event");
|
||||
assert_eq!(json["type"], EventType::OrganizationUserUpdated as i32);
|
||||
assert_eq!(json["itemId"], "33333333-3333-3333-3333-333333333333");
|
||||
assert_eq!(json["memberId"], "22222222-2222-2222-2222-222222222222");
|
||||
assert_eq!(json["actingUserId"], "44444444-4444-4444-4444-444444444444");
|
||||
assert!(json["installationId"].is_null());
|
||||
|
||||
// Internal-only keys MUST NOT be present in the public model
|
||||
assert!(json.get("cipherId").is_none());
|
||||
assert!(json.get("organizationUserId").is_none());
|
||||
assert!(json.get("organizationId").is_none());
|
||||
assert!(json.get("userId").is_none());
|
||||
assert!(json.get("providerId").is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user