Change license to AGPL
This commit:
- Updates the `LICENSE` file
- Start every file with something like:
// Copyright YEAR_FIRST_PUBLISHED Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
---
First, I removed existing license headers with this Ruby 3.1.2 script:
require 'set'
EXTENSIONS_TO_CHECK = Set['.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift']
same = 0
different = 0
all_files = `git ls-files`.lines.map { |line| line.strip }
all_files.each do |relative_path|
if relative_path == 'Pods'
next
end
unless EXTENSIONS_TO_CHECK.include? File.extname(relative_path)
next
end
path = File.expand_path(relative_path)
contents = File.read(path)
new_contents = contents.sub(/\/\/\n\/\/ Copyright .*\n\/\/\n\n/, '')
if contents == new_contents
same += 1
else
different += 1
end
File.write(path, new_contents)
end
puts "updated #{different} file(s), left #{same} untouched"
I'm sure this script could be improved, but it worked well enough.
Then, I created `Scripts/lint/lint-license-headers` and ran it to auto-
fix a lot of files. This changed the mode of some files, but I think
that's actually desirable. For example,
`SignalServiceKit/src/Util/AppContext.m` previously had a mode of
`0755/-rwxr-xr-x`, and it's now `0644/-rw-r--r--`.
Then I fixed some stragglers and updated the precommit script.
See [a similar change in the Desktop app][0].
[0]: 8bfaf598af
226 lines
11 KiB
Swift
226 lines
11 KiB
Swift
//
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import Foundation
|
|
import XCTest
|
|
|
|
@testable import SignalServiceKit
|
|
|
|
class BlockingManagerTests: SSKBaseTestSwift {
|
|
// Some tests will use this to simulate the state as seen by another process
|
|
// If working correctly, state should be reloaded
|
|
var remoteState = BlockingManager.State._testing_createEmpty()
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
databaseStorage.read { remoteState.reloadIfNecessary($0) }
|
|
}
|
|
|
|
override func tearDown() {
|
|
super.tearDown()
|
|
BlockingManager.TestingFlags.optimisticallyCommitSyncToken = false
|
|
}
|
|
|
|
func testAddBlockedAddress() {
|
|
// Setup
|
|
let generatedAddresses = generateAddresses(count: 30)
|
|
let generatedThread = ContactThreadFactory().create()
|
|
|
|
// Test
|
|
expectation(forNotification: BlockingManager.blockListDidChange, object: nil)
|
|
databaseStorage.write { writeTx in
|
|
blockingManager.addBlockedThread(generatedThread, blockMode: .localShouldNotLeaveGroups, transaction: writeTx)
|
|
generatedAddresses.forEach {
|
|
blockingManager.addBlockedAddress($0, blockMode: .localShouldNotLeaveGroups, transaction: writeTx)
|
|
}
|
|
}
|
|
|
|
// Verify
|
|
databaseStorage.read { readTx in
|
|
// First, query the whole set of blocked addresses:
|
|
// Because these are made up generated addresses, a UUID+e164 that goes in may be low trust
|
|
// If that happens, the addresses that come out will be two separate SignalServiceAddresses
|
|
// To work around this, we compactMap the result sets to e164/UUID to compare apples to apples
|
|
let allFetchedBlockedAddresses = blockingManager.blockedAddresses(transaction: readTx)
|
|
let allExpectedBlockedAddresses = generatedAddresses.union([generatedThread.contactAddress])
|
|
XCTAssertEqual(
|
|
Set(allFetchedBlockedAddresses.compactMap { $0.phoneNumber }),
|
|
Set(allExpectedBlockedAddresses.compactMap { $0.phoneNumber })
|
|
)
|
|
XCTAssertEqual(
|
|
Set(allFetchedBlockedAddresses.compactMap { $0.uuidString }),
|
|
Set(allExpectedBlockedAddresses.compactMap { $0.uuidString })
|
|
)
|
|
// Next, ensure that querying an individual address or thread works properly
|
|
generatedAddresses.forEach {
|
|
XCTAssertTrue(blockingManager.isAddressBlocked($0, transaction: readTx))
|
|
}
|
|
XCTAssertTrue(blockingManager.isAddressBlocked(generatedThread.contactAddress, transaction: readTx))
|
|
XCTAssertTrue(blockingManager.isThreadBlocked(generatedThread, transaction: readTx))
|
|
|
|
// Since this was a local change, we expect to need a sync message
|
|
XCTAssertTrue(blockingManager._testingOnly_needsSyncMessage(readTx))
|
|
|
|
// Reload the remote state and ensure it sees the up-to-date block list.
|
|
let oldToken = remoteState.changeToken
|
|
remoteState.reloadIfNecessary(readTx)
|
|
let newToken = remoteState.changeToken
|
|
XCTAssertNotEqual(oldToken, newToken)
|
|
XCTAssertEqual(remoteState.blockedPhoneNumbers, Set(allExpectedBlockedAddresses.compactMap { $0.phoneNumber }))
|
|
XCTAssertEqual(remoteState.blockedUUIDStrings, Set(allExpectedBlockedAddresses.compactMap { $0.uuidString }))
|
|
XCTAssertEqual(remoteState.blockedGroupMap, [:])
|
|
}
|
|
waitForExpectations(timeout: 3)
|
|
}
|
|
|
|
func testRemoveBlockedAddress() {
|
|
// Setup
|
|
let blockedContact = CommonGenerator.address()
|
|
let unblockedContact = CommonGenerator.address()
|
|
let blockedThread = ContactThreadFactory().create()
|
|
let unblockedThread = ContactThreadFactory().create()
|
|
databaseStorage.write {
|
|
blockingManager.addBlockedAddress(blockedContact, blockMode: .localShouldLeaveGroups, transaction: $0)
|
|
blockingManager.addBlockedAddress(blockedContact, blockMode: .localShouldLeaveGroups, transaction: $0)
|
|
blockingManager.addBlockedThread(blockedThread, blockMode: .localShouldLeaveGroups, transaction: $0)
|
|
blockingManager.addBlockedThread(unblockedThread, blockMode: .localShouldLeaveGroups, transaction: $0)
|
|
}
|
|
databaseStorage.write {
|
|
remoteState.reloadIfNecessary($0)
|
|
blockingManager._testingOnly_clearNeedsSyncMessage($0)
|
|
}
|
|
|
|
// Test
|
|
databaseStorage.write {
|
|
blockingManager.removeBlockedAddress(unblockedContact, wasLocallyInitiated: true, transaction: $0)
|
|
blockingManager.removeBlockedThread(unblockedThread, wasLocallyInitiated: true, transaction: $0)
|
|
}
|
|
|
|
// Verify
|
|
databaseStorage.read { readTx in
|
|
XCTAssertTrue(blockingManager.isAddressBlocked(blockedContact, transaction: readTx))
|
|
XCTAssertTrue(blockingManager.isThreadBlocked(blockedThread, transaction: readTx))
|
|
XCTAssertFalse(blockingManager.isAddressBlocked(unblockedContact, transaction: readTx))
|
|
XCTAssertFalse(blockingManager.isThreadBlocked(unblockedThread, transaction: readTx))
|
|
|
|
// Since this was a local change, we expect to need a sync message
|
|
XCTAssertTrue(blockingManager._testingOnly_needsSyncMessage(readTx))
|
|
|
|
// Reload the remote state and ensure it sees the up-to-date block list.
|
|
let oldToken = remoteState.changeToken
|
|
remoteState.reloadIfNecessary(readTx)
|
|
let newToken = remoteState.changeToken
|
|
XCTAssertNotEqual(oldToken, newToken)
|
|
|
|
let expectedRemainingBlocks = [blockedContact, blockedThread.contactAddress]
|
|
XCTAssertEqual(remoteState.blockedUUIDStrings, Set(expectedRemainingBlocks.compactMap { $0.uuidString}))
|
|
XCTAssertEqual(remoteState.blockedPhoneNumbers, Set(expectedRemainingBlocks.compactMap { $0.phoneNumber}))
|
|
XCTAssertEqual(remoteState.blockedGroupMap, [:])
|
|
}
|
|
}
|
|
|
|
func testIncomingSyncMessage() {
|
|
// Setup
|
|
let victimBlockedAddress = CommonGenerator.address()
|
|
let victimGroupId = TSGroupModel.generateRandomV1GroupId()
|
|
|
|
let blockedUUIDs = Set((0..<10).map { _ in UUID() })
|
|
let blockedE164s = Set((0..<10).map { _ in CommonGenerator.e164() })
|
|
let blockedGroupIds = Set((0..<10).map { _ in TSGroupModel.generateRandomV1GroupId() })
|
|
databaseStorage.write { writeTx in
|
|
// For our initial state, we insert the victims that we expect to see removed
|
|
// and a single item out of our block sets that we expect to see persisted.
|
|
blockingManager.addBlockedAddress(
|
|
victimBlockedAddress,
|
|
blockMode: .localShouldNotLeaveGroups,
|
|
transaction: writeTx)
|
|
blockingManager.addBlockedGroup(
|
|
groupId: victimGroupId,
|
|
blockMode: .localShouldNotLeaveGroups,
|
|
transaction: writeTx)
|
|
|
|
blockingManager.addBlockedGroup(
|
|
groupId: blockedGroupIds.randomElement()!,
|
|
blockMode: .localShouldNotLeaveGroups,
|
|
transaction: writeTx)
|
|
blockingManager.addBlockedAddress(
|
|
SignalServiceAddress(uuid: blockedUUIDs.randomElement()!),
|
|
blockMode: .localShouldNotLeaveGroups,
|
|
transaction: writeTx)
|
|
blockingManager.addBlockedAddress(
|
|
SignalServiceAddress(phoneNumber: blockedE164s.randomElement()!),
|
|
blockMode: .localShouldNotLeaveGroups,
|
|
transaction: writeTx)
|
|
}
|
|
|
|
// Test
|
|
databaseStorage.write { writeTx in
|
|
blockingManager.processIncomingSync(
|
|
blockedPhoneNumbers: blockedE164s,
|
|
blockedUUIDs: blockedUUIDs,
|
|
blockedGroupIds: blockedGroupIds,
|
|
transaction: writeTx)
|
|
}
|
|
|
|
// Verify
|
|
databaseStorage.read { readTx in
|
|
// First, our incoming sync message should've cleared our "NeedsSync" flag
|
|
XCTAssertFalse(blockingManager._testingOnly_needsSyncMessage(readTx))
|
|
|
|
// Verify our victims aren't blocked anymore
|
|
XCTAssertFalse(blockingManager.isAddressBlocked(victimBlockedAddress, transaction: readTx))
|
|
XCTAssertFalse(blockingManager.isGroupIdBlocked(victimGroupId, transaction: readTx))
|
|
|
|
// Verify everything that came through our sync message is now blocked
|
|
XCTAssertTrue(blockedE164s
|
|
.map { SignalServiceAddress(phoneNumber: $0) }
|
|
.allSatisfy { blockingManager.isAddressBlocked($0, transaction: readTx) })
|
|
XCTAssertTrue(blockedUUIDs
|
|
.map { SignalServiceAddress(uuid: $0) }
|
|
.allSatisfy { blockingManager.isAddressBlocked($0, transaction: readTx) })
|
|
XCTAssertTrue(blockedGroupIds
|
|
.allSatisfy { blockingManager.isGroupIdBlocked($0, transaction: readTx) })
|
|
|
|
// Finally, verify that any remote state agrees
|
|
remoteState.reloadIfNecessary(readTx)
|
|
XCTAssertEqual(Set(remoteState.blockedGroupMap.keys), blockedGroupIds)
|
|
XCTAssertEqual(remoteState.blockedUUIDStrings, Set(blockedUUIDs.map { $0.uuidString }))
|
|
XCTAssertEqual(remoteState.blockedPhoneNumbers, blockedE164s)
|
|
}
|
|
}
|
|
|
|
func testSendSyncMessage() {
|
|
// Setup
|
|
// ensure local client has necessary "registered" state
|
|
identityManager.generateNewIdentityKey(for: .aci)
|
|
tsAccountManager.registerForTests(withLocalNumber: CommonGenerator.e164(), uuid: UUID())
|
|
BlockingManager.TestingFlags.optimisticallyCommitSyncToken = true
|
|
|
|
// Test
|
|
expectation(forNotification: BlockingManager.blockListDidChange, object: nil)
|
|
let neededSyncMessage = databaseStorage.write { writeTx -> Bool in
|
|
blockingManager.addBlockedAddress(CommonGenerator.address(), blockMode: .localShouldLeaveGroups, transaction: writeTx)
|
|
return blockingManager._testingOnly_needsSyncMessage(writeTx)
|
|
}
|
|
waitForExpectations(timeout: 3)
|
|
|
|
// Verify
|
|
databaseStorage.read {
|
|
XCTAssertTrue(neededSyncMessage)
|
|
XCTAssertFalse(blockingManager._testingOnly_needsSyncMessage($0))
|
|
}
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
func generateAddresses(count: UInt) -> Set<SignalServiceAddress> {
|
|
Set((0..<count).map { _ in
|
|
let hasPhoneNumber = Int.random(in: 0...2) == 0
|
|
let hasUUID = !hasPhoneNumber || Bool.random()
|
|
return CommonGenerator.address(hasUUID: hasUUID, hasPhoneNumber: hasPhoneNumber)
|
|
})
|
|
}
|
|
}
|