Send account details over socket and simplify NextcloudAccount members, turn all to Strings

Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
This commit is contained in:
Claudio Cambra 2023-01-26 23:01:31 +01:00
parent 81b5c33571
commit 1cb7da9bac
No known key found for this signature in database
GPG key ID: C839200C384636B0
6 changed files with 42 additions and 83 deletions

View file

@ -21,7 +21,7 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
private let anchor = NSFileProviderSyncAnchor("an anchor".data(using: .utf8)!) private let anchor = NSFileProviderSyncAnchor("an anchor".data(using: .utf8)!)
private static let maxItemsPerFileProviderPage = 100 private static let maxItemsPerFileProviderPage = 100
var ncAccount: NextcloudAccount? var ncAccount: NextcloudAccount?
var serverUrl: URL? var serverUrl: String?
init(enumeratedItemIdentifier: NSFileProviderItemIdentifier, ncAccount: NextcloudAccount?) { init(enumeratedItemIdentifier: NSFileProviderItemIdentifier, ncAccount: NextcloudAccount?) {
self.enumeratedItemIdentifier = enumeratedItemIdentifier self.enumeratedItemIdentifier = enumeratedItemIdentifier
@ -34,7 +34,7 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
if let itemMetadata = dbManager.itemMetadataFromFileProviderItemIdentifier(enumeratedItemIdentifier), if let itemMetadata = dbManager.itemMetadataFromFileProviderItemIdentifier(enumeratedItemIdentifier),
let itemDirectoryMetadata = dbManager.parentDirectoryMetadataForItem(itemMetadata) { let itemDirectoryMetadata = dbManager.parentDirectoryMetadataForItem(itemMetadata) {
self.serverUrl = URL(string: itemDirectoryMetadata.serverUrl + "/" + itemMetadata.fileName) self.serverUrl = itemDirectoryMetadata.serverUrl + "/" + itemMetadata.fileName
} }
} }
@ -133,33 +133,32 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
completionHandler(metadatas) completionHandler(metadatas)
} }
private static func readServerUrl(_ serverUrl: URL, ncAccount: NextcloudAccount, completionHandler: @escaping (_ metadatas: [NextcloudItemMetadataTable]?) -> Void) { private static func readServerUrl(_ serverUrl: String, ncAccount: NextcloudAccount, completionHandler: @escaping (_ metadatas: [NextcloudItemMetadataTable]?) -> Void) {
let dbManager = NextcloudFilesDatabaseManager.shared let dbManager = NextcloudFilesDatabaseManager.shared
let serverUrlPath = serverUrl.path let ncKitAccount = ncAccount.ncKitAccount
let ncKitAccount = ncAccount.ncKitAccount!
var directoryEtag: String? var directoryEtag: String?
if let directoryMetadata = dbManager.directoryMetadata(account: ncKitAccount, serverUrl: serverUrl.path) { if let directoryMetadata = dbManager.directoryMetadata(account: ncKitAccount, serverUrl: serverUrl) {
directoryEtag = directoryMetadata.etag directoryEtag = directoryMetadata.etag
} }
NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlPath, depth: "0", showHiddenFiles: true) { account, files, _, error in NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "0", showHiddenFiles: true) { account, files, _, error in
guard directoryEtag != files.first?.etag else { guard directoryEtag != files.first?.etag else {
finishReadServerUrl(serverUrlPath, ncKitAccount: ncKitAccount, completionHandler: completionHandler) finishReadServerUrl(serverUrl, ncKitAccount: ncKitAccount, completionHandler: completionHandler)
return return
} }
NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlPath, depth: "1", showHiddenFiles: true) { account, files, _, error in NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: true) { account, files, _, error in
guard error == .success else { guard error == .success else {
finishReadServerUrl(serverUrlPath, ncKitAccount: ncKitAccount, completionHandler: completionHandler) finishReadServerUrl(serverUrl, ncKitAccount: ncKitAccount, completionHandler: completionHandler)
return return
} }
DispatchQueue.global().async { DispatchQueue.global().async {
dbManager.convertNKFilesToItemMetadatas(files, account: ncKitAccount) { _, childDirectoriesMetadata, metadatas in dbManager.convertNKFilesToItemMetadatas(files, account: ncKitAccount) { _, childDirectoriesMetadata, metadatas in
dbManager.updateItemMetadatas(account: ncKitAccount, serverUrl: serverUrlPath, updatedMetadatas: metadatas) dbManager.updateItemMetadatas(account: ncKitAccount, serverUrl: serverUrl, updatedMetadatas: metadatas)
dbManager.updateDirectoryMetadatasFromItemMetadatas(account: ncKitAccount, parentDirectoryServerUrl: serverUrlPath, updatedDirectoryItemMetadatas: childDirectoriesMetadata) dbManager.updateDirectoryMetadatasFromItemMetadatas(account: ncKitAccount, parentDirectoryServerUrl: serverUrl, updatedDirectoryItemMetadatas: childDirectoriesMetadata)
finishReadServerUrl(serverUrlPath, ncKitAccount: ncKitAccount, completionHandler: completionHandler) finishReadServerUrl(serverUrl, ncKitAccount: ncKitAccount, completionHandler: completionHandler)
} }
} }
} }

