Version 1.3.7
This commit is contained in:
@ -9,8 +9,8 @@ android {
|
|||||||
applicationId "nl.privacydragon.bookwyrm"
|
applicationId "nl.privacydragon.bookwyrm"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 13
|
versionCode 14
|
||||||
versionName "1.3.6"
|
versionName "1.3.7"
|
||||||
|
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
BIN
app/release/Bookwyrm-v1.3.7.apk
Normal file
BIN
app/release/Bookwyrm-v1.3.7.apk
Normal file
Binary file not shown.
@ -11,8 +11,8 @@
|
|||||||
"type": "SINGLE",
|
"type": "SINGLE",
|
||||||
"filters": [],
|
"filters": [],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 13,
|
"versionCode": 14,
|
||||||
"versionName": "1.3.6",
|
"versionName": "1.3.7",
|
||||||
"outputFile": "app-release.apk"
|
"outputFile": "app-release.apk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -198,7 +198,7 @@ public class HandlerActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
//view.loadUrl("javascript:(function() { document.getElementById('id_password').value = '" + wacht + "'; ;})()");
|
//view.loadUrl("javascript:(function() { document.getElementById('id_password').value = '" + wacht + "'; ;})()");
|
||||||
//view.loadUrl("javascript:(function() { document.getElementById('id_localname').value = '" + name + "'; ;})()");
|
//view.loadUrl("javascript:(function() { document.getElementById('id_localname').value = '" + name + "'; ;})()");
|
||||||
view.loadUrl("javascript:(function() { if (window.location.href == '" + finalToGoServer + "' && !/(review|generatednote|quotation|comment|book)/i.test(window.location.href)) { document.getElementsByName(\"login\")[0].submit();} ;})()");
|
//view.loadUrl("javascript:(function() { if (window.location.href == '" + finalToGoServer + "' && !/(review|generatednote|quotation|comment|book)/i.test(window.location.href)) { document.getElementsByName(\"login\")[0].submit();} ;})()");
|
||||||
//view.loadUrl("javascript:(function() { if (window.location.href == 'https://" + server + "') { document.getElementsByName(\"login\")[0].submit();} ;})()");
|
//view.loadUrl("javascript:(function() { if (window.location.href == 'https://" + server + "') { document.getElementsByName(\"login\")[0].submit();} ;})()");
|
||||||
view.loadUrl("javascript:(function() { if (/(review|generatednote|quotation|comment|book)/i.test(window.location.href)) {" +
|
view.loadUrl("javascript:(function() { if (/(review|generatednote|quotation|comment|book)/i.test(window.location.href)) {" +
|
||||||
"blocks = document.getElementsByClassName('block');" +
|
"blocks = document.getElementsByClassName('block');" +
|
||||||
|
@ -237,106 +237,37 @@ public class StartActivity extends AppCompatActivity {
|
|||||||
// android.webkit.CookieManager oven = android.webkit.CookieManager.getInstance();
|
// android.webkit.CookieManager oven = android.webkit.CookieManager.getInstance();
|
||||||
//myWebView.loadUrl("javascript:this.document.location.href = 'source://' + encodeURI(document.documentElement.outerHTML);");
|
//myWebView.loadUrl("javascript:this.document.location.href = 'source://' + encodeURI(document.documentElement.outerHTML);");
|
||||||
try {
|
try {
|
||||||
getMiddleWareTokenAndLogIn(server, name, passw);
|
getMiddleWareTokenAndLogIn(server, name, passw); //This should get the login page, retreive the csrf-middlewaretoken, and then log the user in using a POST-request.
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// public void logIn(String lichaam) {
|
|
||||||
// //First, verkrijg the user credentials.
|
|
||||||
// //The user credentials are stored in the shared preferences, so first they have to be read from there.
|
|
||||||
// String defaultValue = "none";
|
|
||||||
// SharedPreferences sharedPref = StartActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE);
|
|
||||||
// String server = sharedPref.getString(getString(R.string.server), defaultValue);
|
|
||||||
// SharedPreferences sharedPrefName = StartActivity.this.getSharedPreferences(getString(R.string.name), Context.MODE_PRIVATE);
|
|
||||||
// String name = sharedPrefName.getString(getString(R.string.name), defaultValue);
|
|
||||||
// SharedPreferences sharedPrefPass = StartActivity.this.getSharedPreferences(getString(R.string.pw), Context.MODE_PRIVATE);
|
|
||||||
// String pass = sharedPrefPass.getString(getString(R.string.pw), defaultValue);
|
|
||||||
// SharedPreferences sharedPrefMagic = StartActivity.this.getSharedPreferences(getString(R.string.q), Context.MODE_PRIVATE);
|
|
||||||
// String codeMagic = sharedPrefMagic.getString(getString(R.string.q), defaultValue);
|
|
||||||
// //Then all the decryption stuff has to happen. There are a lot of try-catch stuff, because apparently that seems to be needed.
|
|
||||||
// //First get the keystore thing.
|
|
||||||
// KeyStore keyStore = null;
|
|
||||||
// try {
|
|
||||||
// keyStore = KeyStore.getInstance("AndroidKeyStore");
|
|
||||||
// } catch (KeyStoreException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// //Then, load it. or something. To make sure that it can be used.
|
|
||||||
// try {
|
|
||||||
// keyStore.load(null);
|
|
||||||
// } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// //Next, retrieve the key to be used for the decryption.
|
|
||||||
// Key DragonLikeKey = null;
|
|
||||||
// try {
|
|
||||||
// DragonLikeKey = keyStore.getKey("BookWyrm", null);
|
|
||||||
// } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// //Do something with getting the/a cipher or something.
|
|
||||||
// Cipher c = null;
|
|
||||||
// try {
|
|
||||||
// c = Cipher.getInstance("AES/GCM/NoPadding");
|
|
||||||
// } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// //And then initiating the cipher, so it can be used.
|
|
||||||
// try {
|
|
||||||
// assert c != null;
|
|
||||||
// c.init(Cipher.DECRYPT_MODE, DragonLikeKey, new GCMParameterSpec(128, codeMagic.getBytes()));
|
|
||||||
// } catch (InvalidAlgorithmParameterException | InvalidKeyException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// //Decrypt the password!
|
|
||||||
// byte[] truePass = null;
|
|
||||||
// try {
|
|
||||||
// truePass = c.doFinal(Base64.decode(pass, Base64.DEFAULT));
|
|
||||||
// } catch (BadPaddingException | IllegalBlockSizeException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// //Convert the decrypted password back to a string.
|
|
||||||
// String passw = new String(truePass, StandardCharsets.UTF_8);
|
|
||||||
// Log.d("body", lichaam);
|
|
||||||
// String[] opgebroken = lichaam.split("name=\"csrfmiddlewaretoken\" value=\"");
|
|
||||||
// String[] breukjes = opgebroken[1].split("\">");
|
|
||||||
// String middelToken = breukjes[0];
|
|
||||||
// String[] splitsing = lichaam.split("var csrf_token = '");
|
|
||||||
// String[] dilemma = splitsing[1].split("';");
|
|
||||||
// String csrf = dilemma[0];
|
|
||||||
// Log.d("tokens", "middel= " + middelToken);
|
|
||||||
// Log.d("tokens", "csrf= " + csrf);
|
|
||||||
// String gegevens = null;
|
|
||||||
// try {
|
|
||||||
// gegevens = "csrfmiddlewaretoken=" + URLEncoder.encode(middelToken, "UTF-8") + "&localname=" + URLEncoder.encode(name, "UTF-8") + "&password=" + URLEncoder.encode(passw, "UTF-8");
|
|
||||||
// } catch (UnsupportedEncodingException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// }
|
|
||||||
//// android.webkit.CookieManager oven = android.webkit.CookieManager.getInstance();
|
|
||||||
//// oven.setCookie("https://" + server, "csrftoken=" + csrf);
|
|
||||||
// myWebView.postUrl("https://" + server + "/login", gegevens.getBytes());
|
|
||||||
// }
|
|
||||||
public void getMiddleWareTokenAndLogIn(String server, String name, String passw) throws IOException {
|
public void getMiddleWareTokenAndLogIn(String server, String name, String passw) throws IOException {
|
||||||
//Het idee is dat deze functie de loginpagina van de server laadt en dan de 'csrfmiddlewaretoken' uit het inlogformulier haalt,
|
//Het idee is dat deze functie de loginpagina van de server laadt en dan de 'csrfmiddlewaretoken' uit het inlogformulier haalt,
|
||||||
//Zodat dat dan gebruikt kan worden bij het inloggen.
|
//Zodat dat dan gebruikt kan worden bij het inloggen.
|
||||||
|
//Becuase network operations cannot be done on the main/ui thread, create a new thread for this complete function. Yay!
|
||||||
Thread draadje = new Thread(new Runnable() {
|
Thread draadje = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
//Load the login page, and do not forget to take some cookies.
|
||||||
URL url = new URL("https://" + server + "/login");
|
URL url = new URL("https://" + server + "/login");
|
||||||
CookieManager koekManager = new CookieManager();
|
CookieManager koekManager = new CookieManager();
|
||||||
CookieHandler.setDefault(koekManager);
|
CookieHandler.setDefault(koekManager);
|
||||||
CookieStore bakker = koekManager.getCookieStore();
|
CookieStore bakker = koekManager.getCookieStore();
|
||||||
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
try {
|
try {
|
||||||
|
//Get the input stream, and move it all into a byte array.
|
||||||
InputStream ina = new BufferedInputStream(urlConnection.getInputStream());
|
InputStream ina = new BufferedInputStream(urlConnection.getInputStream());
|
||||||
byte[] pagina = null;
|
byte[] pagina = null;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
pagina = ina.readAllBytes();
|
pagina = ina.readAllBytes();
|
||||||
} else {
|
} else {
|
||||||
|
//I truly hope that this byte array will always be big enough...
|
||||||
|
//The Tiramisu+ way is much better...
|
||||||
pagina = new byte[30000];
|
pagina = new byte[30000];
|
||||||
ina.read(pagina);
|
ina.read(pagina);
|
||||||
}
|
}
|
||||||
@ -345,23 +276,31 @@ public class StartActivity extends AppCompatActivity {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
//And now create a string out of the byte array, so we can retreive the middleware token.
|
||||||
String zooi = new String(pagina);
|
String zooi = new String(pagina);
|
||||||
|
//Very easy to get the token by taking the text that it is preceded by in the raw html as the regex for a split() function!
|
||||||
String[] opgebroken = zooi.split("name=\"csrfmiddlewaretoken\" value=\"");
|
String[] opgebroken = zooi.split("name=\"csrfmiddlewaretoken\" value=\"");
|
||||||
|
//For that gives as second element the token, followed by all the following html code. Then strip that code off, using the immediately following characters as regex.
|
||||||
String[] breukjes = opgebroken[1].split("\">");
|
String[] breukjes = opgebroken[1].split("\">");
|
||||||
|
//Of course, the token is then the first element in our array.
|
||||||
String token = breukjes[0];
|
String token = breukjes[0];
|
||||||
String gegevens = null;
|
String gegevens = null;
|
||||||
|
//Initiate some strings to use for the delicious csrf cookie.
|
||||||
String speculaas = "", THT = "";
|
String speculaas = "", THT = "";
|
||||||
|
//How to get the cookies? First get the cookie collection, the cookie box so to say, and then...
|
||||||
List<HttpCookie> koektrommel = bakker.get(URI.create("https://" + server));
|
List<HttpCookie> koektrommel = bakker.get(URI.create("https://" + server));
|
||||||
//Log.d("koek", koektrommel.toString());
|
//Log.d("koek", koektrommel.toString());
|
||||||
|
//... for every cookie in it check to see if it is the csrftoken named cookie.
|
||||||
for (int i = 0; i < koektrommel.size(); ++i) {
|
for (int i = 0; i < koektrommel.size(); ++i) {
|
||||||
HttpCookie koekje = koektrommel.get(i);
|
HttpCookie koekje = koektrommel.get(i);
|
||||||
if (Objects.equals(koekje.getName(), "csrftoken")) {
|
if (Objects.equals(koekje.getName(), "csrftoken")) {
|
||||||
|
//If it is the csrftoken cookie, get the value of it, and the expiration date of it.
|
||||||
speculaas = koekje.toString();
|
speculaas = koekje.toString();
|
||||||
THT = String.valueOf(koekje.getMaxAge());
|
THT = String.valueOf(koekje.getMaxAge());
|
||||||
//Log.d("domein", koekje.getDomain());
|
//Log.d("domein", koekje.getDomain());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//And then set the data string up for use in the POST request, with the csrf middleware token, the username, and the password.
|
||||||
try {
|
try {
|
||||||
gegevens = "csrfmiddlewaretoken=" + URLEncoder.encode(token, "UTF-8") + "&localname=" + URLEncoder.encode(name, "UTF-8") + "&password=" + URLEncoder.encode(passw, "UTF-8");
|
gegevens = "csrfmiddlewaretoken=" + URLEncoder.encode(token, "UTF-8") + "&localname=" + URLEncoder.encode(name, "UTF-8") + "&password=" + URLEncoder.encode(passw, "UTF-8");
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
@ -371,17 +310,22 @@ public class StartActivity extends AppCompatActivity {
|
|||||||
//Log.d("token", speculaas);
|
//Log.d("token", speculaas);
|
||||||
String finalSpeculaas = speculaas;
|
String finalSpeculaas = speculaas;
|
||||||
String finalTHT = THT;
|
String finalTHT = THT;
|
||||||
|
//Then we have to run a bit of code on the main (UI) thread. To be able to work with the webview...
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
//First we have to get the cookie manager of the webview, so we can hand it the csrf cookie.
|
||||||
|
//Without being fed the correct csrf cookie, the Wyrm will refuse our request. The wyrm is a very picky eater!
|
||||||
android.webkit.CookieManager oven = android.webkit.CookieManager.getInstance();
|
android.webkit.CookieManager oven = android.webkit.CookieManager.getInstance();
|
||||||
|
//Bake the cookie into the webview.
|
||||||
oven.setCookie("https://" + server, finalSpeculaas + "; Max-Age=" + finalTHT + "; Path=/; SameSite=Lax; Secure");
|
oven.setCookie("https://" + server, finalSpeculaas + "; Max-Age=" + finalTHT + "; Path=/; SameSite=Lax; Secure");
|
||||||
|
//And then finally it is time to send a POST request from the webview to log in.
|
||||||
myWebView.postUrl("https://" + server + "/login?next=/", finalGegevens.getBytes());
|
myWebView.postUrl("https://" + server + "/login?next=/", finalGegevens.getBytes());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
//We should not forget closing the connection we used for hearing what csrf cookie and token we needed.
|
||||||
urlConnection.disconnect();
|
urlConnection.disconnect();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -389,6 +333,8 @@ public class StartActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
//^Here ends all that new Thread() code.
|
||||||
|
//⇓Run all the code in the thread.
|
||||||
draadje.start();
|
draadje.start();
|
||||||
//return token;
|
//return token;
|
||||||
}
|
}
|
||||||
|
1
fastlane/metadata/android/en-US/changelogs/14.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/14.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
* Significant log-in improvements, including better security.
|
Reference in New Issue
Block a user