Merge pull request #662 from mpretty-cyro/fix/hidden-mods-admins-not-recognised

Fixed an issue where hidden mods/admins wouldn't be properly recognised
This commit is contained in:
Morgan Pretty 2022-08-16 14:00:53 +10:00 committed by GitHub
commit d0177c3cee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 539 additions and 51 deletions

View File

@ -66,6 +66,9 @@ abstract_target 'GlobalDependencies' do
pod 'Quick'
pod 'Nimble'
# Need to include this for the tests because otherwise it won't actually build
pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage'
end
end

View File

@ -242,6 +242,6 @@ SPEC CHECKSUMS:
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: f0857369c4831b2e5c1946345e76e493f3286805
PODFILE CHECKSUM: 0e694576fbda3c10bbc762998183d97142b85896
COCOAPODS: 1.11.3

View File

@ -645,6 +645,9 @@
FD245C6C2850669200B966DD /* MessageReceiveJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A31225574F5200338F3E /* MessageReceiveJob.swift */; };
FD245C6D285066A400B966DD /* NotifyPushServerJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A32E2557549C00338F3E /* NotifyPushServerJob.swift */; };
FD37EA0D28AB2A45003AE748 /* _005_FixDeletedMessageReadState.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37EA0C28AB2A45003AE748 /* _005_FixDeletedMessageReadState.swift */; };
FD37EA0F28AB3330003AE748 /* _006_FixHiddenModAdminSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37EA0E28AB3330003AE748 /* _006_FixHiddenModAdminSupport.swift */; };
FD37EA1128AB34B3003AE748 /* TypedTableAlteration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37EA1028AB34B3003AE748 /* TypedTableAlteration.swift */; };
FD37EA1528AB42CB003AE748 /* IdentitySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD37EA1428AB42CB003AE748 /* IdentitySpec.swift */; };
FD3AABE928306BBD00E5099A /* ThreadPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3AABE828306BBD00E5099A /* ThreadPickerViewModel.swift */; };
FD3C905C27E3FBEF00CD579F /* BatchRequestInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */; };
FD3C906027E410F700CD579F /* FileUploadResponseSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */; };
@ -1685,6 +1688,9 @@
FD28A4F327EA79F800FF65E7 /* BlockListUIUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListUIUtils.swift; sourceTree = "<group>"; };
FD28A4F527EAD44C00FF65E7 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
FD37EA0C28AB2A45003AE748 /* _005_FixDeletedMessageReadState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _005_FixDeletedMessageReadState.swift; sourceTree = "<group>"; };
FD37EA0E28AB3330003AE748 /* _006_FixHiddenModAdminSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _006_FixHiddenModAdminSupport.swift; sourceTree = "<group>"; };
FD37EA1028AB34B3003AE748 /* TypedTableAlteration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedTableAlteration.swift; sourceTree = "<group>"; };
FD37EA1428AB42CB003AE748 /* IdentitySpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentitySpec.swift; sourceTree = "<group>"; };
FD3AABE828306BBD00E5099A /* ThreadPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadPickerViewModel.swift; sourceTree = "<group>"; };
FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchRequestInfoSpec.swift; sourceTree = "<group>"; };
FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadResponseSpec.swift; sourceTree = "<group>"; };
@ -3457,6 +3463,7 @@
FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */,
FDF40CDD2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift */,
FD37EA0C28AB2A45003AE748 /* _005_FixDeletedMessageReadState.swift */,
FD37EA0E28AB3330003AE748 /* _006_FixHiddenModAdminSupport.swift */,
);
path = Migrations;
sourceTree = "<group>";
@ -3523,6 +3530,7 @@
FD17D7B727F51ECA00122BE0 /* Migration.swift */,
FD17D7B927F51F2100122BE0 /* TargetMigrations.swift */,
FD17D7C027F5200100122BE0 /* TypedTableDefinition.swift */,
FD37EA1028AB34B3003AE748 /* TypedTableAlteration.swift */,
FD7162DA281B6C440060647B /* TypedTableAlias.swift */,
FD848B8A283DC509000E298B /* PagedDatabaseObserver.swift */,
);
@ -3570,6 +3578,22 @@
path = LegacyDatabase;
sourceTree = "<group>";
};
FD37EA1228AB3F60003AE748 /* Database */ = {
isa = PBXGroup;
children = (
FD37EA1328AB42C1003AE748 /* Models */,
);
path = Database;
sourceTree = "<group>";
};
FD37EA1328AB42C1003AE748 /* Models */ = {
isa = PBXGroup;
children = (
FD37EA1428AB42CB003AE748 /* IdentitySpec.swift */,
);
path = Models;
sourceTree = "<group>";
};
FD3C905D27E410DB00CD579F /* Common Networking */ = {
isa = PBXGroup;
children = (
@ -3659,6 +3683,7 @@
FD83B9B027CF200A005E1583 /* SessionUtilitiesKitTests */ = {
isa = PBXGroup;
children = (
FD37EA1228AB3F60003AE748 /* Database */,
FD83B9B927CF20A5005E1583 /* General */,
);
path = SessionUtilitiesKitTests;
@ -5018,6 +5043,7 @@
FD17D7CA27F546D900122BE0 /* _001_InitialSetupMigration.swift in Sources */,
C3D9E4D12567777D0040E4F3 /* OWSMediaUtils.swift in Sources */,
C3BBE0AA2554D4DE0050F1E3 /* Dictionary+Utilities.swift in Sources */,
FD37EA1128AB34B3003AE748 /* TypedTableAlteration.swift in Sources */,
C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */,
C32C600F256E07F5003C73A2 /* NSUserDefaults+OWS.m in Sources */,
C3D9E35E25675F640040E4F3 /* OWSFileSystem.m in Sources */,
@ -5125,6 +5151,7 @@
7BFD1A8C2747150E00FB91B9 /* TurnServerInfo.swift in Sources */,
FDC4386527B4DE7600C60D73 /* RoomPollInfo.swift in Sources */,
FD245C6B2850667400B966DD /* VisibleMessage+Profile.swift in Sources */,
FD37EA0F28AB3330003AE748 /* _006_FixHiddenModAdminSupport.swift in Sources */,
C3D9E3BF25676AD70040E4F3 /* (null) in Sources */,
B8BF43BA26CC95FB007828D1 /* WebRTC+Utilities.swift in Sources */,
FDC438A627BB113A00C60D73 /* UserUnbanRequest.swift in Sources */,
@ -5444,6 +5471,7 @@
FD83B9BF27CF2294005E1583 /* TestConstants.swift in Sources */,
FD83B9BB27CF20AF005E1583 /* SessionIdSpec.swift in Sources */,
FDC290A927D9B46D005DAE71 /* NimbleExtensions.swift in Sources */,
FD37EA1528AB42CB003AE748 /* IdentitySpec.swift in Sources */,
FDC290AA27D9B6FD005DAE71 /* Mock.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -256,7 +256,8 @@ enum MockDataGenerator {
_ = try! GroupMember(
groupId: randomGroupPublicKey,
profileId: memberId,
role: .standard
role: .standard,
isHidden: false
)
.saved(db)
}
@ -264,7 +265,8 @@ enum MockDataGenerator {
_ = try! GroupMember(
groupId: randomGroupPublicKey,
profileId: adminId,
role: .admin
role: .admin,
isHidden: false
)
.saved(db)
}

View File

@ -17,7 +17,8 @@ public enum SNMessagingKit { // Just to make the external API nice
_004_RemoveLegacyYDB.self
],
[
_005_FixDeletedMessageReadState.self
_005_FixDeletedMessageReadState.self,
_006_FixHiddenModAdminSupport.self
]
]
)

View File

@ -650,7 +650,8 @@ enum _003_YDBToGRDBMigration: Migration {
try GroupMember(
groupId: threadId,
profileId: memberId,
role: .standard
role: .standard,
isHidden: false
).insert(db)
if !validProfileIds.contains(memberId) {
@ -662,7 +663,8 @@ enum _003_YDBToGRDBMigration: Migration {
try GroupMember(
groupId: threadId,
profileId: adminId,
role: .admin
role: .admin,
isHidden: false
).insert(db)
if !validProfileIds.contains(adminId) {
@ -674,7 +676,8 @@ enum _003_YDBToGRDBMigration: Migration {
try GroupMember(
groupId: threadId,
profileId: zombieId,
role: .zombie
role: .zombie,
isHidden: false
).insert(db)
if !validProfileIds.contains(zombieId) {

View File

@ -0,0 +1,30 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import SessionUtilitiesKit
/// This migration fixes an issue where hidden mods/admins weren't getting recognised as mods/admins, it reset's the `info_updates`
/// for open groups so they will fully re-fetch their mod/admin lists
enum _006_FixHiddenModAdminSupport: Migration {
static let target: TargetMigrations.Identifier = .messagingKit
static let identifier: String = "FixHiddenModAdminSupport"
static let needsConfigSync: Bool = false
static let minExpectedRunDuration: TimeInterval = 0.1
static func migrate(_ db: Database) throws {
try db.alter(table: GroupMember.self) { t in
t.add(.isHidden, .boolean)
.notNull()
.defaults(to: false)
}
// When modifying OpenGroup behaviours we should always look to reset the `infoUpdates`
// value for all OpenGroups to ensure they all have the correct state for newly
// added/changed fields
_ = try OpenGroup
.updateAll(db, OpenGroup.Columns.infoUpdates.set(to: 0))
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
}
}

View File

@ -17,6 +17,7 @@ public struct GroupMember: Codable, Equatable, FetchableRecord, PersistableRecor
case groupId
case profileId
case role
case isHidden
}
public enum Role: Int, Codable, DatabaseValueConvertible {
@ -29,6 +30,7 @@ public struct GroupMember: Codable, Equatable, FetchableRecord, PersistableRecor
public let groupId: String
public let profileId: String
public let role: Role
public let isHidden: Bool
// MARK: - Relationships
@ -49,11 +51,13 @@ public struct GroupMember: Codable, Equatable, FetchableRecord, PersistableRecor
public init(
groupId: String,
profileId: String,
role: Role
role: Role,
isHidden: Bool
) {
self.groupId = groupId
self.profileId = profileId
self.role = role
self.isHidden = isHidden
}
}

View File

@ -422,17 +422,41 @@ public final class OpenGroupManager: NSObject {
_ = try GroupMember(
groupId: threadId,
profileId: adminId,
role: .admin
role: .admin,
isHidden: false
).saved(db)
}
try roomDetails.hiddenAdmins
.defaulting(to: [])
.forEach { adminId in
_ = try GroupMember(
groupId: threadId,
profileId: adminId,
role: .admin,
isHidden: true
).saved(db)
}
try roomDetails.moderators.forEach { moderatorId in
_ = try GroupMember(
groupId: threadId,
profileId: moderatorId,
role: .moderator
role: .moderator,
isHidden: false
).saved(db)
}
try roomDetails.hiddenModerators
.defaulting(to: [])
.forEach { moderatorId in
_ = try GroupMember(
groupId: threadId,
profileId: moderatorId,
role: .moderator,
isHidden: true
).saved(db)
}
}
db.afterNextTransactionCommit { db in

View File

@ -89,7 +89,8 @@ extension MessageReceiver {
try GroupMember(
groupId: groupPublicKey,
profileId: memberId,
role: .standard
role: .standard,
isHidden: false
).save(db)
}
@ -97,7 +98,8 @@ extension MessageReceiver {
try GroupMember(
groupId: groupPublicKey,
profileId: adminId,
role: .admin
role: .admin,
isHidden: false
).save(db)
}
@ -254,7 +256,8 @@ extension MessageReceiver {
try GroupMember(
groupId: id,
profileId: memberId,
role: .standard
role: .standard,
isHidden: false
).insert(db)
}
@ -440,7 +443,8 @@ extension MessageReceiver {
try GroupMember(
groupId: id,
profileId: sender,
role: .zombie
role: .zombie,
isHidden: false
).insert(db)
}

View File

@ -37,7 +37,8 @@ extension MessageSender {
try GroupMember(
groupId: groupPublicKey,
profileId: adminId,
role: .admin
role: .admin,
isHidden: false
).insert(db)
}
@ -48,7 +49,8 @@ extension MessageSender {
try GroupMember(
groupId: groupPublicKey,
profileId: memberId,
role: .standard
role: .standard,
isHidden: false
).insert(db)
}
@ -374,7 +376,8 @@ extension MessageSender {
try GroupMember(
groupId: closedGroup.id,
profileId: member,
role: .standard
role: .standard,
isHidden: false
).insert(db)
}
}

View File

@ -37,7 +37,7 @@ class CapabilitiesSpec: QuickSpec {
describe("a Capability") {
context("when initializing") {
it("succeeeds with a valid case") {
let capability: OpenGroupAPI.Capabilities.Capability = OpenGroupAPI.Capabilities.Capability(
let capability: Capability.Variant = Capability.Variant(
from: "sogs"
)
@ -45,7 +45,7 @@ class CapabilitiesSpec: QuickSpec {
}
it("wraps an unknown value in the unsupported case") {
let capability: OpenGroupAPI.Capabilities.Capability = OpenGroupAPI.Capabilities.Capability(
let capability: Capability.Variant = Capability.Variant(
from: "test"
)
@ -55,12 +55,12 @@ class CapabilitiesSpec: QuickSpec {
context("when accessing the rawValue") {
it("provides known cases exactly") {
expect(OpenGroupAPI.Capabilities.Capability.sogs.rawValue).to(equal("sogs"))
expect(OpenGroupAPI.Capabilities.Capability.blind.rawValue).to(equal("blind"))
expect(Capability.Variant.sogs.rawValue).to(equal("sogs"))
expect(Capability.Variant.blind.rawValue).to(equal("blind"))
}
it("provides the wrapped value for unsupported cases") {
expect(OpenGroupAPI.Capabilities.Capability.unsupported("test").rawValue).to(equal("test"))
expect(Capability.Variant.unsupported("test").rawValue).to(equal("test"))
}
}
@ -68,14 +68,14 @@ class CapabilitiesSpec: QuickSpec {
it("decodes known cases exactly") {
expect(
try? JSONDecoder().decode(
OpenGroupAPI.Capabilities.Capability.self,
Capability.Variant.self,
from: "\"sogs\"".data(using: .utf8)!
)
)
.to(equal(.sogs))
expect(
try? JSONDecoder().decode(
OpenGroupAPI.Capabilities.Capability.self,
Capability.Variant.self,
from: "\"blind\"".data(using: .utf8)!
)
)
@ -85,7 +85,7 @@ class CapabilitiesSpec: QuickSpec {
it("decodes unknown cases into the unsupported case") {
expect(
try? JSONDecoder().decode(
OpenGroupAPI.Capabilities.Capability.self,
Capability.Variant.self,
from: "\"test\"".data(using: .utf8)!
)
)

View File

@ -76,7 +76,7 @@ class OpenGroupSpec: QuickSpec {
)
expect(openGroup.debugDescription)
.to(equal("OpenGroup(server: \"server\", roomToken: \"room\", id: \"server.room\", publicKey: \"1234\", isActive: true, name: \"name\", roomDescription: null, imageId: null, userCount: 0, infoUpdates: 0, sequenceNumber: 0, inboxLatestMessageId: 0, outboxLatestMessageId: 0)"))
.to(equal("OpenGroup(server: \"server\", roomToken: \"room\", id: \"server.room\", publicKey: \"1234\", isActive: true, name: \"name\", roomDescription: null, imageId: null, userCount: 0, infoUpdates: 0, sequenceNumber: 0, inboxLatestMessageId: 0, outboxLatestMessageId: 0, pollFailureCount: 0)"))
}
}
}

View File

@ -1237,6 +1237,7 @@ class OpenGroupAPISpec: QuickSpec {
sender: "testSender",
posted: 321,
edited: nil,
deleted: nil,
seqNo: 10,
whisper: false,
whisperMods: false,
@ -1605,6 +1606,7 @@ class OpenGroupAPISpec: QuickSpec {
sender: "testSender",
posted: 321,
edited: nil,
deleted: nil,
seqNo: 10,
whisper: false,
whisperMods: false,

View File

@ -186,6 +186,7 @@ class OpenGroupManagerSpec: QuickSpec {
sender: "05\(TestConstants.publicKey)",
posted: 123,
edited: nil,
deleted: nil,
seqNo: 124,
whisper: false,
whisperMods: false,
@ -1277,7 +1278,12 @@ class OpenGroupManagerSpec: QuickSpec {
defaultWrite: nil,
upload: false,
defaultUpload: nil,
details: TestCapabilitiesAndRoomApi.roomData.with(moderators: ["TestMod"], admins: [])
details: TestCapabilitiesAndRoomApi.roomData.with(
moderators: ["TestMod"],
hiddenModerators: [],
admins: [],
hiddenAdmins: []
)
)
mockStorage.write { db in
@ -1308,7 +1314,67 @@ class OpenGroupManagerSpec: QuickSpec {
server: "testServer"
),
profileId: "TestMod",
role: .moderator
role: .moderator,
isHidden: false
)
))
}
it("updates for hidden moderators") {
var didComplete: Bool = false // Prevent multi-threading test bugs
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
activeUsers: 10,
admin: false,
globalAdmin: false,
moderator: false,
globalModerator: false,
read: false,
defaultRead: nil,
defaultAccessible: nil,
write: false,
defaultWrite: nil,
upload: false,
defaultUpload: nil,
details: TestCapabilitiesAndRoomApi.roomData.with(
moderators: [],
hiddenModerators: ["TestMod2"],
admins: [],
hiddenAdmins: []
)
)
mockStorage.write { db in
try OpenGroupManager.handlePollInfo(
db,
pollInfo: testPollInfo,
publicKey: TestConstants.publicKey,
for: "testRoom",
on: "testServer",
dependencies: dependencies
) { didComplete = true }
}
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(50))
expect(
mockStorage.read { db in
try GroupMember
.filter(GroupMember.Columns.groupId == OpenGroup.idFor(
roomToken: "testRoom",
server: "testServer"
))
.fetchOne(db)
}
).to(equal(
GroupMember(
groupId: OpenGroup.idFor(
roomToken: "testRoom",
server: "testServer"
),
profileId: "TestMod2",
role: .moderator,
isHidden: true
)
))
}
@ -1368,7 +1434,12 @@ class OpenGroupManagerSpec: QuickSpec {
defaultWrite: nil,
upload: false,
defaultUpload: nil,
details: TestCapabilitiesAndRoomApi.roomData.with(moderators: [], admins: ["TestAdmin"])
details: TestCapabilitiesAndRoomApi.roomData.with(
moderators: [],
hiddenModerators: [],
admins: ["TestAdmin"],
hiddenAdmins: []
)
)
mockStorage.write { db in
@ -1399,7 +1470,67 @@ class OpenGroupManagerSpec: QuickSpec {
server: "testServer"
),
profileId: "TestAdmin",
role: .admin
role: .admin,
isHidden: false
)
))
}
it("updates for hidden admins") {
var didComplete: Bool = false // Prevent multi-threading test bugs
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
activeUsers: 10,
admin: false,
globalAdmin: false,
moderator: false,
globalModerator: false,
read: false,
defaultRead: nil,
defaultAccessible: nil,
write: false,
defaultWrite: nil,
upload: false,
defaultUpload: nil,
details: TestCapabilitiesAndRoomApi.roomData.with(
moderators: [],
hiddenModerators: [],
admins: [],
hiddenAdmins: ["TestAdmin2"]
)
)
mockStorage.write { db in
try OpenGroupManager.handlePollInfo(
db,
pollInfo: testPollInfo,
publicKey: TestConstants.publicKey,
for: "testRoom",
on: "testServer",
dependencies: dependencies
) { didComplete = true }
}
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(50))
expect(
mockStorage.read { db in
try GroupMember
.filter(GroupMember.Columns.groupId == OpenGroup.idFor(
roomToken: "testRoom",
server: "testServer"
))
.fetchOne(db)
}
).to(equal(
GroupMember(
groupId: OpenGroup.idFor(
roomToken: "testRoom",
server: "testServer"
),
profileId: "TestAdmin2",
role: .admin,
isHidden: true
)
))
}
@ -1978,6 +2109,7 @@ class OpenGroupManagerSpec: QuickSpec {
sender: nil,
posted: 123,
edited: nil,
deleted: nil,
seqNo: 124,
whisper: false,
whisperMods: false,
@ -2039,6 +2171,7 @@ class OpenGroupManagerSpec: QuickSpec {
sender: nil,
posted: 123,
edited: nil,
deleted: nil,
seqNo: 124,
whisper: false,
whisperMods: false,
@ -2071,6 +2204,7 @@ class OpenGroupManagerSpec: QuickSpec {
sender: "05\(TestConstants.publicKey)",
posted: 123,
edited: nil,
deleted: nil,
seqNo: 124,
whisper: false,
whisperMods: false,
@ -2114,6 +2248,7 @@ class OpenGroupManagerSpec: QuickSpec {
sender: "05\(TestConstants.publicKey)",
posted: 122,
edited: nil,
deleted: nil,
seqNo: 123,
whisper: false,
whisperMods: false,
@ -2152,6 +2287,7 @@ class OpenGroupManagerSpec: QuickSpec {
sender: "05\(TestConstants.publicKey)",
posted: 123,
edited: nil,
deleted: nil,
seqNo: 123,
whisper: false,
whisperMods: false,
@ -2180,6 +2316,7 @@ class OpenGroupManagerSpec: QuickSpec {
sender: "05\(TestConstants.publicKey)",
posted: 123,
edited: nil,
deleted: nil,
seqNo: 123,
whisper: false,
whisperMods: false,
@ -2328,6 +2465,10 @@ class OpenGroupManagerSpec: QuickSpec {
mockSodium
.when { $0.combineKeys(lhsKeyBytes: anyArray(), rhsKeyBytes: anyArray()) }
.thenReturn(Data(hex: testDirectMessage.sender.removingIdPrefixIfNeeded()).bytes)
mockSodium
.when { $0.sessionId(any(), matchesBlindedId: any(), serverPublicKey: any(), genericHash: mockGenericHash) }
.thenReturn(false)
}
it("updates the inbox latest message id") {
@ -2422,6 +2563,10 @@ class OpenGroupManagerSpec: QuickSpec {
mockSodium
.when { $0.combineKeys(lhsKeyBytes: anyArray(), rhsKeyBytes: anyArray()) }
.thenReturn(Data(hex: testDirectMessage.recipient.removingIdPrefixIfNeeded()).bytes)
mockSodium
.when { $0.sessionId(any(), matchesBlindedId: any(), serverPublicKey: any(), genericHash: mockGenericHash) }
.thenReturn(false)
}
it("updates the outbox latest message id") {
@ -2602,7 +2747,8 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "05\(TestConstants.publicKey)",
role: .moderator
role: .moderator,
isHidden: false
).insert(db)
}
@ -2621,7 +2767,48 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "05\(TestConstants.publicKey)",
role: .admin
role: .admin,
isHidden: false
).insert(db)
}
expect(
OpenGroupManager.isUserModeratorOrAdmin(
"05\(TestConstants.publicKey)",
for: "testRoom",
on: "testServer",
using: dependencies
)
).to(beTrue())
}
it("returns true if the moderator is hidden") {
mockStorage.write { db in
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "05\(TestConstants.publicKey)",
role: .moderator,
isHidden: true
).insert(db)
}
expect(
OpenGroupManager.isUserModeratorOrAdmin(
"05\(TestConstants.publicKey)",
for: "testRoom",
on: "testServer",
using: dependencies
)
).to(beTrue())
}
it("returns true if the admin is hidden") {
mockStorage.write { db in
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "05\(TestConstants.publicKey)",
role: .admin,
isHidden: true
).insert(db)
}
@ -2672,7 +2859,8 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "00\(otherKey)",
role: .moderator
role: .moderator,
isHidden: false
).insert(db)
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
@ -2709,7 +2897,8 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "15\(otherKey)",
role: .moderator
role: .moderator,
isHidden: false
).insert(db)
}
@ -2766,7 +2955,8 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "05\(otherKey)",
role: .moderator
role: .moderator,
isHidden: false
).insert(db)
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
@ -2805,7 +2995,8 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "15\(otherKey)",
role: .moderator
role: .moderator,
isHidden: false
).insert(db)
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).save(db)
@ -2911,7 +3102,8 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "05\(otherKey)",
role: .moderator
role: .moderator,
isHidden: false
).insert(db)
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
@ -2951,7 +3143,8 @@ class OpenGroupManagerSpec: QuickSpec {
try GroupMember(
groupId: OpenGroup.idFor(roomToken: "testRoom", server: "testServer"),
profileId: "00\(otherKey)",
role: .moderator
role: .moderator,
isHidden: false
).insert(db)
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).save(db)
@ -2977,6 +3170,7 @@ class OpenGroupManagerSpec: QuickSpec {
context("when getting the default rooms if needed") {
beforeEach {
class TestRoomsApi: TestOnionRequestAPI {
static let capabilitiesData: OpenGroupAPI.Capabilities = OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: nil)
static let roomsData: [OpenGroupAPI.Room] = [
TestCapabilitiesAndRoomApi.roomData,
OpenGroupAPI.Room(
@ -3009,7 +3203,26 @@ class OpenGroupManagerSpec: QuickSpec {
]
override class var mockResponse: Data? {
return try! JSONEncoder().encode(roomsData)
let responses: [Data] = [
try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse(
code: 200,
headers: [:],
body: capabilitiesData,
failedToParseBody: false
)
),
try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse(
code: 200,
headers: [:],
body: roomsData,
failedToParseBody: false
)
)
]
return "[\(responses.map { String(data: $0, encoding: .utf8)! }.joined(separator: ","))]".data(using: .utf8)
}
}
dependencies = dependencies.with(onionApi: TestRoomsApi.self)
@ -3178,6 +3391,7 @@ class OpenGroupManagerSpec: QuickSpec {
it("fetches the image for any rooms with images") {
class TestRoomsApi: TestOnionRequestAPI {
static let capabilitiesData: OpenGroupAPI.Capabilities = OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: nil)
static let roomsData: [OpenGroupAPI.Room] = [
OpenGroupAPI.Room(
token: "test2",
@ -3209,7 +3423,26 @@ class OpenGroupManagerSpec: QuickSpec {
]
override class var mockResponse: Data? {
return try! JSONEncoder().encode(roomsData)
let responses: [Data] = [
try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse(
code: 200,
headers: [:],
body: capabilitiesData,
failedToParseBody: false
)
),
try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse(
code: 200,
headers: [:],
body: roomsData,
failedToParseBody: false
)
)
]
return "[\(responses.map { String(data: $0, encoding: .utf8)! }.joined(separator: ","))]".data(using: .utf8)
}
}
let testDate: Date = Date(timeIntervalSince1970: 1234567890)
@ -3218,7 +3451,9 @@ class OpenGroupManagerSpec: QuickSpec {
date: testDate
)
OpenGroupManager.getDefaultRoomsIfNeeded(using: dependencies)
OpenGroupManager
.getDefaultRoomsIfNeeded(using: dependencies)
.retainUntilComplete()
expect(mockUserDefaults)
.toEventually(
@ -3674,7 +3909,12 @@ class OpenGroupManagerSpec: QuickSpec {
// MARK: - Room Convenience Extensions
extension OpenGroupAPI.Room {
func with(moderators: [String], admins: [String]) -> OpenGroupAPI.Room {
func with(
moderators: [String],
hiddenModerators: [String],
admins: [String],
hiddenAdmins: [String]
) -> OpenGroupAPI.Room {
return OpenGroupAPI.Room(
token: self.token,
name: self.name,
@ -3689,11 +3929,11 @@ extension OpenGroupAPI.Room {
admin: self.admin,
globalAdmin: self.globalAdmin,
admins: admins,
hiddenAdmins: self.hiddenAdmins,
hiddenAdmins: hiddenAdmins,
moderator: self.moderator,
globalModerator: self.globalModerator,
moderators: moderators,
hiddenModerators: self.hiddenModerators,
hiddenModerators: hiddenModerators,
read: self.read,
defaultRead: self.defaultRead,
defaultAccessible: self.defaultAccessible,

View File

@ -129,14 +129,16 @@ public extension Identity {
)
}
static func fetchHexEncodedSeed() -> String? {
return Storage.shared.read { db in
guard let data: Data = try? Identity.fetchOne(db, id: .seed)?.data else {
return nil
}
return data.toHexString()
static func fetchHexEncodedSeed(_ db: Database? = nil) -> String? {
guard let db: Database = db else {
return Storage.shared.read { db in fetchHexEncodedSeed(db) }
}
guard let data: Data = try? Identity.fetchOne(db, id: .seed)?.data else {
return nil
}
return data.toHexString()
}
}

View File

@ -0,0 +1,26 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
/// This is a convenience wrapper around the GRDB `TableAlteration` class which allows for shorthand
/// when creating tables
public class TypedTableAlteration<T> where T: TableRecord, T: ColumnExpressible {
let alteration: TableAlteration
init(alteration: TableAlteration) {
self.alteration = alteration
}
@discardableResult public func add(_ key: T.Columns, _ type: Database.ColumnType? = nil) -> ColumnDefinition {
return alteration.add(column: key.name, type)
}
public func rename(column: String, to key: T.Columns) {
alteration.rename(column: column, to: key.name)
}
public func drop(_ key: T.Columns) {
return alteration.drop(column: key.name)
}
}

View File

@ -16,6 +16,17 @@ public extension Database {
}
}
func alter<T>(
table: T.Type,
body: (TypedTableAlteration<T>) -> Void
) throws where T: TableRecord, T: ColumnExpressible {
try alter(table: T.databaseTableName) { tableAlteration in
let typedAlteration: TypedTableAlteration<T> = TypedTableAlteration(alteration: tableAlteration)
body(typedAlteration)
}
}
func makeFTS5Pattern<T>(rawPattern: String, forTable table: T.Type) throws -> FTS5Pattern where T: TableRecord, T: ColumnExpressible {
return try makeFTS5Pattern(rawPattern: rawPattern, forTable: table.databaseTableName)
}

View File

@ -0,0 +1,105 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import Quick
import Nimble
@testable import SessionUtilitiesKit
class IdentitySpec: QuickSpec {
// MARK: - Spec
override func spec() {
var mockStorage: Storage!
describe("an Identity") {
beforeEach {
mockStorage = Storage(
customWriter: DatabaseQueue(),
customMigrations: [
SNUtilitiesKit.migrations()
]
)
}
it("correctly retrieves the user user public key") {
mockStorage.write { db in
try Identity(variant: .x25519PublicKey, data: "Test1".data(using: .utf8)!).insert(db)
}
mockStorage.read { db in
expect(Identity.fetchUserPublicKey(db))
.to(equal("Test1".data(using: .utf8)))
}
}
it("correctly retrieves the user private key") {
mockStorage.write { db in
try Identity(variant: .x25519PrivateKey, data: "Test2".data(using: .utf8)!).insert(db)
}
mockStorage.read { db in
expect(Identity.fetchUserPrivateKey(db))
.to(equal("Test2".data(using: .utf8)))
}
}
it("correctly retrieves the user key pair") {
mockStorage.write { db in
try Identity(variant: .x25519PublicKey, data: "Test3".data(using: .utf8)!).insert(db)
try Identity(variant: .x25519PrivateKey, data: "Test4".data(using: .utf8)!).insert(db)
}
mockStorage.read { db in
let keyPair = Identity.fetchUserKeyPair(db)
expect(keyPair?.publicKey)
.to(equal("Test3".data(using: .utf8)?.bytes))
expect(keyPair?.secretKey)
.to(equal("Test4".data(using: .utf8)?.bytes))
}
}
it("correctly determines if the user exists") {
mockStorage.write { db in
try Identity(variant: .x25519PublicKey, data: "Test3".data(using: .utf8)!).insert(db)
try Identity(variant: .x25519PrivateKey, data: "Test4".data(using: .utf8)!).insert(db)
}
mockStorage.read { db in
expect(Identity.userExists(db))
.to(equal(true))
}
}
it("correctly retrieves the user ED25519 key pair") {
mockStorage.write { db in
try Identity(variant: .ed25519PublicKey, data: "Test5".data(using: .utf8)!).insert(db)
try Identity(variant: .ed25519SecretKey, data: "Test6".data(using: .utf8)!).insert(db)
}
mockStorage.read { db in
let keyPair = Identity.fetchUserEd25519KeyPair(db)
expect(keyPair?.publicKey)
.to(equal("Test5".data(using: .utf8)?.bytes))
expect(keyPair?.secretKey)
.to(equal("Test6".data(using: .utf8)?.bytes))
}
}
it("correctly retrieves the hex encoded seed") {
mockStorage.write { db in
try Identity(variant: .seed, data: "Test7".data(using: .utf8)!).insert(db)
}
mockStorage.read { db in
expect(Identity.fetchHexEncodedSeed(db))
.to(equal("5465737437"))
}
}
}
}
}