View file

@ -66,19 +66,19 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension {
// resolve the given identifier to a record in the model // resolve the given identifier to a record in the model
if identifier == .rootContainer { if identifier == .rootContainer {
guard let ncAccount = ncAccount, let ncKitAccount = ncAccount.ncKitAccount, let serverUrl = ncAccount.serverUrl else { guard let ncAccount = ncAccount else {
completionHandler(nil, NSFileProviderError(.notAuthenticated)) completionHandler(nil, NSFileProviderError(.notAuthenticated))
return Progress() return Progress()
} }
let metadata = NextcloudItemMetadataTable() let metadata = NextcloudItemMetadataTable()
metadata.account = ncKitAccount metadata.account = ncAccount.ncKitAccount
metadata.directory = true metadata.directory = true
metadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue metadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
metadata.fileName = "root" metadata.fileName = "root"
metadata.fileNameView = "root" metadata.fileNameView = "root"
metadata.serverUrl = serverUrl.path metadata.serverUrl = ncAccount.serverUrl
metadata.classFile = NKCommon.typeClassFile.directory.rawValue metadata.classFile = NKCommon.typeClassFile.directory.rawValue
completionHandler(FileProviderItem(metadata: metadata, parentItemIdentifier: NSFileProviderItemIdentifier.rootContainer), nil) completionHandler(FileProviderItem(metadata: metadata, parentItemIdentifier: NSFileProviderItemIdentifier.rootContainer), nil)
@ -136,7 +136,7 @@ class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension {
socketClient?.sendMessage(message) socketClient?.sendMessage(message)
} }
func setupDomainAccount(keychainAccount:String) { func setupDomainAccount(user: String, serverUrl: String, password: String) {
ncAccount = NextcloudAccount(withKeychainAccount:keychainAccount) ncAccount = NextcloudAccount(user: user, serverUrl: serverUrl, password: password)
} }
} }

View file

@ -35,10 +35,15 @@ class FileProviderSocketLineProcessor: NSObject, LineProcessor {
NSLog("Received command: %@", command) NSLog("Received command: %@", command)
if (command == "SEND_FILE_PROVIDER_DOMAIN_IDENTIFIER") { if (command == "SEND_FILE_PROVIDER_DOMAIN_IDENTIFIER") {
delegate.sendFileProviderDomainIdentifier() delegate.sendFileProviderDomainIdentifier()
} else if (command == "ACCOUNT_KEYCHAIN_NAME") { } else if (command == "ACCOUNT_DETAILS") {
guard let keychainAccountSubsequence = splitLine.last else { return } guard let accountDetailsSubsequence = splitLine.last else { return }
let keychainAccountString = String(keychainAccountSubsequence) let splitAccountDetails = accountDetailsSubsequence.split(separator: ":", maxSplits: 2)
delegate.setupDomainAccount(keychainAccount:keychainAccountString)
let user = String(splitAccountDetails[0])
let serverUrl = String(splitAccountDetails[1])
let password = String(splitAccountDetails[2])
delegate.setupDomainAccount(user: user, serverUrl: serverUrl, password: password)
} }
} }
} }

View file

