From 043beeecaf4f3cb8514bb4d354d9305b37a818e3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 24 Jan 2021 19:16:02 +0100 Subject: [PATCH] 2FA support with pixelfed --- .../android/activities/BaseMainActivity.java | 5 +-- .../activities/WebviewConnectActivity.java | 44 ++++++++++--------- .../asynctasks/RetrieveStoriesAsyncTask.java | 16 ++----- .../java/app/fedilab/android/client/API.java | 16 +++++-- .../android/client/HttpsConnection.java | 12 +++-- .../fedilab/android/client/PixelfedAPI.java | 10 ++--- .../fragments/DisplayStoriesFragment.java | 20 ++++----- .../fedilab/android/sqlite/AccountDAO.java | 24 +++++----- 8 files changed, 76 insertions(+), 71 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java index bc6c2cfc7..8d2eed10e 100644 --- a/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java @@ -2565,10 +2565,7 @@ public abstract class BaseMainActivity extends BaseActivity fragment.setArguments(bundle); return fragment; } else { - DisplayStoriesFragment fragment = new DisplayStoriesFragment(); - bundle.putSerializable("type", RetrieveStoriesAsyncTask.type.ME); - fragment.setArguments(bundle); - return fragment; + return new DisplayStoriesFragment(); } } } diff --git a/app/src/main/java/app/fedilab/android/activities/WebviewConnectActivity.java b/app/src/main/java/app/fedilab/android/activities/WebviewConnectActivity.java index 82ffec338..5e9c41e24 100644 --- a/app/src/main/java/app/fedilab/android/activities/WebviewConnectActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/WebviewConnectActivity.java @@ -25,7 +25,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -164,33 +163,39 @@ public class WebviewConnectActivity extends BaseActivity { @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - + String x_xsrf_token = null; + String x_csrf_token = null; if (request.getUrl().toString().contains("accounts/verify_credentials")) { - Log.v(Helper.TAG, "-> " + request.getUrl()); + + String cookies = CookieManager.getInstance().getCookie(request.getUrl().toString()); + Map requestHeaders = request.getRequestHeaders(); Iterator> it = requestHeaders.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = it.next(); - Log.v(Helper.TAG, "pair.getKey() -> " + pair.getKey()); if (pair.getKey().compareTo("X-XSRF-TOKEN") == 0) { - new Handler(Looper.getMainLooper()).post(() -> { - pixelfedToken = pair.getValue(); - Log.v(Helper.TAG, "pixelfedToken -> " + pixelfedToken); - view.stopLoading(); - SharedPreferences sharedpreferences1 = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences1.edit(); - String token = "X-XSRF-TOKEN: " + pixelfedToken; - editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token); - editor.commit(); - view.setVisibility(View.GONE); - //Update the account with the token; - new UpdateAccountInfoAsyncTask(WebviewConnectActivity.this, token, clientId, clientSecret, null, instance, social); - finish(); - }); - + x_xsrf_token = pair.getValue(); + } + if (pair.getKey().compareTo("X-CSRF-TOKEN") == 0) { + x_csrf_token = pair.getValue(); } it.remove(); } + if (x_xsrf_token != null && x_csrf_token != null) { + String finalX_xsrf_token = x_xsrf_token; + String finalX_csrf_token = x_csrf_token; + new Handler(Looper.getMainLooper()).post(() -> { + view.stopLoading(); + SharedPreferences sharedpreferences1 = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences1.edit(); + String token = "X-XSRF-TOKEN= " + finalX_xsrf_token + ";X-CSRF-TOKEN= " + finalX_csrf_token + "|" + cookies; + editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token); + editor.commit(); + view.setVisibility(View.GONE); + //Update the account with the token; + new UpdateAccountInfoAsyncTask(WebviewConnectActivity.this, token, clientId, clientSecret, null, instance, social); + }); + } } return super.shouldInterceptRequest(view, request); } @@ -199,7 +204,6 @@ public class WebviewConnectActivity extends BaseActivity { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { super.shouldOverrideUrlLoading(view, url); - Log.v(Helper.TAG, "pixelfedToken: " + pixelfedToken); if (url.contains(Helper.REDIRECT_CONTENT_WEB)) { String[] val = url.split("code="); if (val.length < 2) { diff --git a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveStoriesAsyncTask.java b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveStoriesAsyncTask.java index 6a3c1fe01..47474e43b 100644 --- a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveStoriesAsyncTask.java +++ b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveStoriesAsyncTask.java @@ -36,34 +36,26 @@ public class RetrieveStoriesAsyncTask { private final String max_id; private final OnRetrieveStoriesInterface listener; private final WeakReference contextReference; - private final type typeOfStory; private APIResponse apiResponse; + private final String userId; - public RetrieveStoriesAsyncTask(Context context, String max_id, type typeOfStory, OnRetrieveStoriesInterface onRetrieveStoriesInterface) { + public RetrieveStoriesAsyncTask(Context context, String max_id, String userId, OnRetrieveStoriesInterface onRetrieveStoriesInterface) { this.contextReference = new WeakReference<>(context); this.max_id = max_id; this.listener = onRetrieveStoriesInterface; - this.typeOfStory = typeOfStory; + this.userId = userId; doInBackground(); } protected void doInBackground() { new Thread(() -> { PixelfedAPI pixelfedAPI = new PixelfedAPI(this.contextReference.get()); - if (typeOfStory == type.FRIENDS) { - apiResponse = pixelfedAPI.getFriendStories(max_id); - } else if (typeOfStory == type.ME) { - apiResponse = pixelfedAPI.getMyStories(); - } + apiResponse = pixelfedAPI.getFriendStories(userId, max_id); Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> listener.onRetrieveStories(apiResponse); mainHandler.post(myRunnable); }).start(); } - public enum type { - ME, - FRIENDS - } } diff --git a/app/src/main/java/app/fedilab/android/client/API.java b/app/src/main/java/app/fedilab/android/client/API.java index ae1cbe9c5..10b21f70f 100644 --- a/app/src/main/java/app/fedilab/android/client/API.java +++ b/app/src/main/java/app/fedilab/android/client/API.java @@ -1461,9 +1461,12 @@ public class API { } account.setUrl(resobj.getString("url")); account.setAvatar(resobj.getString("avatar")); - account.setAvatar_static(resobj.getString("avatar_static")); - account.setHeader(resobj.getString("header")); - account.setHeader_static(resobj.getString("header_static")); + if (resobj.has("avatar_static")) + account.setAvatar_static(resobj.getString("avatar_static")); + if (resobj.has("header")) + account.setHeader(resobj.getString("header")); + if (resobj.has("header_static")) + account.setHeader_static(resobj.getString("header_static")); try { if (resobj.has("pleroma")) { account.setSocial("PLEROMA"); @@ -6235,7 +6238,12 @@ public class API { } private String getAbsoluteUrl(String action) { - return Helper.instanceWithProtocol(this.context, this.instance) + "/api/v1" + action; + if (prefKeyOauthTokenT.startsWith("X-XSRF-TOKEN")) { + return Helper.instanceWithProtocol(this.context, this.instance) + "/api/pixelfed/v1" + action; + } else { + return Helper.instanceWithProtocol(this.context, this.instance) + "/api/v1" + action; + } + } private String getAbsoluteUr2l(String action) { diff --git a/app/src/main/java/app/fedilab/android/client/HttpsConnection.java b/app/src/main/java/app/fedilab/android/client/HttpsConnection.java index ebe974b0d..db9187cba 100644 --- a/app/src/main/java/app/fedilab/android/client/HttpsConnection.java +++ b/app/src/main/java/app/fedilab/android/client/HttpsConnection.java @@ -129,12 +129,18 @@ public class HttpsConnection { private void setToken(String token) { + if (token != null) { if (token.startsWith("Basic ")) httpURLConnection.setRequestProperty("Authorization", token); - else if (token.startsWith("X-XSRF-TOKEN")) - httpURLConnection.setRequestProperty("Authorization", token); - else + else if (token.startsWith("X-XSRF-TOKEN")) { + String cookie = token.split("\\|")[1]; + httpURLConnection.setRequestProperty("cookie", cookie); + String[] tokens = token.split("\\|")[0].split(";"); + httpURLConnection.setRequestProperty("x-xsrf-token", tokens[0].replace("X-XSRF-TOKEN= ", "")); + httpURLConnection.setRequestProperty("x-csrf-token", tokens[1].replace("X-CSRF-TOKEN= ", "")); + + } else httpURLConnection.setRequestProperty("Authorization", "Bearer " + token); } } diff --git a/app/src/main/java/app/fedilab/android/client/PixelfedAPI.java b/app/src/main/java/app/fedilab/android/client/PixelfedAPI.java index 23076e8e4..0592d7cd4 100644 --- a/app/src/main/java/app/fedilab/android/client/PixelfedAPI.java +++ b/app/src/main/java/app/fedilab/android/client/PixelfedAPI.java @@ -167,7 +167,7 @@ public class PixelfedAPI { * * @return APIResponse */ - public APIResponse getMyStories() { + /* public APIResponse getMyStories() { pixelFedStories = new ArrayList<>(); PixelFedStory pixelFedStory; try { @@ -187,21 +187,21 @@ public class PixelfedAPI { apiResponse = new APIResponse(); apiResponse.setPixelFedStories(pixelFedStories); return apiResponse; - } + }*/ /** * Retrieves Pixelfed Own Stories *synchronously* * * @return APIResponse */ - public APIResponse getFriendStories(String max_id) { + public APIResponse getFriendStories(String userId, String max_id) { pixelFedStories = new ArrayList<>(); HashMap params = new HashMap<>(); if (max_id != null) params.put("max_id", max_id); try { HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); - String response = httpsConnection.get(getAbsoluteUrl("/recent"), 10, params, prefKeyOauthTokenT); + String response = httpsConnection.get(getAbsoluteUrl("/fetch/" + userId), 10, params, prefKeyOauthTokenT); apiResponse.setSince_id(httpsConnection.getSince_id()); apiResponse.setMax_id(httpsConnection.getMax_id()); pixelFedStories = parseStories(new JSONArray(response)); @@ -342,6 +342,6 @@ public class PixelfedAPI { private String getAbsoluteUrl(String action) { - return Helper.instanceWithProtocol(this.context, this.instance) + "/api/stories/v1" + action; + return Helper.instanceWithProtocol(this.context, this.instance) + "/api/stories/v0" + action; } } diff --git a/app/src/main/java/app/fedilab/android/fragments/DisplayStoriesFragment.java b/app/src/main/java/app/fedilab/android/fragments/DisplayStoriesFragment.java index b6332a7dd..17b377b87 100644 --- a/app/src/main/java/app/fedilab/android/fragments/DisplayStoriesFragment.java +++ b/app/src/main/java/app/fedilab/android/fragments/DisplayStoriesFragment.java @@ -65,8 +65,8 @@ public class DisplayStoriesFragment extends Fragment implements OnRetrieveStorie private SwipeRefreshLayout swipeRefreshLayout; private boolean swiped; private RecyclerView lv_stories; - private RetrieveStoriesAsyncTask.type type; private View rootView; + private String userId; public DisplayStoriesFragment() { } @@ -77,10 +77,9 @@ public class DisplayStoriesFragment extends Fragment implements OnRetrieveStorie rootView = inflater.inflate(R.layout.fragment_stories, container, false); - Bundle bundle = this.getArguments(); - if (bundle != null) { - type = (RetrieveStoriesAsyncTask.type) bundle.get("type"); - } + + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); max_id = null; context = getContext(); @@ -109,7 +108,7 @@ public class DisplayStoriesFragment extends Fragment implements OnRetrieveStorie if (firstVisibleItem + visibleItemCount == totalItemCount && context != null) { if (!flag_loading) { flag_loading = true; - new RetrieveStoriesAsyncTask(context, max_id, type, DisplayStoriesFragment.this); + new RetrieveStoriesAsyncTask(context, max_id, userId, DisplayStoriesFragment.this); nextElementLoader.setVisibility(View.VISIBLE); } } else { @@ -124,9 +123,8 @@ public class DisplayStoriesFragment extends Fragment implements OnRetrieveStorie flag_loading = true; swiped = true; if (context != null) - new RetrieveStoriesAsyncTask(context, null, type, DisplayStoriesFragment.this); + new RetrieveStoriesAsyncTask(context, null, userId, DisplayStoriesFragment.this); }); - SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); switch (theme) { case Helper.THEME_LIGHT: @@ -149,11 +147,11 @@ public class DisplayStoriesFragment extends Fragment implements OnRetrieveStorie break; } if (context != null) - new RetrieveStoriesAsyncTask(context, max_id, type, DisplayStoriesFragment.this); + new RetrieveStoriesAsyncTask(context, max_id, userId, DisplayStoriesFragment.this); else new Handler(Looper.getMainLooper()).postDelayed(() -> { if (context != null) - new RetrieveStoriesAsyncTask(context, max_id, type, DisplayStoriesFragment.this); + new RetrieveStoriesAsyncTask(context, max_id, userId, DisplayStoriesFragment.this); }, 500); return rootView; } @@ -259,7 +257,7 @@ public class DisplayStoriesFragment extends Fragment implements OnRetrieveStorie * @param sinceId String */ private void retrieveMissingNotifications(String sinceId) { - new RetrieveStoriesAsyncTask(context, null, type, DisplayStoriesFragment.this); + new RetrieveStoriesAsyncTask(context, null, userId, DisplayStoriesFragment.this); } @Override diff --git a/app/src/main/java/app/fedilab/android/sqlite/AccountDAO.java b/app/src/main/java/app/fedilab/android/sqlite/AccountDAO.java index 7a7d01934..cd963c9cc 100644 --- a/app/src/main/java/app/fedilab/android/sqlite/AccountDAO.java +++ b/app/src/main/java/app/fedilab/android/sqlite/AccountDAO.java @@ -66,10 +66,10 @@ public class AccountDAO { values.put(Sqlite.COL_STATUSES_COUNT, account.getStatuses_count()); values.put(Sqlite.COL_NOTE, account.getNote()); values.put(Sqlite.COL_URL, account.getUrl()); - values.put(Sqlite.COL_AVATAR, account.getAvatar()); - values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static()); - values.put(Sqlite.COL_HEADER, account.getHeader()); - values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static()); + values.put(Sqlite.COL_AVATAR, account.getAvatar() != null ? account.getAvatar() : "null"); + values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static() != null ? account.getAvatar_static() : "null"); + values.put(Sqlite.COL_HEADER, account.getHeader() != null ? account.getHeader() : "null"); + values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static() != null ? account.getHeader_static() : "---"); values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(account.getCreated_at())); values.put(Sqlite.COL_INSTANCE, account.getInstance()); values.put(Sqlite.COL_EMOJIS, Helper.emojisToStringStorage(account.getEmojis())); @@ -117,10 +117,10 @@ public class AccountDAO { values.put(Sqlite.COL_STATUSES_COUNT, account.getStatuses_count()); values.put(Sqlite.COL_NOTE, account.getNote()); values.put(Sqlite.COL_URL, account.getUrl()); - values.put(Sqlite.COL_AVATAR, account.getAvatar()); - values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static()); - values.put(Sqlite.COL_HEADER, account.getHeader()); - values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static()); + values.put(Sqlite.COL_AVATAR, account.getAvatar() != null ? account.getAvatar() : "null"); + values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static() != null ? account.getAvatar_static() : "null"); + values.put(Sqlite.COL_HEADER, account.getHeader() != null ? account.getHeader() : "null"); + values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static() != null ? account.getHeader_static() : "null"); values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(account.getCreated_at())); values.put(Sqlite.COL_EMOJIS, Helper.emojisToStringStorage(account.getEmojis())); if (account.getSocial() != null) { @@ -168,10 +168,10 @@ public class AccountDAO { values.put(Sqlite.COL_STATUSES_COUNT, account.getStatuses_count()); values.put(Sqlite.COL_NOTE, account.getNote()); values.put(Sqlite.COL_URL, account.getUrl()); - values.put(Sqlite.COL_AVATAR, account.getAvatar()); - values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static()); - values.put(Sqlite.COL_HEADER, account.getHeader()); - values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static()); + values.put(Sqlite.COL_AVATAR, account.getAvatar() != null ? account.getAvatar() : "null"); + values.put(Sqlite.COL_AVATAR_STATIC, account.getAvatar_static() != null ? account.getAvatar_static() : "null"); + values.put(Sqlite.COL_HEADER, account.getHeader() != null ? account.getHeader() : "null"); + values.put(Sqlite.COL_HEADER_STATIC, account.getHeader_static() != null ? account.getHeader_static() : "null"); values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(account.getCreated_at())); values.put(Sqlite.COL_EMOJIS, Helper.emojisToStringStorage(account.getEmojis())); if (account.getSocial() != null) {