diff --git a/README.md b/README.md index 7deaec2..dce575a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ More specific instructions can be found in the corresponding folders. * Grep for `TODO!`s: `rg TODO!` and resolve them * Find stray log statements: `rg "^([^/\n]*(console.log|Log\.[de])|[^-]*Debug.log)"` and possibly remove them - * Increment versions in `build.gradle`, `web/src/Data/Sync.elm` and `web_extension/manifest.json` + * Increment versions in `build.gradle`, `web/src/Data/Settings.elm` and `web_extension/manifest.json` * Go through the readmes of `web, android, web_extension` * Push changes to github * Upload the generated builds on github releases diff --git a/android/README.md b/android/README.md index 955d834..dd00724 100644 --- a/android/README.md +++ b/android/README.md @@ -6,8 +6,8 @@ You'll need [Android Studio](https://developer.android.com/studio/index.html). ## Release - * increment version - * generate signed apk + * increment version in build.gradle + * build -> generate signed apk * upload apk to play console: https://play.google.com/apps/publish * download derived apk * test on local device: `adb install -r 3-1.apk` diff --git a/android/app/build.gradle b/android/app/build.gradle index dc19756..d3e657d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "xyz.nokey.nokey" minSdkVersion 21 targetSdkVersion 26 - versionCode 3 - versionName "0.1.2" + versionCode 4 + versionName "0.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/android/app/src/main/java/xyz/nokey/nokey/MainActivity.kt b/android/app/src/main/java/xyz/nokey/nokey/MainActivity.kt index 81f5ce9..7aa7675 100644 --- a/android/app/src/main/java/xyz/nokey/nokey/MainActivity.kt +++ b/android/app/src/main/java/xyz/nokey/nokey/MainActivity.kt @@ -31,16 +31,19 @@ class MainActivity : Activity() { // TODO: Is it possible to change to "file:///android_asset/..." // and later, once online use that? // TODO! change before release - // val appUrl = "https://nokey.xyz/webApp" - // val appBaseUrl = "https://nokey.xyz" + val appUrl = "https://nokey.xyz/webApp" + val appBaseUrl = "https://nokey.xyz" // for this to work run the dev server with yarn dev_ssl. // 10.0.3.2 is from genymotion and forwards to localhost // val appUrl = "https://10.0.3.2:3001/main.html" // val appBaseUrl = "https://10.0.3.2:3001" // local ip, to test on device - val appUrl = "https://10.2.121.215:3001/main.html" - val appBaseUrl = "https://10.2.121.215:3001" + // val appUrl = "https://10.2.121.215:3001/main.html" + // val appBaseUrl = "https://10.2.121.215:3001" + + // TODO! change shell version if android apk changes + val androidShellVersion: String = "0.3" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/android/app/src/main/java/xyz/nokey/nokey/WebViewHelper.kt b/android/app/src/main/java/xyz/nokey/nokey/WebViewHelper.kt index 5453932..d716274 100644 --- a/android/app/src/main/java/xyz/nokey/nokey/WebViewHelper.kt +++ b/android/app/src/main/java/xyz/nokey/nokey/WebViewHelper.kt @@ -147,9 +147,9 @@ class WebViewHelper(private val activity: MainActivity, private val uiManager: U } // TODO! comment out from release - override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { + /*override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { handler.proceed() - } + }*/ } // Now we can call these functions from javascript @@ -159,13 +159,17 @@ class WebViewHelper(private val activity: MainActivity, private val uiManager: U activity.scanQR() } + @JavascriptInterface + fun getAndroidShellVersion() : String { + return activity.androidShellVersion + } + /*@JavascriptInterface fun test() { // communicate back to JS webView.loadUrl("javascript:fromAndroid(\"lloldsds\""); // new and better way: webView.evaluateJavascript("(function() { return 'this'; })();", { s -> - Log.d("sdsd", s) // Prints: "this" }) }*/ }, "Android") @@ -205,7 +209,7 @@ class WebViewHelper(private val activity: MainActivity, private val uiManager: U // handle load errors private fun handleLoadError(errorCode: Int) { if (errorCode != WebViewClient.ERROR_UNSUPPORTED_SCHEME) { - Log.e("sasas", "offline: $errorCode") + Log.e(this.javaClass.name, "offline: $errorCode") uiManager.setOffline(true) // TODO? } else { diff --git a/web/js/setup.js b/web/js/setup.js index 11567d8..ecc147e 100644 --- a/web/js/setup.js +++ b/web/js/setup.js @@ -323,11 +323,16 @@ const setup = (startFn, onStart, onError) => { // e.g. more than enough for 32 character passwords. const rands = getRandomInts(9); - let deviceType = "Browser"; + let deviceTypeString = "Browser"; + let androidShellVersion = null; if (typeof window.Android !== 'undefined') { - deviceType = "Android"; + deviceTypeString = "Android"; } else if (runsInsideExtension()) { - deviceType = "WebExtension"; + deviceTypeString = "WebExtension"; + } + let deviceType = { type: deviceTypeString }; + if (deviceTypeString === "Android" && window.Android.getAndroidShellVersion) { + deviceType.version = window.Android.getAndroidShellVersion(); } // console.log("device type:", deviceType); @@ -473,7 +478,7 @@ const setup = (startFn, onStart, onError) => { // make copyToClipboard work. setupDom(app); - if (deviceType === "Android") { + if (deviceTypeString === "Android") { setupAndroid(app); } diff --git a/web/src/Data/Settings.elm b/web/src/Data/Settings.elm index 39f7ca5..f0975b1 100644 --- a/web/src/Data/Settings.elm +++ b/web/src/Data/Settings.elm @@ -62,7 +62,6 @@ type alias Settings = currentVersion = -- TODO!: change if a new version is released - -- "0.2.1" "0.3.0" @@ -98,8 +97,7 @@ notSeenNews settings = markSeen : String -> Settings -> Settings markSeen s opt = if Set.member s allVersions then - Debug.log "settings" - { opt | seenNews = Set.insert s opt.seenNews } + { opt | seenNews = Set.insert s opt.seenNews } else opt diff --git a/web/src/Data/Sync.elm b/web/src/Data/Sync.elm index 4ba5a30..a12273c 100644 --- a/web/src/Data/Sync.elm +++ b/web/src/Data/Sync.elm @@ -135,13 +135,37 @@ type alias DeviceInfo = type DeviceType = Browser - | Android + | Android (Maybe String) | WebExtension isAndroid : SyncData -> Bool isAndroid sync = - sync.deviceType == Android + case sync.deviceType of + Android _ -> + True + + _ -> + False + + +getDeviceType : SyncData -> DeviceType +getDeviceType sync = + sync.deviceType + + +setDeviceType : DeviceType -> SyncData -> SyncData +setDeviceType devT sync = + { sync | deviceType = devT } + |> updateShared + (\s -> + { s + | knownIds = + ORDict.update sync.id + (SingleVersionRegister.update (\v -> { v | deviceType = devT })) + s.knownIds + } + ) isExtension : SyncData -> Bool @@ -1427,15 +1451,23 @@ decodeDeviceInfo = encodeDeviceType : DeviceType -> Value encodeDeviceType t = - JE.string (toString t) + case t of + Android (Just v) -> + JE.object [ ( "type", JE.string "Android" ), ( "version", JE.string v ) ] + Android Nothing -> + JE.object [ ( "type", JE.string "Android" ) ] -deviceTypeDecoder : Decoder DeviceType -deviceTypeDecoder = + other -> + JE.object [ ( "type", JE.string (toString t) ) ] + + +oldDeviceTypeDecoder : Decoder DeviceType +oldDeviceTypeDecoder = JD.string |> JD.andThen - (\s -> - case s of + (\t -> + case t of "WebExtension" -> JD.succeed WebExtension @@ -1443,13 +1475,37 @@ deviceTypeDecoder = JD.succeed Browser "Android" -> - JD.succeed Android + JD.succeed (Android Nothing) e -> JD.fail (e ++ " isn't a valid instance of DeviceType") ) +deviceTypeDecoder : Decoder DeviceType +deviceTypeDecoder = + JD.oneOf + [ JD.field "type" JD.string + |> JD.andThen + (\t -> + case t of + "WebExtension" -> + JD.succeed WebExtension + + "Browser" -> + JD.succeed Browser + + "Android" -> + decode Android + |> optional "version" (JD.string |> JD.map Just) Nothing + + e -> + JD.fail (e ++ " isn't a valid instance of DeviceType") + ) + , oldDeviceTypeDecoder + ] + + encodeVersion : SyncData -> Value encodeVersion sync = VClock.encode sync.shared.version diff --git a/web/src/Model.elm b/web/src/Model.elm index 28a602a..996a098 100644 --- a/web/src/Model.elm +++ b/web/src/Model.elm @@ -194,7 +194,9 @@ initModel mayState location initialSeed encryptionKey signingKey devType = , requirementsState = PW.init (RandomE.initialSeed base ext) , seed = newSeed , uniqueIdentifyier = uniqueIdentifyier - , syncData = syncData + , syncData = + syncData + |> Data.Sync.setDeviceType devType , pairingDialogue = Views.Pairing.init , notifications = Notifications.init , notificationsView = Views.Notifications.init diff --git a/web/src/Views/Settings.elm b/web/src/Views/Settings.elm index 7337181..acdee36 100644 --- a/web/src/Views/Settings.elm +++ b/web/src/Views/Settings.elm @@ -3,7 +3,7 @@ module Views.Settings exposing (view, State, Config, init, parseFileError, clear import Element exposing (..) import Elements import Route exposing (Page(..)) -import Data.Sync exposing (SyncData) +import Data.Sync exposing (SyncData, DeviceType(..)) import Data.Settings exposing (Settings, setAllowLevel1) import Styles @@ -43,33 +43,43 @@ view config { syncData } state = let options = Data.Sync.getSettings syncData + + deviceType = + Data.Sync.getDeviceType syncData in column [ spacing (Styles.paddingScale 3) ] [ Elements.button (Just config.onShowTutorial) "Show Tutorial" , Elements.line , Elements.button (Just config.onShowReleaseLog) "Show release log" , Elements.line - , Elements.button (Just config.onExportPasswords) "Export Passwords" + + -- Export + , importExportAndroidMsg deviceType + (Elements.button (Just config.onExportPasswords) "Export Passwords") , Elements.line + + -- Import , if Data.Sync.numberOfKnownDevices syncData >= 2 then column [ spacing (Styles.paddingScale 3), height shrink ] [ Elements.b "Import Passwords" - , column [ spacing (Styles.paddingScale 3) ] <| - (if Data.Sync.isExtension syncData then - [ Elements.p "File upload only works if the extension is shown in it's own tab." - , Elements.button (Just config.onOpenExtensionInTab) "Open in seperate tab" - ] - else - [] + , importExportAndroidMsg deviceType + (column [ spacing (Styles.paddingScale 3) ] <| + (if Data.Sync.isExtension syncData then + [ Elements.p "File upload only works if the extension is shown in it's own tab." + , Elements.button (Just config.onOpenExtensionInTab) "Open in seperate tab" + ] + else + [] + ) + ++ [ Elements.fileUpload + , case state.parseFileError of + Just e -> + Elements.p ("Error:\n" ++ e) + + Nothing -> + none + ] ) - ++ [ Elements.fileUpload - , case state.parseFileError of - Just e -> - Elements.p ("Error:\n" ++ e) - - Nothing -> - none - ] , Elements.line ] else @@ -77,6 +87,15 @@ view config { syncData } state = -- Version , Elements.text ("Version: " ++ Data.Sync.appVersion) + , case deviceType of + Android (Just v) -> + Elements.p ("Android shell version: " ++ v) + + Android Nothing -> + Elements.p ("Android shell version: unknown") + + _ -> + none , Elements.line -- Dangerous settings @@ -114,6 +133,26 @@ view config { syncData } state = ] +importExportAndroidMsg : DeviceType -> Element msg -> Element msg +importExportAndroidMsg type_ it = + let + warn = + Elements.p "You need at least version 0.3 of android shell to use the import/export password feature" + in + case type_ of + Android (Just v) -> + if v >= "0.3" then + it + else + warn + + Android Nothing -> + warn + + _ -> + it + + allowLevel1Txt : String allowLevel1Txt = """ diff --git a/web_extension/background.js b/web_extension/background.js index 6e5540e..8d5a36f 100644 --- a/web_extension/background.js +++ b/web_extension/background.js @@ -125,7 +125,7 @@ setup(Elm.MainBackground.fullscreen, (app) => { browser.tabs.create({ url: popupUrl+"?tab=true" }).then((win) => { - console.log("did it work?"); + // console.log("did it work?"); }, (err) => { console.error("creating window failed!", err); }); diff --git a/web_extension/content_scripts/pageUtils.js b/web_extension/content_scripts/pageUtils.js index c78ac14..ab2d09b 100644 --- a/web_extension/content_scripts/pageUtils.js +++ b/web_extension/content_scripts/pageUtils.js @@ -314,14 +314,9 @@ const onWindowLoad = () => { }; -// TODO: isolate styles: -// iframes: -// https://github.com/anderspitman/octopress-blog/blob/dbd21b2a76ea57cf4e967fd44a204c610b35325f/source/_posts/2014-08-04-chrome-extension-content-script-stylesheet-isolation.markdown - - if (document.readyState === 'complete') { onWindowLoad(); } else { - window.onload = onWindowLoad; + window.addEventListener('load', onWindowLoad, false); } diff --git a/web_extension/manifest.json b/web_extension/manifest.json index 8915c08..d01f5b2 100644 --- a/web_extension/manifest.json +++ b/web_extension/manifest.json @@ -3,7 +3,7 @@ "name": "NoKey", "description": "A distributed password manager without a master passwords", - "version": "0.2.1", + "version": "0.3", "author": "Florian Zinggeler", "applications": { @@ -53,7 +53,7 @@ } ], - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';", + "content_security_policy": "script-src 'self'; object-src 'self';", "content_security_policy_RELEASE": "script-src 'self'; object-src 'self';", "content_security_policy_DEBUG": "script-src 'self' 'unsafe-eval'; object-src 'self';" }