@ -17,63 +17,16 @@ import FileProvider
class NextcloudAccount: NSObject { class NextcloudAccount: NSObject {
let webDavFilesUrlSuffix: String = "/remote.php/dav/files/" let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
let username, password, ncKitAccount: String? let username, password, ncKitAccount, serverUrl, davFilesUrl: String
let serverUrl, davFilesUrl: URL?
var isNull: Bool { init(user: String, serverUrl: String, password: String) {
return username?.isEmpty ?? false || serverUrl?.absoluteString.isEmpty ?? false self.username = user
} self.password = password
self.ncKitAccount = user + " " + serverUrl
init?(withKeychainAccount account:String) { self.serverUrl = serverUrl
// The client sets the account field in the keychain entry as a colon-separated string consisting of self.davFilesUrl = serverUrl + webDavFilesUrlSuffix
// an account's username, its homeserver url, and the id of the account
guard let passwordData = NextcloudAccount.getUserPasswordFromKeychain(accountString: account),
let passwordString = String(data: passwordData, encoding: .utf8) else {
return nil
}
let keychainAccountSplit = account.split(separator: ":")
let usernameSubstring = keychainAccountSplit[0]
let serverUrlSubstring = keychainAccountSplit[1]
let clientAccountIdSubstring = keychainAccountSplit[2]
let usernameString = String(usernameSubstring)
let serverUrlString = String(serverUrlSubstring)
let clientAccountIdString = String(clientAccountIdSubstring)
guard let serverUrlUrl = URL(string: String(serverUrlString)) else {
return nil
}
let davFilesUrlUrl = serverUrlUrl.appendingPathComponent(webDavFilesUrlSuffix + usernameString)
username = usernameString
password = passwordString
ncKitAccount = usernameString + " " + serverUrlString
serverUrl = serverUrlUrl
davFilesUrl = davFilesUrlUrl
super.init() super.init()
} }
private static func getUserPasswordFromKeychain(accountString:String) -> Data? {
let query = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : accountString,
kSecReturnData as String : kCFBooleanTrue!,
kSecMatchLimit as String : kSecMatchLimitOne
] as [String : Any]
var dataTypeRef: AnyObject? = nil
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if status == noErr {
return dataTypeRef as! Data?
} else {
return nil
}
}
} }

View file

@ -81,7 +81,7 @@ void FileProviderSocketController::parseReceivedLine(const QString &receivedLine
if (command == QStringLiteral("FILE_PROVIDER_DOMAIN_IDENTIFIER_REQUEST_REPLY")) { if (command == QStringLiteral("FILE_PROVIDER_DOMAIN_IDENTIFIER_REQUEST_REPLY")) {
_accountState = accountStateFromFileProviderDomainIdentifier(argument); _accountState = accountStateFromFileProviderDomainIdentifier(argument);
sendAccountKeychainEntryKey(); sendAccountDetails();
return; return;
} }
@ -135,19 +135,21 @@ void FileProviderSocketController::requestFileProviderDomainInfo() const
sendMessage(requestMessage); sendMessage(requestMessage);
} }
void FileProviderSocketController::sendAccountKeychainEntryKey() const void FileProviderSocketController::sendAccountDetails() const
{ {
Q_ASSERT(_accountState); Q_ASSERT(_accountState);
const auto account = _accountState->account(); const auto account = _accountState->account();
Q_ASSERT(account); Q_ASSERT(account);
const auto credentials = account->credentials(); const auto credentials = account->credentials();
Q_ASSERT(credentials); Q_ASSERT(credentials);
const auto accountUser = credentials->user();
const auto accountUrl = account->url().toString(); const auto accountUrl = account->url().toString();
const auto accountUser = account->credentials()->user(); const auto accountPassword = credentials->password();
const auto accountId = account->id();
const auto accountKey = credentials->keychainKey(accountUrl, accountUser, accountId);
const auto message = QString(QStringLiteral("ACCOUNT_KEYCHAIN_NAME:") + accountKey); const auto message = QString(QStringLiteral("ACCOUNT_DETAILS:") +
accountUser + ":" +
accountUrl + ":" +
accountPassword);
sendMessage(message); sendMessage(message);
} }

View file

@ -45,7 +45,7 @@ private slots:
void parseReceivedLine(const QString &receivedLine); void parseReceivedLine(const QString &receivedLine);
void requestFileProviderDomainInfo() const; void requestFileProviderDomainInfo() const;
void sendAccountKeychainEntryKey() const; void sendAccountDetails() const;
private: private:
static AccountStatePtr accountStateFromFileProviderDomainIdentifier(const QString &domainIdentifier); static AccountStatePtr accountStateFromFileProviderDomainIdentifier(const QString &domainIdentifier);