Move login client out of the data-client (#5476)

This commit is contained in:
Paul Hawke 2024-01-26 08:11:44 -06:00 committed by GitHub
parent 8789879f10
commit 0541aacdff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 195 additions and 234 deletions

View file

@ -27,6 +27,7 @@ dependencies {
} }
implementation 'com.squareup.retrofit2:retrofit:2.8.1' implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation "com.squareup.retrofit2:converter-gson:2.8.1" implementation "com.squareup.retrofit2:converter-gson:2.8.1"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.8.1"
implementation 'com.squareup.okio:okio:2.2.2' implementation 'com.squareup.okio:okio:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3' implementation 'io.reactivex.rxjava2:rxjava:2.2.3'

View file

@ -1,17 +1,14 @@
package fr.free.nrw.commons; package fr.free.nrw.commons;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import okhttp3.OkHttpClient;
import org.wikipedia.AppAdapter; import org.wikipedia.AppAdapter;
import org.wikipedia.dataclient.SharedPreferenceCookieManager; import org.wikipedia.dataclient.SharedPreferenceCookieManager;
import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.json.GsonMarshaller; import org.wikipedia.json.GsonMarshaller;
import org.wikipedia.json.GsonUnmarshaller; import org.wikipedia.json.GsonUnmarshaller;
import org.wikipedia.login.LoginResult;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import okhttp3.OkHttpClient;
public class CommonsAppAdapter extends AppAdapter { public class CommonsAppAdapter extends AppAdapter {
private final int DEFAULT_THUMB_SIZE = 640; private final int DEFAULT_THUMB_SIZE = 640;
@ -60,11 +57,6 @@ public class CommonsAppAdapter extends AppAdapter {
return sessionManager.getPassword(); return sessionManager.getPassword();
} }
@Override
public void updateAccount(@NonNull LoginResult result) {
sessionManager.updateAccount(result);
}
@Override @Override
public SharedPreferenceCookieManager getCookies() { public SharedPreferenceCookieManager getCookies() {
if (!preferences.contains(COOKIE_STORE_NAME)) { if (!preferences.contains(COOKIE_STORE_NAME)) {

View file

@ -25,6 +25,9 @@ import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.NavUtils; import androidx.core.app.NavUtils;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import fr.free.nrw.commons.auth.login.LoginClient;
import fr.free.nrw.commons.auth.login.LoginInterface;
import fr.free.nrw.commons.auth.login.LoginResult;
import fr.free.nrw.commons.databinding.ActivityLoginBinding; import fr.free.nrw.commons.databinding.ActivityLoginBinding;
import fr.free.nrw.commons.utils.ActivityUtils; import fr.free.nrw.commons.utils.ActivityUtils;
import java.util.Locale; import java.util.Locale;
@ -32,9 +35,7 @@ import org.wikipedia.AppAdapter;
import org.wikipedia.dataclient.ServiceFactory; import org.wikipedia.dataclient.ServiceFactory;
import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.mwapi.MwQueryResponse; import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import org.wikipedia.login.LoginClient; import fr.free.nrw.commons.auth.login.LoginClient.LoginCallback;
import org.wikipedia.login.LoginClient.LoginCallback;
import org.wikipedia.login.LoginResult;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -231,7 +232,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
private void doLogin(String username, String password, String twoFactorCode) { private void doLogin(String username, String password, String twoFactorCode) {
progressDialog.show(); progressDialog.show();
loginToken = ServiceFactory.get(commonsWikiSite).getLoginToken(); loginToken = ServiceFactory.get(commonsWikiSite, LoginInterface.class).getLoginToken();
loginToken.enqueue( loginToken.enqueue(
new Callback<MwQueryResponse>() { new Callback<MwQueryResponse>() {
@Override @Override
@ -313,7 +314,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
} }
compositeDisposable.clear(); compositeDisposable.clear();
sessionManager.setUserLoggedIn(true); sessionManager.setUserLoggedIn(true);
AppAdapter.get().updateAccount(loginResult); sessionManager.updateAccount(loginResult);
progressDialog.dismiss(); progressDialog.dismiss();
showSuccessAndDismissDialog(); showSuccessAndDismissDialog();
startMainActivity(); startMainActivity();

View file

@ -1,36 +0,0 @@
package fr.free.nrw.commons.auth;
import org.wikipedia.dataclient.Service;
import org.wikipedia.dataclient.mwapi.MwPostResponse;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import io.reactivex.Observable;
/**
* Handler for logout
*/
@Singleton
public class LogoutClient {
private final Service service;
@Inject
public LogoutClient(@Named("commons-service") Service service) {
this.service = service;
}
/**
* Fetches the CSRF token and uses that to post the logout api call
* @return
*/
public Observable<MwPostResponse> postLogout() {
return service.getCsrfToken().concatMap(tokenResponse -> service.postLogout(
Objects.requireNonNull(Objects.requireNonNull(tokenResponse.query()).csrfToken())));
}
}

View file

@ -9,8 +9,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.wikipedia.login.LoginResult; import fr.free.nrw.commons.auth.login.LoginResult;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;

View file

@ -1,15 +1,16 @@
package fr.free.nrw.commons.auth.csrf package fr.free.nrw.commons.auth.csrf
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import fr.free.nrw.commons.auth.SessionManager
import org.wikipedia.AppAdapter import org.wikipedia.AppAdapter
import org.wikipedia.dataclient.ServiceFactory import org.wikipedia.dataclient.ServiceFactory
import org.wikipedia.dataclient.SharedPreferenceCookieManager import org.wikipedia.dataclient.SharedPreferenceCookieManager
import org.wikipedia.dataclient.WikiSite import org.wikipedia.dataclient.WikiSite
import org.wikipedia.dataclient.mwapi.MwQueryResponse import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.login.LoginClient import fr.free.nrw.commons.auth.login.LoginClient
import org.wikipedia.login.LoginClient.LoginCallback import fr.free.nrw.commons.auth.login.LoginClient.LoginCallback
import org.wikipedia.login.LoginClient.LoginFailedException import fr.free.nrw.commons.auth.login.LoginClient.LoginFailedException
import org.wikipedia.login.LoginResult import fr.free.nrw.commons.auth.login.LoginResult
import retrofit2.Call import retrofit2.Call
import retrofit2.Response import retrofit2.Response
import timber.log.Timber import timber.log.Timber
@ -17,7 +18,10 @@ import java.io.IOException
import java.util.concurrent.Callable import java.util.concurrent.Callable
import java.util.concurrent.Executors.newSingleThreadExecutor import java.util.concurrent.Executors.newSingleThreadExecutor
class CsrfTokenClient(private val csrfWikiSite: WikiSite) { class CsrfTokenClient(
private val csrfWikiSite: WikiSite,
private val sessionManager: SessionManager
) {
private var retries = 0 private var retries = 0
private var csrfTokenCall: Call<MwQueryResponse?>? = null private var csrfTokenCall: Call<MwQueryResponse?>? = null
private val loginClient = LoginClient() private val loginClient = LoginClient()
@ -33,7 +37,8 @@ class CsrfTokenClient(private val csrfWikiSite: WikiSite) {
try { try {
if (retry > 0) { if (retry > 0) {
// Log in explicitly // Log in explicitly
LoginClient().loginBlocking(csrfWikiSite, userName, password, "") LoginClient()
.loginBlocking(csrfWikiSite, userName, password, "")
} }
// Get CSRFToken response off the main thread. // Get CSRFToken response off the main thread.
@ -121,10 +126,11 @@ class CsrfTokenClient(private val csrfWikiSite: WikiSite) {
password: String, password: String,
callback: Callback, callback: Callback,
retryCallback: () -> Unit retryCallback: () -> Unit
) = LoginClient().request(csrfWikiSite, username, password, object : LoginCallback { ) = LoginClient()
.request(csrfWikiSite, username, password, object : LoginCallback {
override fun success(loginResult: LoginResult) { override fun success(loginResult: LoginResult) {
if (loginResult.pass()) { if (loginResult.pass()) {
AppAdapter.get().updateAccount(loginResult) sessionManager.updateAccount(loginResult)
retryCallback() retryCallback()
} else { } else {
callback.failure(LoginFailedException(loginResult.message)) callback.failure(LoginFailedException(loginResult.message))

View file

@ -1,4 +1,4 @@
package org.wikipedia.login; package fr.free.nrw.commons.auth.login;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.text.TextUtils; import android.text.TextUtils;
@ -52,7 +52,7 @@ public class LoginClient {
@NonNull final String password, @NonNull final LoginCallback cb) { @NonNull final String password, @NonNull final LoginCallback cb) {
cancel(); cancel();
tokenCall = ServiceFactory.get(wiki).getLoginToken(); tokenCall = ServiceFactory.get(wiki, LoginInterface.class).getLoginToken();
tokenCall.enqueue(new Callback<MwQueryResponse>() { tokenCall.enqueue(new Callback<MwQueryResponse>() {
@Override public void onResponse(@NonNull Call<MwQueryResponse> call, @Override public void onResponse(@NonNull Call<MwQueryResponse> call,
@NonNull Response<MwQueryResponse> response) { @NonNull Response<MwQueryResponse> response) {
@ -75,8 +75,8 @@ public class LoginClient {
@Nullable final String loginToken, @NonNull final String userLanguage, @NonNull final LoginCallback cb) { @Nullable final String loginToken, @NonNull final String userLanguage, @NonNull final LoginCallback cb) {
this.userLanguage = userLanguage; this.userLanguage = userLanguage;
loginCall = TextUtils.isEmpty(twoFactorCode) && TextUtils.isEmpty(retypedPassword) loginCall = TextUtils.isEmpty(twoFactorCode) && TextUtils.isEmpty(retypedPassword)
? ServiceFactory.get(wiki).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL) ? ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL)
: ServiceFactory.get(wiki).postLogIn(userName, password, retypedPassword, twoFactorCode, loginToken, : ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, retypedPassword, twoFactorCode, loginToken,
userLanguage, true); userLanguage, true);
loginCall.enqueue(new Callback<LoginResponse>() { loginCall.enqueue(new Callback<LoginResponse>() {
@Override @Override
@ -117,15 +117,15 @@ public class LoginClient {
public void loginBlocking(@NonNull final WikiSite wiki, @NonNull final String userName, public void loginBlocking(@NonNull final WikiSite wiki, @NonNull final String userName,
@NonNull final String password, @Nullable final String twoFactorCode) throws Throwable { @NonNull final String password, @Nullable final String twoFactorCode) throws Throwable {
Response<MwQueryResponse> tokenResponse = ServiceFactory.get(wiki).getLoginToken().execute(); Response<MwQueryResponse> tokenResponse = ServiceFactory.get(wiki, LoginInterface.class).getLoginToken().execute();
if (tokenResponse.body() == null || TextUtils.isEmpty(tokenResponse.body().query().loginToken())) { if (tokenResponse.body() == null || TextUtils.isEmpty(tokenResponse.body().query().loginToken())) {
throw new IOException("Unexpected response when getting login token."); throw new IOException("Unexpected response when getting login token.");
} }
String loginToken = tokenResponse.body().query().loginToken(); String loginToken = tokenResponse.body().query().loginToken();
Call<LoginResponse> tempLoginCall = StringUtils.defaultIfEmpty(twoFactorCode, "").isEmpty() Call<LoginResponse> tempLoginCall = StringUtils.defaultIfEmpty(twoFactorCode, "").isEmpty()
? ServiceFactory.get(wiki).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL) ? ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL)
: ServiceFactory.get(wiki).postLogIn(userName, password, null, twoFactorCode, loginToken, : ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, null, twoFactorCode, loginToken,
userLanguage, true); userLanguage, true);
Response<LoginResponse> response = tempLoginCall.execute(); Response<LoginResponse> response = tempLoginCall.execute();
LoginResponse loginResponse = response.body(); LoginResponse loginResponse = response.body();
@ -153,7 +153,7 @@ public class LoginClient {
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
private void getExtendedInfo(@NonNull final WikiSite wiki, @NonNull String userName, private void getExtendedInfo(@NonNull final WikiSite wiki, @NonNull String userName,
@NonNull final LoginResult loginResult, @NonNull final LoginCallback cb) { @NonNull final LoginResult loginResult, @NonNull final LoginCallback cb) {
ServiceFactory.get(wiki).getUserInfo(userName) ServiceFactory.get(wiki, LoginInterface.class).getUserInfo(userName)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> { .subscribe(response -> {

View file

@ -0,0 +1,45 @@
package fr.free.nrw.commons.auth.login
import io.reactivex.Observable
import org.wikipedia.dataclient.Service
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import retrofit2.Call
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.Query
interface LoginInterface {
@Headers("Cache-Control: no-cache")
@GET(Service.MW_API_PREFIX + "action=query&meta=tokens&type=login")
fun getLoginToken(): Call<MwQueryResponse?>
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(Service.MW_API_PREFIX + "action=clientlogin&rememberMe=")
fun postLogIn(
@Field("username") user: String?,
@Field("password") pass: String?,
@Field("logintoken") token: String?,
@Field("uselang") userLanguage: String?,
@Field("loginreturnurl") url: String?
): Call<LoginClient.LoginResponse?>
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(Service.MW_API_PREFIX + "action=clientlogin&rememberMe=")
fun postLogIn(
@Field("username") user: String?,
@Field("password") pass: String?,
@Field("retype") retypedPass: String?,
@Field("OATHToken") twoFactorCode: String?,
@Field("logintoken") token: String?,
@Field("uselang") userLanguage: String?,
@Field("logincontinue") loginContinue: Boolean
): Call<LoginClient.LoginResponse?>
@GET(Service.MW_API_PREFIX + "action=query&meta=userinfo&list=users&usprop=groups|cancreate")
fun getUserInfo(@Query("ususers") userName: String): Observable<MwQueryResponse?>
}

View file

@ -1,4 +1,4 @@
package org.wikipedia.login; package fr.free.nrw.commons.auth.login;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;

View file

@ -1,4 +1,4 @@
package org.wikipedia.login; package fr.free.nrw.commons.auth.login;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;

View file

@ -1,4 +1,4 @@
package org.wikipedia.login; package fr.free.nrw.commons.auth.login;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;

View file

@ -10,6 +10,7 @@ import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.actions.PageEditClient; import fr.free.nrw.commons.actions.PageEditClient;
import fr.free.nrw.commons.actions.PageEditInterface; import fr.free.nrw.commons.actions.PageEditInterface;
import fr.free.nrw.commons.actions.ThanksInterface; import fr.free.nrw.commons.actions.ThanksInterface;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.category.CategoryInterface; import fr.free.nrw.commons.category.CategoryInterface;
import fr.free.nrw.commons.explore.depictions.DepictsClient; import fr.free.nrw.commons.explore.depictions.DepictsClient;
import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore;
@ -40,7 +41,7 @@ import org.wikipedia.dataclient.Service;
import org.wikipedia.dataclient.ServiceFactory; import org.wikipedia.dataclient.ServiceFactory;
import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.json.GsonUtil; import org.wikipedia.json.GsonUtil;
import org.wikipedia.login.LoginClient; import fr.free.nrw.commons.auth.login.LoginClient;
import timber.log.Timber; import timber.log.Timber;
@Module @Module
@ -105,8 +106,9 @@ public class NetworkingModule {
@Named(NAMED_COMMONS_CSRF) @Named(NAMED_COMMONS_CSRF)
@Provides @Provides
@Singleton @Singleton
public CsrfTokenClient provideCommonsCsrfTokenClient(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) { public CsrfTokenClient provideCommonsCsrfTokenClient(
return new CsrfTokenClient(commonsWikiSite); @Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite, SessionManager sessionManager) {
return new CsrfTokenClient(commonsWikiSite, sessionManager);
} }
@Provides @Provides

View file

@ -11,7 +11,7 @@ import org.mockito.Mock
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.wikipedia.dataclient.SharedPreferenceCookieManager import org.wikipedia.dataclient.SharedPreferenceCookieManager
import org.wikipedia.json.GsonMarshaller import org.wikipedia.json.GsonMarshaller
import org.wikipedia.login.LoginResult import fr.free.nrw.commons.auth.login.LoginResult
class CommonsAppAdapterUnitTest { class CommonsAppAdapterUnitTest {
@ -80,13 +80,6 @@ class CommonsAppAdapterUnitTest {
Assert.assertEquals(adapter.password, "test") Assert.assertEquals(adapter.password, "test")
} }
@Test
@Throws(Exception::class)
fun testUpdateAccount() {
adapter.updateAccount(result)
verify(sessionManager).updateAccount(result)
}
@Test @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testSetCookies() { fun testSetCookies() {

View file

@ -8,7 +8,6 @@ import org.wikipedia.dataclient.SharedPreferenceCookieManager;
import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.okhttp.TestStubInterceptor; import org.wikipedia.dataclient.okhttp.TestStubInterceptor;
import org.wikipedia.dataclient.okhttp.UnsuccessfulResponseInterceptor; import org.wikipedia.dataclient.okhttp.UnsuccessfulResponseInterceptor;
import org.wikipedia.login.LoginResult;
public class TestAppAdapter extends AppAdapter { public class TestAppAdapter extends AppAdapter {
@ -50,10 +49,6 @@ public class TestAppAdapter extends AppAdapter {
return null; return null;
} }
@Override
public void updateAccount(@NonNull LoginResult result) {
}
@Override @Override
public SharedPreferenceCookieManager getCookies() { public SharedPreferenceCookieManager getCookies() {
return null; return null;

View file

@ -8,13 +8,12 @@ import android.view.KeyEvent
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.Button
import android.widget.TextView import android.widget.TextView
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestAppAdapter import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.auth.login.LoginResult
import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.kvstore.JsonKvStore
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
@ -29,7 +28,6 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import org.robolectric.fakes.RoboMenuItem import org.robolectric.fakes.RoboMenuItem
import org.wikipedia.AppAdapter import org.wikipedia.AppAdapter
import org.wikipedia.login.LoginResult
import java.lang.reflect.Method import java.lang.reflect.Method

View file

@ -1,45 +0,0 @@
package fr.free.nrw.commons.auth
import com.nhaarman.mockitokotlin2.verify
import io.reactivex.Observable
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
import org.wikipedia.dataclient.Service
import org.wikipedia.dataclient.mwapi.MwPostResponse
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.dataclient.mwapi.MwQueryResult
import javax.inject.Inject
import javax.inject.Named
class LogoutClientTest {
@Mock @field:[Inject Named("commons-service")]
lateinit var service: Service
@InjectMocks
var logoutClient: LogoutClient? = null
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.openMocks(this)
val mwQueryResponse = mock(MwQueryResponse::class.java)
val mwQueryResult = mock(MwQueryResult::class.java)
`when`(mwQueryResult!!.csrfToken()).thenReturn("test_token")
`when`(mwQueryResponse.query()).thenReturn(mwQueryResult)
`when`(service!!.csrfToken)
.thenReturn(Observable.just(mwQueryResponse))
}
@Test
fun postLogout() {
`when`(service!!.postLogout(anyString())).thenReturn(Observable.just(mock(MwPostResponse::class.java)))
logoutClient!!.postLogout()
verify(service, times(1))!!.csrfToken
}
}

View file

@ -5,6 +5,7 @@ import android.accounts.AccountManager
import android.content.Context import android.content.Context
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.auth.login.LoginResult
import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.kvstore.JsonKvStore
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
@ -17,7 +18,6 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode import org.robolectric.annotation.LooperMode
import org.wikipedia.login.LoginResult
import java.lang.reflect.Method import java.lang.reflect.Method

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.auth.csrf
import com.google.gson.stream.MalformedJsonException import com.google.gson.stream.MalformedJsonException
import fr.free.nrw.commons.MockWebServerTest import fr.free.nrw.commons.MockWebServerTest
import fr.free.nrw.commons.auth.SessionManager
import org.junit.Test import org.junit.Test
import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq import org.mockito.ArgumentMatchers.eq
@ -16,8 +17,9 @@ import org.wikipedia.dataclient.okhttp.HttpStatusException
class CsrfTokenClientTest : MockWebServerTest() { class CsrfTokenClientTest : MockWebServerTest() {
private val wikiSite = WikiSite("test.wikipedia.org") private val wikiSite = WikiSite("test.wikipedia.org")
private val subject = CsrfTokenClient(wikiSite)
private val cb = mock(CsrfTokenClient.Callback::class.java) private val cb = mock(CsrfTokenClient.Callback::class.java)
private val sessionManager = mock(SessionManager::class.java)
private val subject = CsrfTokenClient(wikiSite, sessionManager)
@Test @Test
@Throws(Throwable::class) @Throws(Throwable::class)

View file

@ -0,0 +1,84 @@
package fr.free.nrw.commons.login;
import android.net.Uri;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.MalformedJsonException;
import fr.free.nrw.commons.MockWebServerTest;
import fr.free.nrw.commons.auth.login.LoginInterface;
import io.reactivex.observers.TestObserver;
import org.junit.Before;
import org.junit.Test;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import org.wikipedia.json.NamespaceTypeAdapter;
import org.wikipedia.json.PostProcessingTypeAdapter;
import org.wikipedia.json.UriTypeAdapter;
import org.wikipedia.json.WikiSiteTypeAdapter;
import org.wikipedia.page.Namespace;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class UserExtendedInfoClientTest extends MockWebServerTest {
private LoginInterface apiService;
@Override
@Before
public void setUp() throws Throwable {
super.setUp();
apiService = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(getGson()))
.baseUrl(server().getUrl())
.build()
.create(LoginInterface.class);
}
@Test
public void testRequestSuccess() throws Throwable {
enqueueFromFile("user_extended_info.json");
TestObserver<MwQueryResponse> observer = new TestObserver<>();
apiService.getUserInfo("USER").subscribe(observer);
observer
.assertComplete()
.assertNoErrors()
.assertValue(
result -> result.query().userInfo().id() == 24531888
&& result.query().getUserResponse("USER").name().equals("USER")
);
}
@Test
public void testRequestResponse404() {
enqueue404();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
apiService.getUserInfo("USER").subscribe(observer);
observer.assertError(Exception.class);
}
@Test
public void testRequestResponseMalformed() {
enqueueMalformed();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
apiService.getUserInfo("USER").subscribe(observer);
observer.assertError(MalformedJsonException.class);
}
private Gson getGson() {
return new GsonBuilder()
.registerTypeHierarchyAdapter(Uri.class, new UriTypeAdapter().nullSafe())
.registerTypeHierarchyAdapter(Namespace.class, new NamespaceTypeAdapter().nullSafe())
.registerTypeAdapter(WikiSite.class, new WikiSiteTypeAdapter().nullSafe())
.registerTypeAdapterFactory(new PostProcessingTypeAdapter())
.create();
}
}

View file

@ -0,0 +1,19 @@
{
"batchcomplete": true,
"query": {
"users": [
{
"userid": 24531888,
"name": "USER",
"implicitgroups": [
"*",
"user"
]
}
],
"userinfo": {
"id": 24531888,
"name": "USER"
}
}
}

View file

@ -4,7 +4,6 @@ import androidx.annotation.NonNull;
import org.wikipedia.dataclient.SharedPreferenceCookieManager; import org.wikipedia.dataclient.SharedPreferenceCookieManager;
import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.login.LoginResult;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -18,7 +17,6 @@ public abstract class AppAdapter {
public abstract boolean isLoggedIn(); public abstract boolean isLoggedIn();
public abstract String getUserName(); public abstract String getUserName();
public abstract String getPassword(); public abstract String getPassword();
public abstract void updateAccount(@NonNull LoginResult result);
public abstract SharedPreferenceCookieManager getCookies(); public abstract SharedPreferenceCookieManager getCookies();
public abstract void setCookies(@NonNull SharedPreferenceCookieManager cookies); public abstract void setCookies(@NonNull SharedPreferenceCookieManager cookies);

View file

@ -13,7 +13,6 @@ import org.wikipedia.dataclient.mwapi.page.MwMobileViewPageRemaining;
import org.wikipedia.dataclient.mwapi.page.MwQueryPageSummary; import org.wikipedia.dataclient.mwapi.page.MwQueryPageSummary;
import org.wikipedia.edit.Edit; import org.wikipedia.edit.Edit;
import org.wikipedia.edit.preview.EditPreview; import org.wikipedia.edit.preview.EditPreview;
import org.wikipedia.login.LoginClient;
import org.wikipedia.search.PrefixSearchResponse; import org.wikipedia.search.PrefixSearchResponse;
import org.wikipedia.wikidata.Entities; import org.wikipedia.wikidata.Entities;
@ -190,54 +189,6 @@ public interface Service {
@NonNull Observable<MwQueryResponse> getCategoryMembers(@NonNull @Query("cmtitle") String title, @NonNull Observable<MwQueryResponse> getCategoryMembers(@NonNull @Query("cmtitle") String title,
@Nullable @Query("cmcontinue") String continueStr); @Nullable @Query("cmcontinue") String continueStr);
// ------- CSRF, Login, and Create Account -------
@Headers("Cache-Control: no-cache")
@GET(MW_API_PREFIX + "action=query&meta=tokens&type=csrf")
@NonNull Observable<MwQueryResponse> getCsrfToken();
@SuppressWarnings("checkstyle:parameternumber")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=createaccount&createmessageformat=html")
@NonNull Observable<CreateAccountResponse> postCreateAccount(@NonNull @Field("username") String user,
@NonNull @Field("password") String pass,
@NonNull @Field("retype") String retype,
@NonNull @Field("createtoken") String token,
@NonNull @Field("createreturnurl") String returnurl,
@Nullable @Field("email") String email,
@Nullable @Field("captchaId") String captchaId,
@Nullable @Field("captchaWord") String captchaWord);
@Headers("Cache-Control: no-cache")
@GET(MW_API_PREFIX + "action=query&meta=tokens&type=login")
@NonNull Call<MwQueryResponse> getLoginToken();
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=clientlogin&rememberMe=")
@NonNull Call<LoginClient.LoginResponse> postLogIn(@Field("username") String user, @Field("password") String pass,
@Field("logintoken") String token, @Field("uselang") String userLanguage, @Field("loginreturnurl") String url);
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=clientlogin&rememberMe=")
@NonNull Call<LoginClient.LoginResponse> postLogIn(@Field("username") String user, @Field("password") String pass,
@Field("retype") String retypedPass, @Field("OATHToken") String twoFactorCode,
@Field("logintoken") String token, @Field("uselang") String userLanguage,
@Field("logincontinue") boolean loginContinue);
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=logout")
@NonNull Observable<MwPostResponse> postLogout(@NonNull @Field("token") String token);
@GET(MW_API_PREFIX + "action=query&meta=authmanagerinfo|tokens&amirequestsfor=create&type=createaccount")
@NonNull Observable<MwQueryResponse> getAuthManagerInfo();
@GET(MW_API_PREFIX + "action=query&meta=userinfo&list=users&usprop=groups|cancreate")
@NonNull Observable<MwQueryResponse> getUserInfo(@Query("ususers") @NonNull String userName);
// ------- Notifications ------- // ------- Notifications -------
@Headers("Cache-Control: no-cache") @Headers("Cache-Control: no-cache")

View file

@ -1,44 +0,0 @@
package org.wikipedia.login;
import com.google.gson.stream.MalformedJsonException;
import org.junit.Test;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import org.wikipedia.test.MockRetrofitTest;
import io.reactivex.Observable;
import io.reactivex.observers.TestObserver;
public class UserExtendedInfoClientTest extends MockRetrofitTest {
@Test public void testRequestSuccess() throws Throwable {
enqueueFromFile("user_extended_info.json");
TestObserver<MwQueryResponse> observer = new TestObserver<>();
getObservable().subscribe(observer);
final int id = 24531888;
observer.assertComplete().assertNoErrors()
.assertValue(result -> result.query().userInfo().id() == id
&& result.query().getUserResponse("USER").name().equals("USER"));
}
@Test public void testRequestResponse404() {
enqueue404();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
getObservable().subscribe(observer);
observer.assertError(Exception.class);
}
@Test public void testRequestResponseMalformed() {
enqueueMalformed();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
getObservable().subscribe(observer);
observer.assertError(MalformedJsonException.class);
}
private Observable<MwQueryResponse> getObservable() {
return getApiService().getUserInfo("USER");
}
}