diff --git a/AgileWorks/AgileWorks.xcodeproj/project.pbxproj b/AgileWorks/AgileWorks.xcodeproj/project.pbxproj index a18f4ce93d3b5e7e9eb6b2c7a3d45447ec422e05..c66d355e7efa042040af44f5fe785192dc7f32ab 100644 --- a/AgileWorks/AgileWorks.xcodeproj/project.pbxproj +++ b/AgileWorks/AgileWorks.xcodeproj/project.pbxproj @@ -1150,6 +1150,7 @@ 75D4EC602A3C00B00096F9D2 /* Frameworks */, 75D4EC612A3C00B00096F9D2 /* Resources */, 75D4EC822A3C2CAD0096F9D2 /* Embed Frameworks */, + F9E71CBD2DB8B01D00485422 /* ShellScript */, ); buildRules = ( ); @@ -1438,6 +1439,23 @@ shellPath = /bin/sh; shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n# GoogleService-Infoのコピー\n# FLAVOR でファイルを切り替えていたときの名残\ncp \"${PROJECT_DIR}/${PROJECT_NAME}/Configurations/GoogleService-Info-Production.plist\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\necho \"Production GoogleService-Info copied.\"\n\n# 設定にバージョン、ビルド番号を設定\nVERSION=\"$MARKETING_VERSION\"\n/usr/libexec/PlistBuddy -c \"Set :PreferenceSpecifiers:1:DefaultValue ${VERSION}\" \"${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Settings.bundle/Root.plist\"\n\n# LicensePlist\nif [ $CONFIGURATION = \"Debug\" ]; then\n ${PODS_ROOT}/LicensePlist/license-plist --output-path $PRODUCT_NAME/Settings.bundle\nfi\n\n# SwiftLint\nif which ${PODS_ROOT}/SwiftLint/swiftlint >/dev/null; then\n ${PODS_ROOT}/SwiftLint/swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n\n# Crashlytics\n\"${PODS_ROOT}/Fabric/run\"\n"; }; + F9E71CBD2DB8B01D00485422 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\"\nif [[ -d \"Frameworks\" ]]; then\nrm -fr Frameworks\nfi\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -2170,7 +2188,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4TWZNUHVN6; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 4TWZNUHVN6; INFOPLIST_FILE = AgileWorks/Info.plist; @@ -2179,7 +2197,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; + MARKETING_VERSION = 1.1.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -2345,7 +2363,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4TWZNUHVN6; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 4TWZNUHVN6; INFOPLIST_FILE = AgileWorks/Info.plist; @@ -2354,7 +2372,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; + MARKETING_VERSION = 1.1.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/AgileWorks/AgileWorks/App/RootViewController.swift b/AgileWorks/AgileWorks/App/RootViewController.swift index 77102984917768fce52688884dcd6fe0583b9988..e6712b7da8250220b84d9b07b8991560fe09cdc6 100644 --- a/AgileWorks/AgileWorks/App/RootViewController.swift +++ b/AgileWorks/AgileWorks/App/RootViewController.swift @@ -109,6 +109,7 @@ extension RootViewController { KeychainDataStore().removeAccessToken(serverNumber: serverNumber) KeychainDataStore().removeDeviceID(serverNumber: serverNumber) KeychainDataStore().removeSessionID(serverNumber: serverNumber) + KeychainDataStore().removeCookieHeader(serverNumber: serverNumber) } //認証情報削除 KeychainDataStore().removeSystemName() diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index edee03256efde2a90d0b9553e141194d43101d2d..106cc37ea7317fde2065be0e5b3fef6e9855035a 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -166,12 +166,22 @@ class WebViewController: UIViewController { // WebView ロード処理 private func loadWebView(url: String) { - let sesstionid = "JSESSIONID=" + KeychainDataStore().readSessionID()! - + var requestCookie = "" + if let cookieHeader = KeychainDataStore().readCookieHeader(){ + let individualCookies = self.separateCookieHeader(cookieHeader: cookieHeader) + for cookie in individualCookies { + let componentCookies = cookie.components(separatedBy: ";") + for componentCookie in componentCookies { + if !(componentCookie.contains("Path=") || componentCookie.contains("Secure") || componentCookie.contains("HttpOnly") || componentCookie.contains("Expires=") || componentCookie.contains("Max-Age=") || componentCookie.contains("Domain=")) { + requestCookie += componentCookie + ";" + } + } + } + } let url = URL(string: url)! var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 30.0) request.httpShouldHandleCookies = false - request.setValue(sesstionid, forHTTPHeaderField: "Cookie") + request.setValue(requestCookie, forHTTPHeaderField: "Cookie") log.debug(""" Session Request: \(String(describing: request.url)) \nHTTPMethod: \(String(describing: request.httpMethod) ) @@ -188,6 +198,22 @@ class WebViewController: UIViewController { self.setWebView() loadWebView(url: url) } + + private func separateCookieHeader(cookieHeader: String) -> [String] { + var individualCookies: [String] = [] + let cookies = cookieHeader.components(separatedBy: ",") + var currentCookie = "" + for cookie in cookies { + currentCookie += cookie + if cookie.contains("Path=") { + individualCookies.append(currentCookie) + currentCookie = "" + } else { + currentCookie += "," + } + } + return individualCookies + } // sessionId を cookie にセット private func setSessionId() { @@ -201,6 +227,61 @@ class WebViewController: UIViewController { ]) let cookieStore = self.mainWebView.configuration.websiteDataStore.httpCookieStore cookieStore.setCookie(cookie!) + + if let cookieHeader = KeychainDataStore().readCookieHeader(){ + let individualCookies = self.separateCookieHeader(cookieHeader: cookieHeader) + for cookie in individualCookies { + var cookiePath = "" + var cookieName = "" + var cookieValue = "" + var samesite: String? = "" + var secure = false + let components = cookie.components(separatedBy: ";") + for component in components { + if component.contains("Path=") { + let componentValue = component.split(separator: "=") + if componentValue.count == 1 { + cookiePath = String(componentValue[1]) + } + } else if component.contains("Secure") { + secure = true + } else if component.contains("SameSite=") { + let componentValue = component.split(separator: "=") + if componentValue.count == 1 { + if String(componentValue[1]) == "Strict" { + samesite = HTTPCookieStringPolicy.sameSiteStrict.rawValue + } else if String(componentValue[1]) == "Lax" { + samesite = HTTPCookieStringPolicy.sameSiteLax.rawValue + } else if String(componentValue[1]) == "None" { + samesite = nil + } + } + } else if !(component.contains("HttpOnly") || component.contains("Expires=") || component.contains("Max-Age=") || component.contains("Domain=")) { + let componentValue = component.split(separator: "=") + if componentValue.count == 2 { + cookieName = String(componentValue[0]) + cookieValue = String(componentValue[1]) + } + } + } + if samesite == nil { + if secure != true { + samesite = "" + } + } + if cookieName != "JSESSIONID" { + let cookie = HTTPCookie(properties: [ + HTTPCookiePropertyKey.domain: KeychainDataStore().readServerURL() ?? "", + HTTPCookiePropertyKey.path: cookiePath, + HTTPCookiePropertyKey.secure: secure, + HTTPCookiePropertyKey.name: cookieName, + HTTPCookiePropertyKey.value: cookieValue, + HTTPCookiePropertyKey.sameSitePolicy: samesite + ]) + cookieStore.setCookie(cookie!) + } + } + } } } diff --git a/AgileWorks/Common/DataStore/KeychainDataStore.swift b/AgileWorks/Common/DataStore/KeychainDataStore.swift index 03de57d9aad1e30614ffe427c83b906ed362b5b2..d6de3c24a6c824e5e701e8ff7bb76abf0014d049 100644 --- a/AgileWorks/Common/DataStore/KeychainDataStore.swift +++ b/AgileWorks/Common/DataStore/KeychainDataStore.swift @@ -21,6 +21,7 @@ final class KeychainDataStore: DataStoreProtocol { private let kSessionID: String = "SessionID" private let kSystemName: String = "SystemName" private let kCertificateLabel: String = "CertificateLabel" + private let kCookieHeader: String = "CookieHeader" // アクセストークンの書き込み func writeAccessToken(accessToken: String) { @@ -175,6 +176,25 @@ final class KeychainDataStore: DataStoreProtocol { removeKeychainValue(key: kSessionID) } } + + // cookieの書き込み + func writeCookieHeader(cookieHeader: String) { + setKeychainValue(key: kCookieHeader, value: cookieHeader) + } + + // cookieの読み込み + func readCookieHeader() -> String? { + return getKeychainValue(key: kCookieHeader) + } + + // cookieの削除 + func removeCookieHeader(serverNumber: Int? = nil) { + if let serverNumber = serverNumber { + removeKeychainValue(key: kCookieHeader, serverNumber: serverNumber) + } else { + removeKeychainValue(key: kCookieHeader) + } + } // サーバー識別名の書き込み func writeSystemName(systemName: String?) { diff --git a/AgileWorks/Common/WebClient/GetSessionEndpoint.swift b/AgileWorks/Common/WebClient/GetSessionEndpoint.swift index b36a22b56cf70a15eecef040db6653a1ccf2b23b..fa6590794742a652d00e147439d4499b27e2537d 100644 --- a/AgileWorks/Common/WebClient/GetSessionEndpoint.swift +++ b/AgileWorks/Common/WebClient/GetSessionEndpoint.swift @@ -32,10 +32,29 @@ extension GetSessionEndpoint { do { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - return try decoder.decode(SessionInfo.self, from: object as! Data) + // 新規JSESSIONID生成のとき、cookieを保存する + let decodedSessionInfo = try decoder.decode(SessionInfo.self, from: object as! Data) + let bodySessionId = decodedSessionInfo.sessionId + let headers = urlResponse.allHeaderFields[AnyHashable("Set-Cookie")] as! String + let headerSessionId = extractSessionId(from: headers) + if headerSessionId == bodySessionId { + print("CookieHeaders: \(headers)") + KeychainDataStore().writeCookieHeader(cookieHeader: headers) + } + return decodedSessionInfo } catch { log.error(error) throw ResponseError.unexpectedObject(object) } } + func extractSessionId(from cookieString: String) -> String?{ + let components = cookieString.components(separatedBy: ";") + for component in components { + let trimmedComponent = component.trimmingCharacters(in: .whitespaces) + if trimmedComponent.hasPrefix("JSESSIONID="){ + return String(trimmedComponent.dropFirst("JSESSIONID=".count)) + } + } + return nil + } }