Skip to content

Commit

Permalink
Add support for multi-user, single-admin key rotations
Browse files Browse the repository at this point in the history
tutadb#1842
  • Loading branch information
vitoreiji authored and vaf-hub committed Sep 2, 2024
1 parent 42c8655 commit 40d9224
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/common/api/common/TutanotaConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1192,10 +1192,12 @@ export enum CryptoProtocolVersion {

export enum GroupKeyRotationType {
User = "0",
Admin = "1",
AdminGroupKeyRotationSingleUserAccount = "1", // scheduled for accounts that only have one user (incl. deactivated users)
Team = "2",
UserArea = "3",
Customer = "4",
AdminGroupKeyRotationMultipleUserAccount = "5", // scheduled for accounts that have multiple users but only a single admin user
AdminGroupKeyRotationMultipleAdminAccount = "6", // scheduled for accounts that have multiple admin users
}

export const GroupKeyRotationTypeNameByCode = reverse(GroupKeyRotationType)
Expand Down
42 changes: 32 additions & 10 deletions src/common/api/worker/facades/KeyRotationFacade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,20 @@ import {
UserGroupRootTypeRef,
UserTypeRef,
} from "../../entities/sys/TypeRefs.js"
import { GroupKeyRotationType, GroupType } from "../../common/TutanotaConstants.js"
import { assertNotNull, defer, DeferredObject, downcast, getFirstOrThrow, groupBy, isEmpty, isSameTypeRef, lazyAsync, promiseMap } from "@tutao/tutanota-utils"
import { assertEnumValue, GroupKeyRotationType, GroupType } from "../../common/TutanotaConstants.js"
import {
assertNotNull,
defer,
DeferredObject,
downcast,
getFirstOrThrow,
groupBy,
isEmpty,
isNotNull,
isSameTypeRef,
lazyAsync,
promiseMap,
} from "@tutao/tutanota-utils"
import { elementIdPart, isSameId, listIdPart } from "../../common/utils/EntityUtils.js"
import { KeyLoaderFacade } from "./KeyLoaderFacade.js"
import {
Expand Down Expand Up @@ -184,11 +196,18 @@ export class KeyRotationFacade {
if (userGroupRoot.keyRotations != null) {
const pendingKeyRotations = await this.entityClient.loadAll(KeyRotationTypeRef, userGroupRoot.keyRotations.list)
const keyRotationsByType = groupBy(pendingKeyRotations, (keyRotation) => keyRotation.groupKeyRotationType)
let adminOrUserGroupKeyRotationArray = keyRotationsByType.get(GroupKeyRotationType.Admin)
let adminOrUserGroupKeyRotationArray: Array<KeyRotation> = [
keyRotationsByType.get(GroupKeyRotationType.AdminGroupKeyRotationSingleUserAccount),
keyRotationsByType.get(GroupKeyRotationType.AdminGroupKeyRotationMultipleUserAccount),
keyRotationsByType.get(GroupKeyRotationType.AdminGroupKeyRotationMultipleAdminAccount),
]
.flat()
.filter(isNotNull)
let customerGroupKeyRotationArray = keyRotationsByType.get(GroupKeyRotationType.Customer) || []
const adminOrUserGroupKeyRotation = adminOrUserGroupKeyRotationArray[0]
this.pendingKeyRotations = {
pwKey: this.pendingKeyRotations.pwKey,
adminOrUserGroupKeyRotation: adminOrUserGroupKeyRotationArray ? adminOrUserGroupKeyRotationArray[0] : null,
adminOrUserGroupKeyRotation: adminOrUserGroupKeyRotation ? adminOrUserGroupKeyRotation : null,
teamOrCustomerGroupKeyRotations: customerGroupKeyRotationArray.concat(keyRotationsByType.get(GroupKeyRotationType.Team) || []),
userAreaGroupsKeyRotations: keyRotationsByType.get(GroupKeyRotationType.UserArea) || [],
}
Expand All @@ -204,10 +223,13 @@ export class KeyRotationFacade {
// first admin, then user and then user area
try {
if (this.pendingKeyRotations.adminOrUserGroupKeyRotation && this.pendingKeyRotations.pwKey) {
switch (this.pendingKeyRotations.adminOrUserGroupKeyRotation.groupKeyRotationType) {
// Currently we only support updating user area groups, so we ignore these,
// but we leave the instances in place in case another client supports them.
case GroupKeyRotationType.Admin:
const groupKeyRotationType = assertEnumValue(GroupKeyRotationType, this.pendingKeyRotations.adminOrUserGroupKeyRotation.groupKeyRotationType)
switch (groupKeyRotationType) {
case GroupKeyRotationType.AdminGroupKeyRotationMultipleAdminAccount:
console.log("Rotating the admin group with multiple members is not yet implemented")
break
case GroupKeyRotationType.AdminGroupKeyRotationSingleUserAccount:
case GroupKeyRotationType.AdminGroupKeyRotationMultipleUserAccount:
await this.rotateAdminGroupKeys(user, this.pendingKeyRotations.pwKey, this.pendingKeyRotations.adminOrUserGroupKeyRotation)
break
case GroupKeyRotationType.User:
Expand Down Expand Up @@ -272,7 +294,7 @@ export class KeyRotationFacade {
}> {
//group key rotation is skipped if
// * user is not an admin user
const adminGroupMembership = user.memberships.find((m) => m.groupType === GroupKeyRotationType.Admin)
const adminGroupMembership = user.memberships.find((m) => m.groupType === GroupKeyRotationType.AdminGroupKeyRotationSingleUserAccount)
if (adminGroupMembership == null) {
// group key rotations are currently only scheduled for single user customers, so this user must be an admin
console.log("Only admin user can rotate the group")
Expand Down Expand Up @@ -308,7 +330,7 @@ export class KeyRotationFacade {
private async rotateCustomerOrTeamGroupKeys(user: User) {
//group key rotation is skipped if
// * user is not an admin user
const adminGroupMembership = user.memberships.find((m) => m.groupType === GroupKeyRotationType.Admin)
const adminGroupMembership = user.memberships.find((m) => m.groupType === GroupKeyRotationType.AdminGroupKeyRotationSingleUserAccount)
if (adminGroupMembership == null) {
console.log("Only admin user can rotate the group")
return
Expand Down
4 changes: 2 additions & 2 deletions test/tests/api/worker/facades/KeyRotationFacadeTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ o.spec("KeyRotationFacadeTest", function () {
adminOrUserGroupKeyRotation: createTestEntity(KeyRotationTypeRef, {
_id: [keyRotationsListId, adminGroupId],
targetKeyVersion: String(Number(adminGroup.groupKeyVersion) + 1),
groupKeyRotationType: GroupKeyRotationType.Admin,
groupKeyRotationType: GroupKeyRotationType.AdminGroupKeyRotationSingleUserAccount,
}),
teamOrCustomerGroupKeyRotations: [],
userAreaGroupsKeyRotations: [],
Expand Down Expand Up @@ -791,7 +791,7 @@ o.spec("KeyRotationFacadeTest", function () {
adminOrUserGroupKeyRotation: createTestEntity(KeyRotationTypeRef, {
_id: [keyRotationsListId, adminGroupId],
targetKeyVersion: String(Number(adminGroup.groupKeyVersion) + 1),
groupKeyRotationType: GroupKeyRotationType.Admin,
groupKeyRotationType: GroupKeyRotationType.AdminGroupKeyRotationSingleUserAccount,
}),
teamOrCustomerGroupKeyRotations: [],
userAreaGroupsKeyRotations: [],
Expand Down

0 comments on commit 40d9224

Please sign in to comment.