Skip to content

Commit

Permalink
Merge branch 'main' into jubie/BIT-2300-mask-totp
Browse files Browse the repository at this point in the history
  • Loading branch information
jubie-livefront committed May 9, 2024
2 parents 7c2271b + 5dde814 commit 50fd3ae
Show file tree
Hide file tree
Showing 132 changed files with 2,307 additions and 672 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Check out repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
submodules: 'true'

- name: Set Up Ruby
uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0
with:
bundler-cache: true
ruby-version: 3.2.2
Expand Down Expand Up @@ -214,6 +214,10 @@ jobs:
run: |
plutil -replace ITSEncryptionExportComplianceCode -string 3dd3e32f-efa6-4d99-b410-28aa28b1cb77 Bitwarden/Application/Support/Info.plist
- name: Update APNS entitlements
run: |
plutil -replace aps-environment -string production Bitwarden/Application/Support/Bitwarden.entitlements
- name: Install Mint, xcbeautify, and yq
run: |
brew install mint xcbeautify yq
Expand Down Expand Up @@ -243,7 +247,7 @@ jobs:
./Scripts/build.sh
- name: Upload IPA
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: Bitwarden iOS
path: build/Bitwarden/Bitwarden.ipa
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cache-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4

- name: Setup Ruby
uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0
with:
bundler-cache: true
ruby-version: 3.2.2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:
runs-on: macos-14-xlarge
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4

- name: Setup Ruby
uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0
with:
bundler-cache: true
ruby-version: 3.2.2
Expand Down
8 changes: 8 additions & 0 deletions Bitwarden/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
appProcessor?.failedToRegister(error)
}

func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any]
) async -> UIBackgroundFetchResult {
await appProcessor?.messageReceived(userInfo)
return .newData
}

