Show an administrator a share as a share #187
No files matched your search
@@ -608,6 +608,25 @@ impl AccessToken {
|
||||
self.inner.account_id == account_id || self.inner.member_of.contains(&account_id)
|
||||
}
|
||||
|
||||
/// inbuxa: #186: an account that shared something with this token and
|
||||
/// isn't its own or a group's it belongs to. Reads there go through the
|
||||
/// share even for an `Impersonate` holder, so an administrator sees what
|
||||
/// was shared with them, with the share's rights, like anyone else.
|
||||
/// Writes still follow `is_member`.
|
||||
pub fn reads_through_share(&self, account_id: u32) -> bool {
|
||||
!self.is_member_directly(account_id)
|
||||
&& self
|
||||
.inner
|
||||
.access_to
|
||||
.iter()
|
||||
.any(|a| a.account_id == account_id)
|
||||
}
|
||||
|
||||
/// inbuxa: #186: reads see the whole account (see `reads_through_share`).
|
||||
pub fn reads_as_owner(&self, account_id: u32) -> bool {
|
||||
self.is_member(account_id) && !self.reads_through_share(account_id)
|
||||
}
|
||||
|
||||
/// inbuxa: MA-D0: in the account only because it is a group this token
|
||||
/// belongs to. Such a member has the group's mailbox but may not share it
|
||||
/// on: who is in a group is an administrator's decision, and a share
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::auth::AccessToken;
|
||||
@@ -27,7 +29,7 @@ impl EffectiveAcl for &[AclGrant] {
|
||||
fn effective_acl(&self, access_token: &AccessToken) -> Bitmap<Acl> {
|
||||
let mut acl = Bitmap::<Acl>::new();
|
||||
for item in self.iter() {
|
||||
if access_token.is_member(item.account_id) {
|
||||
if access_token.is_member_directly(item.account_id) {
|
||||
acl.union(&item.grants);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +42,7 @@ impl EffectiveAcl for ArchivedVec<ArchivedAclGrant> {
|
||||
fn effective_acl(&self, access_token: &AccessToken) -> Bitmap<Acl> {
|
||||
let mut acl = Bitmap::<Acl>::new();
|
||||
for item in self.iter() {
|
||||
if access_token.is_member(item.account_id.into()) {
|
||||
if access_token.is_member_directly(item.account_id.into()) {
|
||||
acl.union_raw(item.grants.bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{DavResources, auth::AccessToken};
|
||||
@@ -22,7 +24,7 @@ impl DavResources {
|
||||
for resource in &self.resources {
|
||||
if let Some(acls) = resource.acls() {
|
||||
for acl in acls {
|
||||
if access_token.is_member(acl.account_id) {
|
||||
if access_token.is_member_directly(acl.account_id) {
|
||||
let mut grants = acl.grants;
|
||||
grants.intersection(&check_acls);
|
||||
if grants == check_acls || (match_any && !grants.is_empty()) {
|
||||
@@ -102,7 +104,7 @@ impl DavResources {
|
||||
&& let Some(acls) = resource.acls()
|
||||
{
|
||||
for acl in acls {
|
||||
if access_token.is_member(acl.account_id) {
|
||||
if access_token.is_member_directly(acl.account_id) {
|
||||
let mut grants = acl.grants;
|
||||
grants.intersection(&check_acls);
|
||||
return !grants.is_empty();
|
||||
@@ -123,7 +125,7 @@ impl DavResources {
|
||||
&& let Some(acls) = resource.acls()
|
||||
{
|
||||
for acl in acls {
|
||||
if access_token.is_member(acl.account_id) {
|
||||
if access_token.is_member_directly(acl.account_id) {
|
||||
account_acls.union(&acl.grants);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ impl AddressBookGet for Server {
|
||||
SyncCollection::AddressBook,
|
||||
)
|
||||
.await?;
|
||||
let address_book_ids = if access_token.is_member(account_id) {
|
||||
let address_book_ids = if access_token.reads_as_owner(account_id) {
|
||||
cache.document_ids(true).collect::<RoaringBitmap>()
|
||||
} else {
|
||||
cache.shared_containers(access_token, [Acl::Read, Acl::ReadItems], true)
|
||||
@@ -177,7 +177,7 @@ impl AddressBookGet for Server {
|
||||
AddressBookProperty::MyRights => {
|
||||
result.insert_unchecked(
|
||||
AddressBookProperty::MyRights,
|
||||
if access_token.is_shared(account_id) {
|
||||
if access_token.reads_through_share(account_id) {
|
||||
JmapRights::rights::<addressbook::AddressBook>(
|
||||
address_book.acls.effective_acl(access_token),
|
||||
)
|
||||
|
||||
@@ -116,7 +116,7 @@ impl SessionHandler for Server {
|
||||
|
||||
// Add secondary accounts
|
||||
for &account_id in access_token.secondary_ids() {
|
||||
let is_owner = access_token.is_member(account_id);
|
||||
let is_owner = access_token.reads_as_owner(account_id);
|
||||
let Some(account) = self
|
||||
.try_account(account_id)
|
||||
.await
|
||||
|
||||
@@ -116,7 +116,7 @@ impl BlobDownload for Server {
|
||||
collection,
|
||||
document_id,
|
||||
} => {
|
||||
if access_token.is_member(*account_id) {
|
||||
if access_token.reads_as_owner(*account_id) {
|
||||
// inbuxa: AU-1.6: another account's blob
|
||||
self.audit_foreign_access(access_token, *account_id, true)
|
||||
.await;
|
||||
|
||||
@@ -73,7 +73,7 @@ impl CalendarGet for Server {
|
||||
SyncCollection::Calendar,
|
||||
)
|
||||
.await?;
|
||||
let is_owner = access_token.is_member(account_id);
|
||||
let is_owner = access_token.reads_as_owner(account_id);
|
||||
let calendar_ids = if is_owner {
|
||||
cache.document_ids(true).collect::<RoaringBitmap>()
|
||||
} else {
|
||||
@@ -250,7 +250,7 @@ impl CalendarGet for Server {
|
||||
CalendarProperty::MyRights => {
|
||||
result.insert_unchecked(
|
||||
CalendarProperty::MyRights,
|
||||
if access_token.is_shared(account_id) {
|
||||
if access_token.reads_through_share(account_id) {
|
||||
JmapRights::rights::<calendar::Calendar>(
|
||||
calendar.acls.effective_acl(access_token),
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
@@ -90,7 +92,7 @@ impl JmapCalendarEventCopy for Server {
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let from_calendar_event_ids = if access_token.is_member(from_account_id) {
|
||||
let from_calendar_event_ids = if access_token.reads_as_owner(from_account_id) {
|
||||
from_cache.document_ids(false).collect::<RoaringBitmap>()
|
||||
} else {
|
||||
from_cache.shared_items(access_token, [Acl::ReadItems], true)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{calendar_event::CalendarSyntheticId, changes::state::JmapCacheState};
|
||||
@@ -71,7 +73,7 @@ impl CalendarEventGet for Server {
|
||||
SyncCollection::Calendar,
|
||||
)
|
||||
.await?;
|
||||
let calendar_event_ids = if access_token.is_member(account_id) {
|
||||
let calendar_event_ids = if access_token.reads_as_owner(account_id) {
|
||||
cache.document_ids(false).collect::<RoaringBitmap>()
|
||||
} else {
|
||||
cache.shared_items(access_token, [Acl::ReadItems], true)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{api::query::QueryResponseBuilder, changes::state::JmapCacheState};
|
||||
@@ -248,7 +250,7 @@ impl CalendarEventQuery for Server {
|
||||
.with_filters(filters)
|
||||
.with_comparators(comparators)
|
||||
.with_account_id(account_id)
|
||||
.with_mask(if access_token.is_shared(account_id) {
|
||||
.with_mask(if access_token.reads_through_share(account_id) {
|
||||
cache.shared_items(access_token, [Acl::ReadItems], true)
|
||||
} else {
|
||||
cache.document_ids(false).collect()
|
||||
|
||||
@@ -134,7 +134,7 @@ impl ChangesLookup for Server {
|
||||
};
|
||||
let account_id = request.account_id.document_id();
|
||||
|
||||
let allowed_ids: Option<RoaringBitmap> = if access_token.is_member(account_id) {
|
||||
let allowed_ids: Option<RoaringBitmap> = if access_token.reads_as_owner(account_id) {
|
||||
None
|
||||
} else {
|
||||
Some(match object {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{changes::state::JmapCacheState, contact::set::ContactCardSet};
|
||||
@@ -87,7 +89,7 @@ impl JmapContactCardCopy for Server {
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let from_contact_ids = if access_token.is_member(from_account_id) {
|
||||
let from_contact_ids = if access_token.reads_as_owner(from_account_id) {
|
||||
from_cache.document_ids(false).collect::<RoaringBitmap>()
|
||||
} else {
|
||||
from_cache.shared_items(access_token, [Acl::ReadItems], true)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::changes::state::JmapCacheState;
|
||||
@@ -52,7 +54,7 @@ impl ContactCardGet for Server {
|
||||
SyncCollection::AddressBook,
|
||||
)
|
||||
.await?;
|
||||
let contact_ids = if access_token.is_member(account_id) {
|
||||
let contact_ids = if access_token.reads_as_owner(account_id) {
|
||||
cache.document_ids(false).collect::<RoaringBitmap>()
|
||||
} else {
|
||||
cache.shared_items(access_token, [Acl::ReadItems], true)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{api::query::QueryResponseBuilder, changes::state::JmapCacheState};
|
||||
@@ -313,7 +315,7 @@ impl ContactCardQuery for Server {
|
||||
.with_filters(filters)
|
||||
.with_comparators(comparators)
|
||||
.with_account_id(account_id)
|
||||
.with_mask(if access_token.is_shared(account_id) {
|
||||
.with_mask(if access_token.reads_through_share(account_id) {
|
||||
cache.shared_items(access_token, [Acl::ReadItems], true)
|
||||
} else {
|
||||
cache.document_ids(false).collect()
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
@@ -79,7 +81,7 @@ impl JmapEmailCopy for Server {
|
||||
.get_cached_messages(from_account_id)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let from_message_ids = if access_token.is_member(from_account_id) {
|
||||
let from_message_ids = if access_token.reads_as_owner(from_account_id) {
|
||||
from_cache.email_document_ids()
|
||||
} else {
|
||||
from_cache.shared_messages(access_token, Acl::ReadItems)
|
||||
|
||||
@@ -112,7 +112,7 @@ impl EmailGet for Server {
|
||||
.get_cached_messages(account_id)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let message_ids = if access_token.is_member(account_id) {
|
||||
let message_ids = if access_token.reads_as_owner(account_id) {
|
||||
cache.email_document_ids()
|
||||
} else {
|
||||
cache.shared_messages(access_token, Acl::ReadItems)
|
||||
|
||||
@@ -356,7 +356,7 @@ impl EmailQuery for Server {
|
||||
.with_filters(filters)
|
||||
.with_comparators(comparators)
|
||||
.with_account_id(account_id)
|
||||
.with_mask(if access_token.is_shared(account_id) {
|
||||
.with_mask(if access_token.reads_through_share(account_id) {
|
||||
cached_messages.shared_messages(access_token, Acl::ReadItems)
|
||||
} else {
|
||||
cached_messages
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use common::{Server, auth::AccessToken};
|
||||
@@ -98,7 +100,7 @@ impl EmailSearchSnippet for Server {
|
||||
.get_cached_messages(account_id)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let document_ids = if access_token.is_member(account_id) {
|
||||
let document_ids = if access_token.reads_as_owner(account_id) {
|
||||
cached_messages.email_document_ids()
|
||||
} else {
|
||||
cached_messages.shared_messages(access_token, Acl::ReadItems)
|
||||
|
||||
@@ -98,7 +98,7 @@ impl FileNodeCopy for Server {
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let from_node_ids = if access_token.is_member(from_account_id) {
|
||||
let from_node_ids = if access_token.reads_as_owner(from_account_id) {
|
||||
from_cache
|
||||
.resources
|
||||
.iter()
|
||||
|
||||
@@ -71,7 +71,7 @@ impl FileNodeGet for Server {
|
||||
)
|
||||
.await?;
|
||||
// TODO: draft-14 section 5 case 2 - ancestors of shared nodes should be discoverable with mayRead=false
|
||||
let file_node_ids = if access_token.is_member(account_id) {
|
||||
let file_node_ids = if access_token.reads_as_owner(account_id) {
|
||||
cache
|
||||
.resources
|
||||
.iter()
|
||||
@@ -169,7 +169,7 @@ impl FileNodeGet for Server {
|
||||
FileNodeProperty::MyRights => {
|
||||
result.insert_unchecked(
|
||||
FileNodeProperty::MyRights,
|
||||
if access_token.is_shared(account_id) {
|
||||
if access_token.reads_through_share(account_id) {
|
||||
JmapRights::rights::<file_node::FileNode>(
|
||||
file_node.acls.effective_acl(access_token),
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{api::query::QueryResponseBuilder, changes::state::JmapCacheState};
|
||||
@@ -193,7 +195,7 @@ impl FileNodeQuery for Server {
|
||||
|
||||
let results = SearchQuery::new(SearchIndex::InMemory)
|
||||
.with_filters(filters)
|
||||
.with_mask(if access_token.is_shared(account_id) {
|
||||
.with_mask(if access_token.reads_through_share(account_id) {
|
||||
cache.shared_documents(access_token, [Acl::Read, Acl::ReadItems], true)
|
||||
} else {
|
||||
cache.resources.iter().map(|r| r.document_id).collect()
|
||||
|
||||
@@ -51,7 +51,7 @@ impl MailboxGet for Server {
|
||||
let account_id = request.account_id.document_id();
|
||||
let personal_id = access_token.personal_id(account_id, Collection::Mailbox);
|
||||
let cache = self.get_cached_messages(account_id).await?;
|
||||
let shared_ids = if access_token.is_shared(account_id) {
|
||||
let shared_ids = if access_token.reads_through_share(account_id) {
|
||||
cache.shared_mailboxes(access_token, Acl::Read).into()
|
||||
} else {
|
||||
None
|
||||
@@ -137,7 +137,7 @@ impl MailboxGet for Server {
|
||||
.into(),
|
||||
),
|
||||
MailboxProperty::MyRights => {
|
||||
if access_token.is_shared(account_id) {
|
||||
if access_token.reads_through_share(account_id) {
|
||||
JmapRights::rights::<Mailbox>(
|
||||
cached_mailbox.acls.as_slice().effective_acl(access_token),
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{api::query::QueryResponseBuilder, changes::state::JmapCacheState};
|
||||
@@ -229,7 +231,7 @@ impl MailboxQuery for Server {
|
||||
let mut results = SearchQuery::new(SearchIndex::InMemory)
|
||||
.with_filters(filters)
|
||||
.with_comparators(comparators)
|
||||
.with_mask(if access_token.is_shared(account_id) {
|
||||
.with_mask(if access_token.reads_through_share(account_id) {
|
||||
mailboxes.shared_mailboxes(access_token, Acl::Read)
|
||||
} else {
|
||||
mailboxes
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*
|
||||
* Modified by Coffey Labs in 2026 for INBUXA.
|
||||
*/
|
||||
|
||||
use crate::{calendar::Availability, calendar_event::CalendarSyntheticId};
|
||||
@@ -118,7 +120,7 @@ impl PrincipalGetAvailability for Server {
|
||||
|
||||
// Obtain shared ids
|
||||
let is_account_owner = principal_id == account_id;
|
||||
let shared_ids = if !access_token.is_member(account_id) {
|
||||
let shared_ids = if !access_token.reads_as_owner(account_id) {
|
||||
// Condition: The user has the "mayReadFreeBusy" permission for the calendar.
|
||||
let shared_ids = resources.shared_items(
|
||||
access_token,
|
||||
|
||||
@@ -44,7 +44,7 @@ impl ThreadGet for Server {
|
||||
.get_cached_messages(account_id)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let shared_ids = if access_token.is_shared(account_id) {
|
||||
let shared_ids = if access_token.reads_through_share(account_id) {
|
||||
Some(cache.shared_messages(access_token, Acl::ReadItems))
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -253,7 +253,7 @@ pub async fn test(test: &mut TestServer) {
|
||||
&mail,
|
||||
json!([["Mailbox/set", {
|
||||
"accountId": sharer.id_string(),
|
||||
"update": {inbox: {format!("shareWith/{admin_id}"): {"mayReadItems": true}}}
|
||||
"update": {inbox.clone(): {format!("shareWith/{admin_id}"): {"mayReadItems": true}}}
|
||||
}, "0"]]),
|
||||
)
|
||||
.await;
|
||||
@@ -281,6 +281,50 @@ pub async fn test(test: &mut TestServer) {
|
||||
"test 3b"
|
||||
);
|
||||
|
||||
// Test 3c (#186): in an account that shared with them, an administrator
|
||||
// reads what was shared, with the share's rights: not every folder, and
|
||||
// not folders shared with someone else.
|
||||
let sent = mailboxes
|
||||
.0
|
||||
.pointer("/methodResponses/0/1/list")
|
||||
.and_then(|list| list.as_array())
|
||||
.and_then(|list| list.iter().find(|m| m["role"] == "sent"))
|
||||
.and_then(|m| m["id"].as_str())
|
||||
.unwrap_or_else(|| panic!("test 3c: no Sent in {}", mailboxes.0))
|
||||
.to_string();
|
||||
let shared = sharer
|
||||
.jmap_request(
|
||||
&mail,
|
||||
json!([["Mailbox/set", {
|
||||
"accountId": sharer.id_string(),
|
||||
"update": {sent: {format!("shareWith/{}", user.id_string()): {"mayReadItems": true}}}
|
||||
}, "0"]]),
|
||||
)
|
||||
.await;
|
||||
assert!(
|
||||
shared
|
||||
.0
|
||||
.pointer("/methodResponses/0/1/updated")
|
||||
.is_some_and(|updated| !updated.is_null()),
|
||||
"test 3c: share: {}",
|
||||
shared.0
|
||||
);
|
||||
let response = admin
|
||||
.jmap_request(
|
||||
&mail,
|
||||
json!([["Mailbox/get", {"accountId": sharer.id_string(), "ids": null, "properties": ["id", "myRights"]}, "0"]]),
|
||||
)
|
||||
.await;
|
||||
let list = response
|
||||
.0
|
||||
.pointer("/methodResponses/0/1/list")
|
||||
.and_then(|list| list.as_array())
|
||||
.unwrap_or_else(|| panic!("test 3c: {}", response.0));
|
||||
assert_eq!(list.len(), 1, "test 3c: {}", response.0);
|
||||
assert_eq!(list[0]["id"], inbox.as_str(), "test 3c");
|
||||
assert_eq!(list[0]["myRights"]["mayReadItems"], true, "test 3c");
|
||||
assert_eq!(list[0]["myRights"]["mayDelete"], false, "test 3c");
|
||||
|
||||
// AU-9: a plain user can't read the audit log
|
||||
let plain = Account::new(
|
||||
"[email protected]",
|
||||
|
||||
Reference in new issue
Block a user