Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
21489B7C267CBA0E00BDE4AC /* ClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21489B7B267CBA0E00BDE4AC /* ClientConfiguration.swift */; };
21489B80267CC39E00BDE4AC /* OrganisationalRulesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21489B7F267CC39E00BDE4AC /* OrganisationalRulesService.swift */; };
21489B83267CC99C00BDE4AC /* OrganisationalRulesServiceError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21489B82267CC99C00BDE4AC /* OrganisationalRulesServiceError.swift */; };
215002A32690B1DD00980DDD /* client_configuraion_with_unknown_flag.json in Resources */ = {isa = PBXBuildFile; fileRef = 215002A22690B1DD00980DDD /* client_configuraion_with_unknown_flag.json */; };
215897E8267A553300423694 /* FilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215897E7267A553200423694 /* FilesManager.swift */; };
2196A2202684B9BE001B9E00 /* URLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2196A21F2684B9BE001B9E00 /* URLExtension.swift */; };
21C7DEFC26669A3700C44800 /* CalendarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21C7DEFB26669A3700C44800 /* CalendarExtension.swift */; };
Expand Down Expand Up @@ -370,6 +371,7 @@
21489B7B267CBA0E00BDE4AC /* ClientConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientConfiguration.swift; sourceTree = "<group>"; };
21489B7F267CC39E00BDE4AC /* OrganisationalRulesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganisationalRulesService.swift; sourceTree = "<group>"; };
21489B82267CC99C00BDE4AC /* OrganisationalRulesServiceError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrganisationalRulesServiceError.swift; sourceTree = "<group>"; };
215002A22690B1DD00980DDD /* client_configuraion_with_unknown_flag.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = client_configuraion_with_unknown_flag.json; sourceTree = "<group>"; };
215897E7267A553200423694 /* FilesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesManager.swift; sourceTree = "<group>"; };
2196A21F2684B9BE001B9E00 /* URLExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLExtension.swift; sourceTree = "<group>"; };
21C7DEFB26669A3700C44800 /* CalendarExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -818,6 +820,7 @@
21EA3B2E26565B7400691848 /* client_configuraion.json */,
21EA3B3C26565B9800691848 /* client_configuraion_partly_empty.json */,
21EA3B3526565B8100691848 /* client_configuraion_empty.json */,
215002A22690B1DD00980DDD /* client_configuraion_with_unknown_flag.json */,
);
path = "Models Parsing";
sourceTree = "<group>";
Expand Down Expand Up @@ -2055,6 +2058,7 @@
9F976569267E18F30058419D /* client_configuraion.json in Resources */,
9F976576267E18F90058419D /* client_configuraion_partly_empty.json in Resources */,
9F97657D267E18FE0058419D /* client_configuraion_empty.json in Resources */,
215002A32690B1DD00980DDD /* client_configuraion_with_unknown_flag.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,47 @@ import FlowCryptUI
* - Tap on each row will navigate user to appropriate settings controller
*/
final class SettingsViewController: TableNodeViewController {
private enum Settings: Int, CaseIterable {
case backups, privacy, contacts, keys, atteseter, notifications, legal, experimental
private enum SettingsMenuItem: Int, CaseIterable {
case backups, privacy, contacts, keys, attester, notifications, legal, experimental

var title: String {
switch self {
case .backups: return "settings_screen_backup".localized
case .privacy: return "settings_screen_security".localized
case .contacts: return "settings_screen_contacts".localized
case .keys: return "settings_screen_keys".localized
case .atteseter: return "settings_screen_attester".localized
case .attester: return "settings_screen_attester".localized
case .notifications: return "settings_screen_notifications".localized
case .legal: return "settings_screen_legal".localized
case .experimental: return "settings_screen_experimental".localized
}
}

static func filtered(with rules: OrganisationalRules) -> [SettingsMenuItem] {
var cases = SettingsMenuItem.allCases

if !rules.canBackupKeys {
cases.removeAll(where: { $0 == .backups })
}

return cases
}
}

private let decorator: SettingsViewDecoratorType
private let currentUser: User?
private let organisationalRules: OrganisationalRules
private let rows: [SettingsMenuItem]

init(
decorator: SettingsViewDecoratorType = SettingsViewDecorator(),
currentUser: User? = DataService.shared.currentUser
currentUser: User? = DataService.shared.currentUser,
organisationalRulesService: OrganisationalRulesServiceType = OrganisationalRulesService()
) {
self.decorator = decorator
self.currentUser = currentUser
self.organisationalRules = organisationalRulesService.getSavedOrganisationalRulesForCurrentUser()
self.rows = SettingsMenuItem.filtered(with: self.organisationalRules)
super.init(node: TableNode())
}

Expand Down Expand Up @@ -71,13 +86,14 @@ final class SettingsViewController: TableNodeViewController {

extension SettingsViewController: ASTableDelegate, ASTableDataSource {
func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int {
Settings.allCases.count
rows.count
}

func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock {
{ [weak self] in
guard let self = self, let setting = Settings(rawValue: indexPath.row) else { return ASCellNode() }

guard let self = self else { return ASCellNode() }
let setting = self.rows[indexPath.row]
return SettingsCellNode(
title: self.decorator.attributedSetting(setting.title),
insets: self.decorator.insets
Expand All @@ -86,15 +102,15 @@ extension SettingsViewController: ASTableDelegate, ASTableDataSource {
}

func tableNode(_: ASTableNode, didSelectRowAt indexPath: IndexPath) {
guard let setting = Settings(rawValue: indexPath.row) else { return assertionFailure() }
let setting = rows[indexPath.row]
proceed(to: setting)
}
}

// MARK: - Actions

extension SettingsViewController {
private func proceed(to setting: Settings) {
private func proceed(to setting: SettingsMenuItem) {
let viewController: UIViewController?

switch setting {
Expand All @@ -105,7 +121,8 @@ extension SettingsViewController {
case .contacts:
viewController = ContactsListViewController()
case .backups:
guard let currentUser = currentUser else {
guard let currentUser = currentUser,
!organisationalRules.canBackupKeys else {
viewController = nil
return
}
Expand Down
11 changes: 10 additions & 1 deletion FlowCrypt/Controllers/Setup/SetupInitialViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,22 @@ final class SetupInitialViewController: TableNodeViewController {
private let user: UserId
private let router: GlobalRouterType
private let decorator: SetupViewDecorator
private let organisationalRules: OrganisationalRules

private lazy var logger = Logger.nested(in: Self.self, with: .setup)

init(
user: UserId,
backupService: BackupServiceType = BackupService(),
router: GlobalRouterType = GlobalRouter(),
decorator: SetupViewDecorator = SetupViewDecorator()
decorator: SetupViewDecorator = SetupViewDecorator(),
organisationalRulesService: OrganisationalRulesServiceType = OrganisationalRulesService()
) {
self.user = user
self.backupService = backupService
self.router = router
self.decorator = decorator
self.organisationalRules = organisationalRulesService.getSavedOrganisationalRulesForCurrentUser()

super.init(node: TableNode())
}
Expand Down Expand Up @@ -103,6 +106,12 @@ extension SetupInitialViewController {
}

private func searchBackups() {
if !organisationalRules.canBackupKeys {
logger.logInfo("Skipping backups searching because canBackupKeys == false")
proceedToSetupWith(keys: [])
return
}

logger.logInfo("Searching for backups in inbox")

backupService.fetchBackupsFromInbox(for: user)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,15 @@ extension EncryptedStorage: LogOutHandler {
.filter { keys.map(\.primaryLongid).contains($0.longid) }
let sessions = storage.objects(SessionObject.self)
.filter { $0.email == email }
let clientConfigurations = storage.objects(ClientConfigurationObject.self)
.filter { $0.userEmail == email }

try storage.write {
storage.delete(keys)
storage.delete(sessions)
storage.delete(passPhrases)
storage.delete(userToDelete)
storage.delete(clientConfigurations)
Comment on lines 123 to +124
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kharchevskyi does the order of items matter here? If there are some foreign keys then we probably need to delete the user last?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case - yes.
ClientConfigurationObject has userObject as a reference.
To be sure please check if this cause a crash on log out when there are 2 accounts logged in.

}
}
}
Expand Down
10 changes: 5 additions & 5 deletions FlowCrypt/Functionality/Services/AppStartup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ struct AppStartup {
try awaitPromise(self.setupCore())
try self.setupMigrationIfNeeded()
try self.setupSession()
// Fetching of org rules is being called async in purpose we don't need to wait until it's fetched
self.getUserOrgRulesIfNeeded()
try self.getUserOrgRulesIfNeeded()
}.then(on: .main) {
self.chooseView(for: window, session: session)
}.catch(on: .main) { error in
Expand Down Expand Up @@ -96,9 +95,10 @@ struct AppStartup {
}
}

private func getUserOrgRulesIfNeeded() {
private func getUserOrgRulesIfNeeded() throws {
if DataService.shared.isLoggedIn {
_ = OrganisationalRulesService().fetchOrganisationalRulesForCurrentUser()
let service = OrganisationalRulesService()
_ = try awaitPromise(service.fetchOrganisationalRulesForCurrentUser())
}
}

Expand Down Expand Up @@ -131,7 +131,7 @@ struct AppStartup {
}

private func showErrorAlert(with error: Error, on window: UIWindow, session: SessionType?) {
let alert = UIAlertController(title: "Startup Error", message: "\(error)", preferredStyle: .alert)
let alert = UIAlertController(title: "Startup Error", message: "\(error.localizedDescription)", preferredStyle: .alert)
let retry = UIAlertAction(title: "Retry", style: .default) { _ in
self.initializeApp(window: window, session: session)
}
Expand Down
39 changes: 28 additions & 11 deletions FlowCrypt/Functionality/Services/EnterpriseServerApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,21 @@ protocol EnterpriseServerApiType {
func getActiveFesUrl(for email: String) -> Promise<String?>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in this file are very good, thanks

func getActiveFesUrlForCurrentUser() -> Promise<String?>

func getClientConfiguration(for email: String) -> Promise<ClientConfiguration?>
func getClientConfigurationForCurrentUser() -> Promise<ClientConfiguration?>
func getClientConfiguration(for email: String) -> Promise<ClientConfiguration>
func getClientConfigurationForCurrentUser() -> Promise<ClientConfiguration>
}

enum EnterpriseServerApiError: Error {
case parse
case emailFormat
}
extension EnterpriseServerApiError: LocalizedError {
var errorDescription: String? {
switch self {
case .parse: return "organisational_rules_parse_error_description".localized
case .emailFormat: return "organisational_rules_email_format_error_description".localized
}
}
}

class EnterpriseServerApi: EnterpriseServerApiType {
Expand Down Expand Up @@ -71,11 +84,14 @@ class EnterpriseServerApi: EnterpriseServerApiType {
.recoverFromTimeOut(result: nil)
}

func getClientConfiguration(for email: String) -> Promise<ClientConfiguration?> {
Promise<ClientConfiguration?> { resolve, _ in
guard let userDomain = email.recipientDomain,
!Configuration.publicEmailProviderDomains.contains(userDomain) else {
resolve(nil)
func getClientConfiguration(for email: String) -> Promise<ClientConfiguration> {
Promise<ClientConfiguration> { resolve, reject in
guard let userDomain = email.recipientDomain else {
reject(EnterpriseServerApiError.emailFormat)
return
}
if Configuration.publicEmailProviderDomains.contains(userDomain) {
resolve(.empty)
return
}
let request = URLRequest.urlRequest(
Expand All @@ -93,17 +109,18 @@ class EnterpriseServerApi: EnterpriseServerApiType {
from: safeReponse.data
))?.clientConfiguration
else {
resolve(nil)
reject(EnterpriseServerApiError.parse)
return
}
resolve(clientConfiguration)
}
}

func getClientConfigurationForCurrentUser() -> Promise<ClientConfiguration?> {
func getClientConfigurationForCurrentUser() -> Promise<ClientConfiguration> {
guard let email = DataService.shared.currentUser?.email else {
return Promise<ClientConfiguration?> { resolve, _ in
resolve(nil)
return Promise<ClientConfiguration> { _, reject in
assertionFailure("User has to be set while getting client configuration")
reject(AppErr.user("currentUser == nil"))
}
}
return getClientConfiguration(for: email)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import Promises
protocol OrganisationalRulesServiceType {
func fetchOrganisationalRulesForCurrentUser() -> Promise<OrganisationalRules>
func fetchOrganisationalRules(for email: String) -> Promise<OrganisationalRules>

func getSavedOrganisationalRulesForCurrentUser() -> OrganisationalRules
}

final class OrganisationalRulesService {
Expand Down Expand Up @@ -41,26 +43,36 @@ extension OrganisationalRulesService: OrganisationalRulesServiceType {
}

func fetchOrganisationalRules(for email: String) -> Promise<OrganisationalRules> {
Promise<OrganisationalRules> { [weak self] resolve, reject in
Promise<OrganisationalRules> { [weak self] resolve, _ in
guard let self = self else { throw AppErr.nilSelf }

guard let clientConfigurationResponse = try awaitPromise(
self.enterpriseServerApi.getClientConfiguration(for: email)
) else {
reject(OrganisationalRulesServiceError.parse)
return
}
guard let organisationalRules = OrganisationalRules(
clientConfiguration: clientConfigurationResponse,
email: email
) else {
reject(OrganisationalRulesServiceError.emailFormat)
return
}
let clientConfigurationResponse = try awaitPromise(
self.enterpriseServerApi.getClientConfiguration(for: email)
)

let organisationalRules = OrganisationalRules(
clientConfiguration: clientConfigurationResponse
)

self.clientConfigurationProvider.save(clientConfiguration: clientConfigurationResponse)

resolve(organisationalRules)
}
.recover { [weak self] error -> OrganisationalRules in
guard let self = self else { throw AppErr.nilSelf }
guard let clientConfig = self.clientConfigurationProvider.fetch() else {
throw error
}
return OrganisationalRules(clientConfiguration: clientConfig)
}
}

func getSavedOrganisationalRulesForCurrentUser() -> OrganisationalRules {
guard let configuration = self.clientConfigurationProvider.fetch() else {
assertionFailure("There should not be a user without OrganisationalRules")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does assertionFailure also work for production builds?

return OrganisationalRules(clientConfiguration: .empty)
}

return OrganisationalRules(clientConfiguration: configuration)
}
}
21 changes: 21 additions & 0 deletions FlowCrypt/Models/ClientConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ enum ClientConfigurationFlag: String, Codable {
case useLegacyAttesterSubmit = "USE_LEGACY_ATTESTER_SUBMIT"
case defaultRememberPassphrase = "DEFAULT_REMEMBER_PASS_PHRASE"
case hideArmorMeta = "HIDE_ARMOR_META"
case forbidStoringPassphrase = "FORBID_STORING_PASS_PHRASE"

case unknown

init(from decoder: Decoder) throws {
self = try ClientConfigurationFlag(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
}
}

struct ClientConfiguration: Codable, Equatable {
Expand All @@ -32,6 +39,20 @@ struct ClientConfiguration: Codable, Equatable {
let enforceKeygenExpireMonths: Int?
}

// MARK: - Empty model
extension ClientConfiguration {
static var empty: ClientConfiguration {
return ClientConfiguration(
flags: [],
customKeyserverUrl: nil,
keyManagerUrl: nil,
disallowAttesterSearchForDomains: nil,
enforceKeygenAlgo: nil,
enforceKeygenExpireMonths: nil
)
}
}

// MARK: - Map from realm model
extension ClientConfiguration {
init?(_ object: ClientConfigurationObject?) {
Expand Down
13 changes: 1 addition & 12 deletions FlowCrypt/Models/OrganisationalRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,8 @@ import Foundation
class OrganisationalRules {

private let clientConfiguration: ClientConfiguration
let domain: String

init(clientConfiguration: ClientConfiguration, domain: String) {
self.clientConfiguration = clientConfiguration
self.domain = domain
}

init?(clientConfiguration: ClientConfiguration, email: String) {
guard let recipientDomain = email.recipientDomain else {
return nil
}

self.domain = recipientDomain
init(clientConfiguration: ClientConfiguration) {
self.clientConfiguration = clientConfiguration
}

Expand Down
Loading