/// Received a response to a push notification alert.
func userNotificationCenter(
_: UNUserNotificationCenter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ enum TwoFactorAuthMethod: Int {
case .yubiKey:
Localizations.yubiKeyInstructionIos
case .webAuthn:
Localizations.continueToCompleteWebAuthnVerfication
Localizations.continueToCompleteWebAuthnVerification
default:
""
}
Expand Down
20 changes: 17 additions & 3 deletions BitwardenShared/Core/Auth/Repositories/AuthRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ class DefaultAuthRepository {
/// The service that handles common client functionality such as encryption and decryption.
private let clientService: ClientService

/// The services to get server-specified configuration.
private let configService: ConfigService

/// The service used by the application to manage the environment settings.
private let environmentService: EnvironmentService

Expand Down Expand Up @@ -330,6 +333,7 @@ class DefaultAuthRepository {
/// - authService: The service used that handles some of the auth logic.
/// - biometricsRepository: The service to use system Biometrics for vault unlock.
/// - clientService: The service that handles common client functionality such as encryption and decryption.
/// - configService: The service to get server-specified configuration.
/// - environmentService: The service used by the application to manage the environment settings.
/// - keychainService: The keychain service used by the application.
/// - organizationAPIService: The service used by the application to make organization-related API requests.
Expand All @@ -345,6 +349,7 @@ class DefaultAuthRepository {
authService: AuthService,
biometricsRepository: BiometricsRepository,
clientService: ClientService,
configService: ConfigService,
environmentService: EnvironmentService,
keychainService: KeychainRepository,
organizationAPIService: OrganizationAPIService,
Expand All @@ -358,6 +363,7 @@ class DefaultAuthRepository {
self.authService = authService
self.biometricsRepository = biometricsRepository
self.clientService = clientService
self.configService = configService
self.environmentService = environmentService
self.keychainService = keychainService
self.organizationAPIService = organizationAPIService
Expand Down Expand Up @@ -394,6 +400,13 @@ extension DefaultAuthRepository: AuthRepository {
publicKey: registrationKeys.publicKey
))

try await stateService.setAccountEncryptionKeys(
AccountEncryptionKeys(
encryptedPrivateKey: registrationKeys.privateKey,
encryptedUserKey: nil
)
)

try await organizationUserAPIService.organizationUserResetPasswordEnrollment(
organizationId: enrollStatus.id,
requestModel: OrganizationUserResetPasswordEnrollmentRequestModel(
Expand Down Expand Up @@ -520,6 +533,7 @@ extension DefaultAuthRepository: AuthRepository {
func setActiveAccount(userId: String) async throws -> Account {
try await stateService.setActiveAccount(userId: userId)
await environmentService.loadURLsForActiveAccount()
_ = await configService.getConfig()
return try await stateService.getActiveAccount()
}

Expand Down Expand Up @@ -607,7 +621,7 @@ extension DefaultAuthRepository: AuthRepository {
func setPins(_ pin: String, requirePasswordAfterRestart: Bool) async throws {
let pinKey = try await clientService.crypto().derivePinKey(pin: pin)
try await stateService.setPinKeys(
pinKeyEncryptedUserKey: pinKey.encryptedPin,
encryptedPin: pinKey.encryptedPin,
pinProtectedUserKey: pinKey.pinProtectedUserKey,
requirePasswordAfterRestart: requirePasswordAfterRestart
)
Expand Down Expand Up @@ -808,9 +822,9 @@ extension DefaultAuthRepository: AuthRepository {

// If the user has a pin, but requires master password after restart, set the pin
// protected user key in memory for future unlocks prior to app restart.
if let pinKeyEncryptedUserKey = try await stateService.pinKeyEncryptedUserKey() {
if let encryptedPin = try await stateService.getEncryptedPin() {
let pinProtectedUserKey = try await clientService.crypto().derivePinUserKey(
encryptedPin: pinKeyEncryptedUserKey
encryptedPin: encryptedPin
)
try await stateService.setPinProtectedUserKeyToMemory(pinProtectedUserKey)
}
Expand Down
20 changes: 16 additions & 4 deletions BitwardenShared/Core/Auth/Repositories/AuthRepositoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
var biometricsRepository: MockBiometricsRepository!
var client: MockHTTPClient!
var clientService: MockClientService!
var configService: MockConfigService!
var environmentService: MockEnvironmentService!
var keychainService: MockKeychainRepository!
var organizationService: MockOrganizationService!
Expand Down Expand Up @@ -86,6 +87,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
accountAPIService = APIService(client: client)
authService = MockAuthService()
biometricsRepository = MockBiometricsRepository()
configService = MockConfigService()
environmentService = MockEnvironmentService()
keychainService = MockKeychainRepository()
organizationService = MockOrganizationService()
Expand All @@ -98,6 +100,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
authService: authService,
biometricsRepository: biometricsRepository,
clientService: clientService,
configService: configService,
environmentService: environmentService,
keychainService: keychainService,
organizationAPIService: APIService(client: client),
Expand All @@ -117,6 +120,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
biometricsRepository = nil
client = nil
clientService = nil
configService = nil
environmentService = nil
keychainService = nil
organizationService = nil
Expand All @@ -133,12 +137,12 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
let userId = Account.fixture().profile.userId

stateService.pinProtectedUserKeyValue[userId] = "123"
stateService.pinKeyEncryptedUserKeyValue[userId] = "123"
stateService.encryptedPinByUserId[userId] = "123"
stateService.accountVolatileData[userId]?.pinProtectedUserKey = "123"

try await subject.clearPins()
XCTAssertNil(stateService.pinProtectedUserKeyValue[userId])
XCTAssertNil(stateService.pinKeyEncryptedUserKeyValue[userId])
XCTAssertNil(stateService.encryptedPinByUserId[userId])
XCTAssertNil(stateService.accountVolatileData[userId]?.pinProtectedUserKey)
}

Expand Down Expand Up @@ -171,6 +175,10 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysEmail, "[email protected]")
XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysOrgPublicKey, "MIIBIjAN...2QIDAQAB")
XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysRememberDevice, true)
XCTAssertEqual(
stateService.accountEncryptionKeys["1"],
AccountEncryptionKeys(encryptedPrivateKey: "privateKey", encryptedUserKey: nil)
)
}

/// `createNewSsoUser()` creates a new account for sso JIT user and don't trust device.
Expand Down Expand Up @@ -200,6 +208,10 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
XCTAssertNil(trustDeviceService.trustDeviceWithExistingKeysValue)
XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysOrgPublicKey, "MIIBIjAN...2QIDAQAB")
XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysRememberDevice, false)
XCTAssertEqual(
stateService.accountEncryptionKeys["1"],
AccountEncryptionKeys(encryptedPrivateKey: "privateKey", encryptedUserKey: nil)
)
}

/// `deleteAccount()` deletes the active account and removes it from the state.
Expand Down Expand Up @@ -1029,7 +1041,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
let userId = account.profile.userId
try await subject.setPins("123", requirePasswordAfterRestart: true)
XCTAssertEqual(stateService.pinProtectedUserKeyValue[userId], "12")
XCTAssertEqual(stateService.pinKeyEncryptedUserKeyValue[userId], "34")
XCTAssertEqual(stateService.encryptedPinByUserId[userId], "34")
XCTAssertEqual(stateService.accountVolatileData[
userId,
default: AccountVolatileData()
Expand Down Expand Up @@ -1299,7 +1311,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo
"1": AccountEncryptionKeys(encryptedPrivateKey: "PRIVATE_KEY", encryptedUserKey: "USER_KEY"),
]

stateService.pinKeyEncryptedUserKeyValue[account.profile.userId] = "123"
stateService.encryptedPinByUserId[account.profile.userId] = "123"
stateService.pinProtectedUserKeyValue[account.profile.userId] = "123"

await assertAsyncDoesNotThrow {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import Foundation
import Networking

/// Errors thrown from validating a `CheckLoginRequestRequest` response.
enum CheckLoginRequestError: Error {
/// The request has expired.
case expired
}

// MARK: - CheckLoginRequestRequest

/// A request for checking the status of a login request for an unauthenticated user.
Expand All @@ -24,4 +30,12 @@ struct CheckLoginRequestRequest: Request {

/// The query item for this request.
var query: [URLQueryItem] { [URLQueryItem(name: "code", value: accessCode)] }

// MARK: Request

func validate(_ response: HTTPResponse) throws {
if response.statusCode == 404 {
throw CheckLoginRequestError.expired
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Networking
import XCTest

@testable import BitwardenShared

class CheckLoginRequestRequestTests: BitwardenTestCase {
// MARK: Properties

var subject: CheckLoginRequestRequest!

// MARK: Setup & Teardown

override func setUp() {
super.setUp()
subject = CheckLoginRequestRequest(accessCode: "ACCESS_CODE", id: "ID")
}

override func tearDown() {
super.tearDown()
subject = nil
}

// MARK: Tests

/// `method` returns the method of the request.
func test_method() {
XCTAssertEqual(subject.method, .get)
}

/// `path` returns the path of the request.
func test_path() {
XCTAssertEqual(subject.path, "/auth-requests/ID/response")
}

/// `query` returns the query items of the request.
func test_query() {
XCTAssertEqual(subject.query, [URLQueryItem(name: "code", value: "ACCESS_CODE")])
}

/// `validate(_:)` validates the response for the request and throws an error if the request is expired.
func test_validate() {
XCTAssertNoThrow(try subject.validate(.success()))
XCTAssertNoThrow(try subject.validate(.failure(statusCode: 400)))
XCTAssertNoThrow(try subject.validate(.failure(statusCode: 500)))

XCTAssertThrowsError(try subject.validate(.failure(statusCode: 404))) { error in
XCTAssertEqual(error as? CheckLoginRequestError, .expired)
}
}
}
Loading

0 comments on commit 50fd3ae

Please sign in to comment.