diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2115831..ca1fede 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,28 +2,42 @@ + - + + + + + + + android:theme="@style/FUTheme"> + + + android:resource="@xml/provider_paths" /> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/de/sebse/fuplanner/MainActivity.java b/app/src/main/java/de/sebse/fuplanner/MainActivity.java index 0a1ce48..21043a4 100644 --- a/app/src/main/java/de/sebse/fuplanner/MainActivity.java +++ b/app/src/main/java/de/sebse/fuplanner/MainActivity.java @@ -1,5 +1,6 @@ package de.sebse.fuplanner; +import android.accounts.AccountManager; import android.content.Intent; import android.os.Bundle; import android.view.Menu; @@ -28,7 +29,6 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import de.sebse.fuplanner.fragments.CanteensFragment; -import de.sebse.fuplanner.fragments.LoginFragment; import de.sebse.fuplanner.fragments.ModulesFragment; import de.sebse.fuplanner.fragments.NewsFragment; import de.sebse.fuplanner.fragments.PrefsFragment; @@ -46,6 +46,8 @@ import de.sebse.fuplanner.services.KVV.KVVListener; import de.sebse.fuplanner.services.KVV.types.LoginToken; import de.sebse.fuplanner.services.KVV.types.Modules; import de.sebse.fuplanner.services.News.NewsManager; +import de.sebse.fuplanner.services.newkvv.AccountGeneral; +import de.sebse.fuplanner.tools.CustomAccountManager; import de.sebse.fuplanner.tools.MainActivityListener; import de.sebse.fuplanner.tools.NewAsyncQueue; import de.sebse.fuplanner.tools.Preferences; @@ -67,7 +69,6 @@ public class MainActivity extends AppCompatActivity private static final int FRAGMENT_STARTUP = 0; private static final int FRAGMENT_MODULES = 1; private static final int FRAGMENT_MODULES_DETAILS = 2; - private static final int FRAGMENT_LOGIN = 3; private static final int FRAGMENT_SCHEDULE = 4; private static final int FRAGMENT_CANTEENS = 5; private static final int FRAGMENT_CANTEENS_DETAILS = 6; @@ -93,10 +94,14 @@ public class MainActivity extends AppCompatActivity private boolean mOfflineBanner; private final NewAsyncQueue mQueue = new NewAsyncQueue(); private long mDoubleBackToExitPressedOnce = 0; + private CustomAccountManager mAccountManager; + private boolean isPaused = false; + private boolean isLoggedInBeforePause = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mAccountManager = new CustomAccountManager(AccountManager.get(this), () -> MainActivity.this); int desiredPage = getDefaultFragmentAfterLogin(); String desiredData = ""; if (savedInstanceState != null) { @@ -118,12 +123,53 @@ public class MainActivity extends AppCompatActivity mNavigationView.setNavigationItemSelectedListener(this); mFragmentManager = getSupportFragmentManager(); - if (!getKVV().account().restoreOnlineLogin()) { - desiredPage = FRAGMENT_LOGIN; + //if (mAccountManager.getAccountsByType(AccountGeneral.ACCOUNT_TYPE).length == 0) { + if (!mAccountManager.hasAccounts(AccountGeneral.ACCOUNT_TYPE)) { + desiredPage = getDefaultFragmentAfterLogout(); desiredData = ""; + mAccountManager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, null); + updateNavigation(); + changeFragment(desiredPage, desiredData); + } else { + updateNavigation(); + changeFragment(FRAGMENT_STARTUP); + int targetPage = desiredPage; + String targetData = desiredData; + getKVV().account().restoreOnlineLogin(isRestored -> { + updateNavigation(); + if (isRestored) + changeFragment(targetPage, targetData); + else + changeFragment(getDefaultFragmentAfterLogout()); + }); } - updateNavigation(); - changeFragment(desiredPage, desiredData); + } + + @Override + protected void onPause() { + super.onPause(); + isPaused = true; + isLoggedInBeforePause = getKVV().account().isLoggedIn(); + log.d("onPause", isPaused, isLoggedInBeforePause); + } + + @Override + protected void onResume() { + super.onResume(); + log.d("onResume", isPaused, isLoggedInBeforePause); + if (isPaused) { + getKVV().account().restoreOnlineLogin(isRestored -> { + log.d("onResume", isRestored); + updateNavigation(); + if (isRestored && !isLoggedInBeforePause) + changeFragment(getDefaultFragmentAfterLogin()); + else if (!isRestored && isLoggedInBeforePause) { + getKVV().account().logout(false); + changeFragment(getDefaultFragmentAfterLogout()); + } + }); + } + isPaused = false; } @Override @@ -153,11 +199,10 @@ public class MainActivity extends AppCompatActivity } } else if (getKVV().account().isLoggedIn() && mFragmentPage != getDefaultFragmentAfterLogin()) { changeFragment(getDefaultFragmentAfterLogin()); - } else if (!getKVV().account().isLoggedIn() && mFragmentPage != FRAGMENT_LOGIN) { - changeFragment(FRAGMENT_LOGIN); } else { mDoubleBackToExitPressedOnce = System.currentTimeMillis(); showToast(R.string.back_to_exit); + //getTokenForAccountCreateIfNeeded(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV); } } } @@ -255,7 +300,7 @@ public class MainActivity extends AppCompatActivity @Override protected void onSaveInstanceState(Bundle savedInstanceState) { - if (mFragmentPage != FRAGMENT_STARTUP && mFragmentPage != FRAGMENT_NONE && mFragmentPage != FRAGMENT_LOGIN) { + if (mFragmentPage != FRAGMENT_STARTUP && mFragmentPage != FRAGMENT_NONE) { Fragment fragment = mFragmentManager.findFragmentByTag(String.valueOf(mFragmentPage)); savedInstanceState.putInt(ARG_FRAGMENT_PAGE, mFragmentPage); if (fragment instanceof ModDetailFragment) { @@ -312,11 +357,16 @@ public class MainActivity extends AppCompatActivity return FRAGMENT_MODULES; } + private int getDefaultFragmentAfterLogout() { + return FRAGMENT_CANTEENS; + } + private void toLogoutState() { setOfflineBanner(false); setRefreshFailedBanner(false); updateNavigation(); - changeFragment(FRAGMENT_LOGIN); + changeFragment(getDefaultFragmentAfterLogout()); + mAccountManager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, null); } private void toLoginState(String fullName, String email, int newFragment, boolean onlineMode) { @@ -351,9 +401,9 @@ public class MainActivity extends AppCompatActivity case FRAGMENT_MODULES: fragment = ModulesFragment.newInstance(); break; - case FRAGMENT_LOGIN: + /*case FRAGMENT_LOGIN: fragment = LoginFragment.newInstance(); - break; + break;*/ case FRAGMENT_SCHEDULE: fragment = ScheduleFragment.newInstance(); break; @@ -475,7 +525,7 @@ public class MainActivity extends AppCompatActivity if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } - changeFragment(FRAGMENT_LOGIN); + mAccountManager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, null); }); } @@ -526,7 +576,7 @@ public class MainActivity extends AppCompatActivity menuItem.setIcon(R.drawable.ic_sms_failed); View view = View.inflate(this, R.layout.action_icon_number, null); TextView v = view.findViewById(R.id.number); - ((TextView) view.findViewById(R.id.number)).setText(String.format(Locale.getDefault(), "%d", i)); + v.setText(String.format(Locale.getDefault(), "%d", i)); menuItem.setActionView(view); } if (++count[0] == MAX_COUNT) done.run(); @@ -560,6 +610,9 @@ public class MainActivity extends AppCompatActivity + + + @Override public void onModulesFragmentInteraction(final String itemID) { changeFragment(FRAGMENT_MODULES_DETAILS, itemID); @@ -646,4 +699,9 @@ public class MainActivity extends AppCompatActivity public void onKVVNetworkResponse(NetworkResponse error) { setRefreshFailedBanner(error != null); } + + @Override + public CustomAccountManager getAccountManager() { + return mAccountManager; + } } diff --git a/app/src/main/java/de/sebse/fuplanner/fragments/LoginFragment.java b/app/src/main/java/de/sebse/fuplanner/fragments/LoginFragment.java deleted file mode 100644 index a481e4f..0000000 --- a/app/src/main/java/de/sebse/fuplanner/fragments/LoginFragment.java +++ /dev/null @@ -1,112 +0,0 @@ -package de.sebse.fuplanner.fragments; - -import android.app.ProgressDialog; -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import de.sebse.fuplanner.R; -import de.sebse.fuplanner.tools.MainActivityListener; -import de.sebse.fuplanner.tools.logging.Logger; - -/** - * A simple {@link Fragment} subclass. - * Use the {@link LoginFragment#newInstance} factory method to - * create an instance of this fragment. - */ -public class LoginFragment extends Fragment { - private final Logger log = new Logger(this); - @Nullable private MainActivityListener mActivityListener; - - public LoginFragment() { - // Required empty public constructor - } - - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @return A new instance of fragment LoginFragment. - */ - public static LoginFragment newInstance() { - LoginFragment fragment = new LoginFragment(); - Bundle args = new Bundle(); - fragment.setArguments(args); - return fragment; - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - View v = inflater.inflate(R.layout.fragment_login, container, false); - if (mActivityListener != null && mActivityListener.getKVV().account().isOfflineStoredAvailable()) { - Button offline_btn = v.findViewById(R.id.btn_offline); - offline_btn.setVisibility(View.VISIBLE); - offline_btn.setText(v.getResources().getString(R.string.enter_offline_mode, mActivityListener.getKVV().modules().list().getUsername())); - offline_btn.setOnClickListener(v1 -> mActivityListener.getKVV().account().doOfflineLogin()); - } - - View btn_login = v.findViewById(R.id.btn_login); - btn_login.setOnClickListener(view -> { - final ProgressDialog progressDialog = new ProgressDialog(LoginFragment.this.getContext(), - R.style.FUTheme_Dialog); - progressDialog.setIndeterminate(true); - progressDialog.setMessage("Authenticating..."); - progressDialog.show(); - - EditText input_usr = ((View) view.getParent()).findViewById(R.id.input_username); - EditText input_pwd = ((View) view.getParent()).findViewById(R.id.input_password); - - String username = input_usr.getText().toString(); - String password = input_pwd.getText().toString(); - - mActivityListener.getKVV().account().doOnlineLogin(username, password, success -> { - progressDialog.dismiss(); - mActivityListener.getGoogleAuth().setLoginState(username, password); - input_usr.setError(null); - input_pwd.setError(null); - }, error -> { - progressDialog.dismiss(); - // Invalid password - if (mActivityListener != null) { - if (error.getCode() == 100131) { - mActivityListener.showToast(R.string.invalid_credentials); - input_usr.setError(input_usr.getResources().getString(R.string.invalid_credentials)); - input_pwd.setError(input_pwd.getResources().getString(R.string.invalid_credentials)); - } else { - mActivityListener.showToast(v.getResources().getString(R.string.error_occurred_code, error.getCode())); - } - } - log.e("Error on KVV login!", error); - }); - }); - - return v; - } - - - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof MainActivityListener) { - mActivityListener = (MainActivityListener) context; - mActivityListener.onTitleTextChange(R.string.log_in); - } else - throw new RuntimeException(context.toString() + " must implement MainActivityListener"); - } - - @Override - public void onDetach() { - super.onDetach(); - mActivityListener = null; - } -} diff --git a/app/src/main/java/de/sebse/fuplanner/services/KVV/KVVListener.java b/app/src/main/java/de/sebse/fuplanner/services/KVV/KVVListener.java index 402899f..9747f7f 100644 --- a/app/src/main/java/de/sebse/fuplanner/services/KVV/KVVListener.java +++ b/app/src/main/java/de/sebse/fuplanner/services/KVV/KVVListener.java @@ -4,6 +4,7 @@ import com.android.volley.NetworkResponse; import de.sebse.fuplanner.services.GoogleAuth.Credentials; import de.sebse.fuplanner.services.KVV.types.LoginToken; +import de.sebse.fuplanner.tools.CustomAccountManager; import de.sebse.fuplanner.tools.network.NetworkCallback; import de.sebse.fuplanner.tools.network.NetworkErrorCallback; @@ -17,4 +18,6 @@ public interface KVVListener { void onModuleListChange(); void onKVVNetworkResponse(NetworkResponse error); + + CustomAccountManager getAccountManager(); } diff --git a/app/src/main/java/de/sebse/fuplanner/services/KVV/Login.java b/app/src/main/java/de/sebse/fuplanner/services/KVV/Login.java index 70c2c92..ca47f2e 100644 --- a/app/src/main/java/de/sebse/fuplanner/services/KVV/Login.java +++ b/app/src/main/java/de/sebse/fuplanner/services/KVV/Login.java @@ -2,19 +2,14 @@ package de.sebse.fuplanner.services.KVV; import android.content.Context; - import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import androidx.annotation.Nullable; import de.sebse.fuplanner.services.KVV.types.LoginToken; +import de.sebse.fuplanner.services.newkvv.AccountGeneral; +import de.sebse.fuplanner.tools.CustomAccountManager; import de.sebse.fuplanner.tools.NetworkCallbackCollector; import de.sebse.fuplanner.tools.network.HTTPService; import de.sebse.fuplanner.tools.network.NetworkCallback; @@ -33,63 +28,43 @@ public class Login extends HTTPService { this.mListener = listener; } + @Deprecated + public void reset() { + mToken = null; + mLoginPending = false; + mOnlineMode = false; + } + public void doOnlineLogin(@NotNull String username, @NotNull String password, NetworkCallback callback, NetworkErrorCallback errorCallback) { + + } + + //public boolean restoreOnlineLogin() { + // return restoreLogin(true); + //} + + //public boolean doOfflineLogin() { + // return restoreLogin(false); + //} + + public void restoreOnlineLogin(BooleanInterface callback) { if (mLoginPending) { - errorCallback.onError(new NetworkError(100160, -1, "Login already pending!")); - log.t(); + callback.run(false); + return; } mLoginPending = true; - doLogin(username, password, token -> { - testLoginToken(token, token2 -> { - setToken(token2, true); - mLoginPending = false; - callback.onResponse(token2); - }, error -> { - mLoginPending = false; - errorCallback.onError(error); - }); - }, error -> { + LoginToken.load(mListener.getAccountManager(), token -> { + boolean result = setToken(token, true); mLoginPending = false; - errorCallback.onError(error); + log.d("loginToken", token != null ? token.toString() : null); + callback.run(result); }); } - public boolean restoreOnlineLogin() { - return restoreLogin(true); - } - - public boolean doOfflineLogin() { - return restoreLogin(false); - } - - private boolean restoreLogin(boolean enteringOnlineMode) { - if (mLoginPending || mToken != null) - return false; - mLoginPending = true; - boolean result = false; - try { - result = setToken(LoginToken.load(getContext()), enteringOnlineMode); - } catch (FileNotFoundException ignored) { - } catch (IOException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - mLoginPending = false; - return result; - } - - public boolean isOfflineStoredAvailable() { - try { - LoginToken load = LoginToken.load(getContext()); - return load != null; - } catch (FileNotFoundException ignored) { - } catch (IOException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - return false; + public void isOfflineStoredAvailable(BooleanInterface callback) { + LoginToken.load(mListener.getAccountManager(), token -> { + callback.run(token != null); + }); } public boolean logout(boolean delete) { @@ -98,7 +73,7 @@ public class Login extends HTTPService { if (mToken == null) return true; if (delete) - mToken.delete(getContext()); + mToken.delete(mListener.getAccountManager()); mToken = null; return handleCallbacks(); } @@ -155,14 +130,27 @@ public class Login extends HTTPService { mRefreshCallbacks.add(success, error); if (!isFirst) return; - mListener.getCredentials(credentials -> { + CustomAccountManager manager = mListener.getAccountManager(); + manager.doInvalidateToken(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, ignored -> { + log.d("try restore", ignored); + restoreOnlineLogin(isRestored -> { + log.d("restore", isRestored, mToken); + if (isRestored) + testLoginToken(mRefreshCallbacks::responseResponse, mRefreshCallbacks::responseError); + else { + logout(true); + mRefreshCallbacks.responseError(new NetworkError(100180, 403, "Re-login failed!")); + } + }); + }); + /* mListener.getCredentials(credentials -> { doOnlineLogin(credentials.getUsername(), credentials.getPassword(), mRefreshCallbacks::responseResponse, mRefreshCallbacks::responseError); }, e -> { logout(false); mRefreshCallbacks.responseError(e); - }); + });*/ } @@ -182,272 +170,11 @@ public class Login extends HTTPService { return false; boolean isOnlyRefresh = mToken != null; mToken = token; - if (enteringOnlineMode) { - try { - mToken.save(getContext()); - } catch (IOException e) { - e.printStackTrace(); - } - } mOnlineMode = enteringOnlineMode; return isOnlyRefresh || handleCallbacks(); } - - - - - - - - - - private void doLogin(String username, String password, NetworkCallback callback, NetworkErrorCallback error) { - startKVVSession(success -> { - String kvvJSESSIONID = success.get("JSESSIONID"); - getSAMLRequest(kvvJSESSIONID, success1 -> startIdentSession(success1.get("Location"), success11 -> { - String identJSESSIONID = success11.get("JSESSIONID"); - String ident_idp_authn_lc_key = success11.get("_idp_authn_lc_key"); - String identROUTEID = success11.get("ROUTEID"); - loginIdent(true, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, success111 -> loginIdent(false, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, success11112 -> { - String ident_idp_session = success11112.get("_idp_session"); - getSAMLResponse(identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, ident_idp_session, success1111 -> loginKVV(success1111.get("RelayState"), success1111.get("SAMLResponse"), kvvJSESSIONID, success111112 -> { - LoginToken token = new LoginToken(username, success111112.get("shibsessionKey"), success111112.get("shibsessionName"), kvvJSESSIONID); - finishKVVlogin(token, success11111 -> callback.onResponse(token), error); - }, error), error); - }, error), error); - }, error), error); - }, error); - } - - /* - GET https://kvv.imp.fu-berlin.de/portal/login - -> JSESSIONID 5c10406f-588c-4c16-96e9-c80d115417de.tomcat1 - */ - private void startKVVSession(final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { - get("https://kvv.imp.fu-berlin.de/portal/login", null, response -> { - String cookies = response.getHeaders().get("Set-Cookie"); - if (cookies==null) { - errorCallback.onError(new NetworkError(100101, -1, "Error on starting KVV session!")); - return; - } - HashMap object; - try { - object = getCookie(cookies, new String[]{"JSESSIONID"}); - } catch (NoSuchFieldException e) { - errorCallback.onError(new NetworkError(100102, -1, "Error on starting KVV session!")); - return; - } - callback.onResponse(object); - }, error -> errorCallback.onError(new NetworkError(100100, error.networkResponse.statusCode, "Error on starting KVV session!"))); - } - - /* - GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container - <- JSESSIONID - -> (Location-Header) https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO - ?SAMLRequest=fZLLb.....Q8yre3X1IHwkJKE0Mnpy/V9TH4A - &RelayState=ss:mem:7ea01e29157b8bd906f7002176.....0d1a505f2c8bf - */ - private void getSAMLRequest(String JSESSIONID, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { - HashMap cookies = new HashMap<>(); - cookies.put("JSESSIONID", JSESSIONID); - get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", cookies, response -> { - String location = response.getHeaders().get("Location"); - if (location==null) { - errorCallback.onError(new NetworkError(100111, -1, "Error on getting SAML request!")); - return; - } - HashMap object = new HashMap<>(); - object.put("Location", location); - callback.onResponse(object); - }, error -> errorCallback.onError(new NetworkError(100110, error.networkResponse.statusCode, "Error on getting SAML request!"))); - } - - /* - GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO - ?SAMLRequest=fZLLbsIwEEV/JfI+cWJAUIsgpbAoEi2IpF10UznxUKw6dupxaPn7hkdb2LD29bkzRzNGUeuGZ63fmjV8toA++K61QX58SEnrDLcCFXIjakDuK55njwvOopg3znpbWU2CDBGcV9ZMrcG2BpeD26kKnteLlGy9b5BT+rHbRapuok0bluC0MpEEmm9VWVoNfhshWnpgM7pa5gUJZt0wyogD9h+iJBiv/P6aomQTbtqSdhNtlIYzZg1SOag8zfMlCeazlLyNqpHsy1gO2V1fVsNBMuqJoUyAJaxXDUaiiyG2MDfohfEpYXEyDJM4ZKxgCe/FPI5fSbA6L36vjFTm/bal8hRC/lAUq/C02gs4PK7VBchkfHDNj8Xuwv5trPhVTiY3BeOf4DG96DmVNvypA89nK6tVtQ8yre3X1IHwkJKE0Mnpy/V9TH4A - &RelayState=ss:mem:7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf - -> JSESSIONID C4B6A428BA1F50746235D03F5D107A57 - -> _idp_authn_lc_key 57a6ae26067f374cc3d0ccfc47e27b04b47752d2a3d4eb2782af0d3994535395 - -> ROUTEID .1 - */ - private void startIdentSession(String url, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { - get(url, null, response -> { - String cookies = response.getHeaders().get("Set-Cookie"); - if (cookies==null) { - errorCallback.onError(new NetworkError(100121, -1, "Error on starting Ident session!")); - return; - } - HashMap object; - try { - object = getCookie(cookies, new String[]{"JSESSIONID", "_idp_authn_lc_key", "ROUTEID"}); - } catch (NoSuchFieldException e) { - errorCallback.onError(new NetworkError(100122, -1, "Error on starting Ident session!")); - return; - } - callback.onResponse(object); - }, error -> errorCallback.onError(new NetworkError(100120, error.networkResponse.statusCode, "Error on starting Ident session!"))); - } - - /* - POST https://identity.fu-berlin.de/idp-fub/Authn/UserPassword - <- j_username seedorf96 - <- j_password neinhieristpatrick - <- (Header-"Content-Type") application/x-www-form-urlencoded - <- JSESSIONID - <- _idp_authn_lc_key - <- ROUTEID - -> _idp_session OTMuMTkzLjg1LjMz|LQ==|OGYxOWI4MjA2NTQ4YWUwYzJkOWM4Mjk4YzcwZDMwZmJiZjBmMTdmMzkyZGU2OWIwY2JkNmZlNjlmNTRmNzBlMQ==|wLlzQal7VqyntmG2vLNn06wt8wQ= - */ - private void loginIdent(final boolean first, String username, String password, String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { - HashMap cookies = new HashMap<>(); - cookies.put("JSESSIONID", JSESSIONID); - cookies.put("_idp_authn_lc_key", _idp_authn_lc_key); - cookies.put("ROUTEID", ROUTEID); - HashMap body = new HashMap<>(); - body.put("j_username", username); - body.put("j_password", password); - post("https://identity.fu-berlin.de/idp-fub/Authn/UserPassword", cookies, body, response -> { - if (first) { - callback.onResponse(new HashMap<>()); - return; - } - - String cookies1 = response.getHeaders().get("Set-Cookie"); - if (cookies1 ==null) { - errorCallback.onError(new NetworkError(100131, -1, "Error on logging in to Identity Server!")); - return; - } - HashMap object; - try { - object = getCookie(cookies1, new String[]{"_idp_session"}); - } catch (NoSuchFieldException e) { - errorCallback.onError(new NetworkError(100132, -1, "Error on logging in to Identity Server!")); - return; - } - callback.onResponse(object); - }, error -> errorCallback.onError(new NetworkError(100130, error.networkResponse.statusCode, "Error on logging in to Identity Server!"))); - } - - /* - GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO - <- JSESSIONID - <- _idp_authn_lc_key - <- ROUTEID - <- _idp_session - -> (BODY) RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf - -> (BODY) SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg== - */ - private void getSAMLResponse(String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, String _idp_session, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { - HashMap cookies = new HashMap<>(); - cookies.put("JSESSIONID", JSESSIONID); - cookies.put("_idp_authn_lc_key", _idp_authn_lc_key); - cookies.put("ROUTEID", ROUTEID); - cookies.put("_idp_session", _idp_session); - get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO", cookies, response -> { - String body = response.getParsed(); - if (body == null) { - errorCallback.onError(new NetworkError(100143, -1, "Error on getting SAML response!")); - return; - } - - HashMap object = new HashMap<>(); - - Pattern pattern = Pattern.compile("ss:mem:([0-9a-f]+)"); - Matcher matcher = pattern.matcher(body); - if (!matcher.find()) { - errorCallback.onError(new NetworkError(100142, -1, "Error on getting SAML response!")); - return; - } - object.put("RelayState", "ss:mem:"+matcher.group(1)); - - pattern = Pattern.compile("name=\"SAMLResponse\" value=\"([0-9a-zA-Z+]+=*)"); - matcher = pattern.matcher(body); - if (!matcher.find()) { - errorCallback.onError(new NetworkError(100141, -1, "Error on getting SAML response!")); - return; - } - object.put("SAMLResponse", matcher.group(1)); - - callback.onResponse(object); - }, error -> errorCallback.onError(new NetworkError(100140, error.networkResponse.statusCode, "Error on getting SAML response!"))); - } - - - /* - POST https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST - <- RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf - <- SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg== - <- JSESSIONID - -> _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468 - _b1912c5a03d733a80bd3fee772bf68d4 - */ - private void loginKVV(String RelayState, String SAMLResponse, String JSESSIONID, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { - HashMap cookies = new HashMap<>(); - cookies.put("JSESSIONID", JSESSIONID); - HashMap body = new HashMap<>(); - body.put("RelayState", RelayState); - body.put("SAMLResponse", SAMLResponse); - post("https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST", cookies, body, response -> { - String cookies1 = response.getHeaders().get("Set-Cookie"); - if (cookies1 ==null) { - errorCallback.onError(new NetworkError(100151, -1, "Error on starting KVV session!")); - return; - } - HashMap object = new HashMap<>(); - - - Pattern pattern = Pattern.compile("(_shibsession_[0-9a-f]+)=([^;]+);"); - Matcher matcher = pattern.matcher(cookies1); - if (!matcher.find()) { - errorCallback.onError(new NetworkError(100152, -1, "Error on starting Ident session!")); - } - object.put("shibsessionKey", matcher.group(1)); - object.put("shibsessionName", matcher.group(2)); - - callback.onResponse(object); - }, error -> errorCallback.onError(new NetworkError(100150, error.networkResponse.statusCode, "Error on starting Ident session!"))); - } - - - /* - GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container - <- JSESSIONID - <- _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468 - _b1912c5a03d733a80bd3fee772bf68d4 - */ - private void finishKVVlogin(LoginToken loginToken, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { - get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", loginToken.getCookies(), response -> callback.onResponse(new HashMap<>()), error -> errorCallback.onError(new NetworkError(100160, error.networkResponse.statusCode, "Cannot finish login process!"))); - } - - - - - - - - - - - - - private String getCookie(String cookies, String name) throws NoSuchFieldException { - Pattern pattern = Pattern.compile(name+"=([^;]+);"); - Matcher matcher = pattern.matcher(cookies); - if (!matcher.find()) { - log.e("GETcookie failed", name); - log.e("GETcookie failed", cookies); - throw new NoSuchFieldException(); - } - return matcher.group(1); - } - - private HashMap getCookie(String cookies, String[] names) throws NoSuchFieldException { - HashMap result = new HashMap<>(); - for (String name: names) { - result.put(name,this.getCookie(cookies, name)); - } - return result; + public interface BooleanInterface { + void run(boolean bool); } } diff --git a/app/src/main/java/de/sebse/fuplanner/services/KVV/types/LoginToken.java b/app/src/main/java/de/sebse/fuplanner/services/KVV/types/LoginToken.java index 0657069..544729f 100644 --- a/app/src/main/java/de/sebse/fuplanner/services/KVV/types/LoginToken.java +++ b/app/src/main/java/de/sebse/fuplanner/services/KVV/types/LoginToken.java @@ -2,23 +2,25 @@ package de.sebse.fuplanner.services.KVV.types; import android.content.Context; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.HashMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import de.sebse.fuplanner.services.newkvv.AccountGeneral; +import de.sebse.fuplanner.tools.CustomAccountManager; +import de.sebse.fuplanner.tools.logging.Logger; /** * Created by sebastian on 29.10.17. */ public class LoginToken implements Serializable { + static Logger log = new Logger("LoginToken"); private static final String FILE_NAME = "LoginTokenSaving"; private final String username; @@ -36,33 +38,35 @@ public class LoginToken implements Serializable { } @Nullable - public static LoginToken load(Context context) throws IOException, ClassNotFoundException { - FileInputStream fis; - try { - fis = context.openFileInput(FILE_NAME); - } catch (FileNotFoundException e) { - return null; + public static void load(CustomAccountManager manager, LoginTokenInterface callback) { + log.d("try to login"); + if (!manager.hasAccounts(AccountGeneral.ACCOUNT_TYPE)) { + callback.run(null); + return; } - ObjectInputStream is = new ObjectInputStream(fis); - Object readObject = is.readObject(); - if (!(readObject instanceof LoginToken)) - return null; - LoginToken loginToken = (LoginToken) readObject; - is.close(); - fis.close(); - return loginToken; + log.d("has accounts"); + manager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, tokenString -> { + //manager.getTokenByTypeSync(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV); + if (tokenString == null) { + callback.run(null); + return; + } + log.d("got token string", tokenString); + callback.run(LoginToken.fromJsonString(tokenString)); + //return LoginToken.fromJsonString(tokenString); + }); } public void save(Context context) throws IOException { - FileOutputStream fos = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE); + /*FileOutputStream fos = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(this); os.close(); - fos.close(); + fos.close();*/ } - public void delete(Context context) { - context.deleteFile(FILE_NAME); + public void delete(CustomAccountManager manager) { + manager.deleteAccount(AccountGeneral.ACCOUNT_TYPE); } public void setAdditionals(String fullName, String email) { @@ -118,4 +122,43 @@ public class LoginToken implements Serializable { } return result.substring(0, result.length()-1); } + + public String toJsonString() { + JSONObject json = new JSONObject(); + try { + json.put("username", username); + json.put("shibsessionKey", shibsessionKey); + json.put("shibsessionName", shibsessionName); + json.put("JSESSIONID", JSESSIONID); + json.put("fullName", fullName); + json.put("email", email); + } catch (JSONException e) { + return null; + } + return json.toString(); + } + + public static LoginToken fromJsonString(String tokenString) { + try { + JSONObject json = new JSONObject(tokenString); + LoginToken token = new LoginToken( + json.getString("username"), + json.getString("shibsessionName"), + json.getString("shibsessionName"), + json.getString("JSESSIONID")); + if (!json.isNull("fullName")) + token.setAdditionals( + json.getString("fullName"), + json.getString("email") + ); + return token; + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } + + public interface LoginTokenInterface { + void run(LoginToken token); + } } diff --git a/app/src/main/java/de/sebse/fuplanner/services/newkvv/AccountGeneral.java b/app/src/main/java/de/sebse/fuplanner/services/newkvv/AccountGeneral.java new file mode 100644 index 0000000..2d98253 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/newkvv/AccountGeneral.java @@ -0,0 +1,7 @@ +package de.sebse.fuplanner.services.newkvv; + +public class AccountGeneral { + public static final String ACCOUNT_TYPE = "de.sebse.fuplanner.fuauth"; + public static final String AUTHTOKEN_TYPE_KVV = "KVV"; + public static final String AUTHTOKEN_TYPE_BLACKBOARD = "Blackboard"; +} diff --git a/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticator.java b/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticator.java new file mode 100644 index 0000000..6dfba9c --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticator.java @@ -0,0 +1,104 @@ +package de.sebse.fuplanner.services.newkvv; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.AccountManager; +import android.accounts.NetworkErrorException; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; + +import java.util.concurrent.ExecutionException; + +public class FUAuthenticator extends AbstractAccountAuthenticator { + + private final Context mContext; + + public FUAuthenticator(Context context) { + super(context); + this.mContext = context; + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) { + return null; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { + final Intent intent = new Intent(mContext, FUAuthenticatorActivity.class); + intent.putExtra(FUAuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType); + intent.putExtra(FUAuthenticatorActivity.ARG_AUTH_TYPE, authTokenType); + intent.putExtra(FUAuthenticatorActivity.ARG_IS_ADDING_NEW_ACCOUNT, true); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); + final Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + return bundle; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException { + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + + // Extract the username and password from the Account Manager, and ask + // the server for an appropriate AuthToken. + final AccountManager am = AccountManager.get(mContext); + + String authToken = am.peekAuthToken(account, authTokenType); + + // Lets give another try to authenticate the user + if (TextUtils.isEmpty(authToken)) { + final String password = am.getPassword(account); + if (password != null) { + try { + authToken = new UserLoginTask(account.name, password, authTokenType, mContext).execute((Void) null).get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + } + + // If we get an authToken - we return it + if (!TextUtils.isEmpty(authToken)) { + final Bundle result = new Bundle(); + result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); + result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); + result.putString(AccountManager.KEY_AUTHTOKEN, authToken); + return result; + } + + // If we get here, then we couldn't access the user's password - so we + // need to re-prompt them for their credentials. We do that by creating + // an intent to display our AuthenticatorActivity. + final Intent intent = new Intent(mContext, FUAuthenticatorActivity.class); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); + intent.putExtra(FUAuthenticatorActivity.ARG_ACCOUNT_TYPE, account.type); + intent.putExtra(FUAuthenticatorActivity.ARG_AUTH_TYPE, authTokenType); + final Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + return bundle; + } + + @Override + public String getAuthTokenLabel(String s) { + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException { + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException { + return null; + } +} diff --git a/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticatorActivity.java b/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticatorActivity.java new file mode 100644 index 0000000..03ee6cd --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticatorActivity.java @@ -0,0 +1,290 @@ +package de.sebse.fuplanner.services.newkvv; + +import android.accounts.Account; +import android.accounts.AccountAuthenticatorActivity; +import android.accounts.AccountManager; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.TargetApi; +import android.app.LoaderManager.LoaderCallbacks; +import android.content.Context; +import android.content.CursorLoader; +import android.content.Intent; +import android.content.Loader; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.text.TextUtils; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; + +import androidx.annotation.NonNull; +import de.sebse.fuplanner.R; + +import static de.sebse.fuplanner.services.newkvv.UserLoginTask.PARAM_USER_PASS; + +/** + * A login screen that offers login via email/password. + */ +public class FUAuthenticatorActivity extends AccountAuthenticatorActivity implements LoaderCallbacks { + + /** + * Id to identity READ_CONTACTS permission request. + */ + private static final int REQUEST_READ_CONTACTS = 0; + + /** + * Keep track of the login task to ensure we can cancel it if requested. + */ + UserLoginTask mAuthTask = null; + + // UI references. + private EditText mEmailView; + EditText mPasswordView; + private View mProgressView; + private View mLoginFormView; + + + + public static final String ARG_ACCOUNT_TYPE = "ARG_ACCOUNT_TYPE"; + public static final String ARG_AUTH_TYPE = "ARG_AUTH_TYPE"; + public static final String ARG_IS_ADDING_NEW_ACCOUNT = "ARG_IS_ADDING_NEW_ACCOUNT"; + private String mAccountType; + private String mAuthTokenType; + private boolean mIsAddingNewAccount; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mAccountType = getIntent().getStringExtra(ARG_ACCOUNT_TYPE); + mAuthTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE); + mIsAddingNewAccount = getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false); + + setContentView(R.layout.activity_fu_authenticator); + // Set up the login form. + mEmailView = findViewById(R.id.input_username); + populateAutoComplete(); + + mPasswordView = findViewById(R.id.input_password); + mPasswordView.setOnEditorActionListener((textView, id, keyEvent) -> { + if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { + attemptLogin(); + return true; + } + return false; + }); + + Button mEmailSignInButton = findViewById(R.id.btn_login); + mEmailSignInButton.setOnClickListener(view -> attemptLogin()); + + mLoginFormView = findViewById(R.id.login_form); + mProgressView = findViewById(R.id.login_progress); + } + + private void populateAutoComplete() { + if (!mayRequestContacts()) { + return; + } + + getLoaderManager().initLoader(0, null, this); + } + + private boolean mayRequestContacts() { + /*if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return true; + } + if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { + return true; + } + if (shouldShowRequestPermissionRationale(READ_CONTACTS)) { + Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE) + .setAction(android.R.string.ok, new View.OnClickListener() { + @Override + @TargetApi(Build.VERSION_CODES.M) + public void onClick(View v) { + requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); + } + }); + } else { + requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); + }*/ + return false; + } + + /** + * Callback received when a permissions request has been completed. + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (requestCode == REQUEST_READ_CONTACTS) { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + populateAutoComplete(); + } + } + } + + + /** + * Attempts to sign in or register the account specified by the login form. + * If there are form errors (invalid email, missing fields, etc.), the + * errors are presented and no actual login attempt is made. + */ + private void attemptLogin() { + InputMethodManager inputManager = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputManager != null && getCurrentFocus() != null) + inputManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + if (mAuthTask != null) { + return; + } + + // Reset errors. + mEmailView.setError(null); + mPasswordView.setError(null); + + // Store values at the time of the login attempt. + String email = mEmailView.getText().toString(); + String password = mPasswordView.getText().toString(); + + boolean cancel = false; + View focusView = null; + + // Check for a valid password, if the user entered one. + if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { + mPasswordView.setError(getString(R.string.error_invalid_password)); + focusView = mPasswordView; + cancel = true; + } + + // Check for a valid email address. + if (TextUtils.isEmpty(email)) { + mEmailView.setError(getString(R.string.error_field_required)); + focusView = mEmailView; + cancel = true; + } else if (!isEmailValid(email)) { + mEmailView.setError(getString(R.string.error_invalid_email)); + focusView = mEmailView; + cancel = true; + } + + if (cancel) { + // There was an error; don't attempt login and focus the first + // form field with an error. + focusView.requestFocus(); + } else { + // Show a progress spinner, and kick off a background task to + // perform the user login attempt. + showProgress(true); + mAuthTask = new UserLoginTask(email, password, mAuthTokenType, this); + mAuthTask.execute((Void) null); + } + } + + private boolean isEmailValid(String email) { + //TODO: Replace this with your own logic + return true;//email.contains("@"); + } + + private boolean isPasswordValid(String password) { + //TODO: Replace this with your own logic + return password.length() > 4; + } + + /** + * Shows the progress UI and hides the login form. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) + void showProgress(final boolean show) { + // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow + // for very easy animations. If available, use these APIs to fade-in + // the progress spinner. + int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); + + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + mLoginFormView.animate().setDuration(shortAnimTime).alpha( + show ? 0 : 1).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + } + }); + + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + mProgressView.animate().setDuration(shortAnimTime).alpha( + show ? 1 : 0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + } + }); + } + + @Override + public Loader onCreateLoader(int i, Bundle bundle) { + return new CursorLoader(this, + // Retrieve data rows for the device user's 'profile' contact. + Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, + ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION, + + // Select only email addresses. + ContactsContract.Contacts.Data.MIMETYPE + + " = ?", new String[]{ContactsContract.CommonDataKinds.Email + .CONTENT_ITEM_TYPE}, + + // Show primary email addresses first. Note that there won't be + // a primary email address if the user hasn't specified one. + ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"); + } + + @Override + public void onLoadFinished(Loader cursorLoader, Cursor cursor) { + } + + @Override + public void onLoaderReset(Loader cursorLoader) { + + } + + + private interface ProfileQuery { + String[] PROJECTION = { + ContactsContract.CommonDataKinds.Email.ADDRESS, + ContactsContract.CommonDataKinds.Email.IS_PRIMARY, + }; + + int ADDRESS = 0; + int IS_PRIMARY = 1; + } + + void finishLogin(Intent intent) { + String accountName = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); + String accountPassword = intent.getStringExtra(PARAM_USER_PASS); + final Account account = new Account(accountName, intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE)); + final AccountManager mAccountManager = AccountManager.get(this); + + if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false)) { + String authtoken = intent.getStringExtra(AccountManager.KEY_AUTHTOKEN); + String authtokenType = mAuthTokenType; + + // Creating the account on the device and setting the auth token we got + // (Not setting the auth token will cause another call to the server to authenticate the user) + mAccountManager.addAccountExplicitly(account, accountPassword, null); + mAccountManager.setAuthToken(account, authtokenType, authtoken); + } else { + mAccountManager.setPassword(account, accountPassword); + } + + setAccountAuthenticatorResult(intent.getExtras()); + setResult(RESULT_OK, intent); + finish(); + } +} + diff --git a/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticatorService.java b/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticatorService.java new file mode 100644 index 0000000..a6f1516 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/newkvv/FUAuthenticatorService.java @@ -0,0 +1,13 @@ +package de.sebse.fuplanner.services.newkvv; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class FUAuthenticatorService extends Service { + @Override + public IBinder onBind(Intent intent) { + FUAuthenticator authenticator = new FUAuthenticator(this); + return authenticator.getIBinder(); + } +} diff --git a/app/src/main/java/de/sebse/fuplanner/services/newkvv/UserLoginTask.java b/app/src/main/java/de/sebse/fuplanner/services/newkvv/UserLoginTask.java new file mode 100644 index 0000000..3338106 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/newkvv/UserLoginTask.java @@ -0,0 +1,124 @@ +package de.sebse.fuplanner.services.newkvv; + +import android.accounts.AccountManager; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Build; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import androidx.annotation.Nullable; +import de.sebse.fuplanner.R; +import de.sebse.fuplanner.services.KVV.types.LoginToken; +import de.sebse.fuplanner.services.newkvv.network.Login; +import de.sebse.fuplanner.tools.logging.Logger; + + +/** + * Represents an asynchronous login/registration task used to authenticate + * the user. + */ +public class UserLoginTask extends AsyncTask { + + /** + * A dummy authentication store containing known user names and passwords. + * TODO: remove after connecting to a real authentication system. + */ + /*private static final String[] DUMMY_CREDENTIALS = new String[]{ + "foo@example.com:hello", "bar@example.com:world" + };*/ + static final String PARAM_USER_PASS = "PARAM_USER_PASS"; + + private final String mEmail; + private final String mPassword; + private final Login mVolleyLogin; + private String mTokenType; + private Logger log = new Logger(this); + @SuppressLint("StaticFieldLeak") + @Nullable + private FUAuthenticatorActivity mActivity; + + UserLoginTask(String email, String password, String tokenType, @NotNull Context context) { + mEmail = email; + mPassword = password; + mTokenType = tokenType; + mVolleyLogin = new Login(context); + if (context instanceof FUAuthenticatorActivity) + mActivity = (FUAuthenticatorActivity) context; + } + + @Override + protected String doInBackground(Void... params) { + // TODO: attempt authentication against a network service. + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference login = new AtomicReference<>(); + mVolleyLogin.doLogin(mEmail, mPassword, success -> { + mVolleyLogin.testLoginToken(success, success1 -> { + login.set(success); + latch.countDown(); + }, error -> latch.countDown()); + }, error -> latch.countDown()); + try { + latch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + log.d(login.get()); + + if (login.get() == null) { + return null; + } else { + return login.get().toJsonString(); + } + + /*for (String credential : DUMMY_CREDENTIALS) { + String[] pieces = credential.split(":"); + if (pieces[0].equals(mEmail)) { + // Account exists, return true if the password matches. + return pieces[1].equals(mPassword) ? "auth token here" : null; + } + } + + // TODO: register the new account here. + return null;*/ + } + + @Override + protected void onPostExecute(final String success) { + if (mActivity == null || mActivity.isFinishing()) + return; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mActivity.isDestroyed()) + return; + mActivity.mAuthTask = null; + mActivity.showProgress(false); + + if (success != null) { + final Intent res = new Intent(); + res.putExtra(AccountManager.KEY_ACCOUNT_NAME, mEmail); + res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountGeneral.ACCOUNT_TYPE); + res.putExtra(AccountManager.KEY_AUTHTOKEN, success); + res.putExtra(PARAM_USER_PASS, mPassword); + mActivity.finishLogin(res); + } else { + mActivity.mPasswordView.setError(mActivity.getString(R.string.error_incorrect_password)); + mActivity.mPasswordView.requestFocus(); + } + } + + @Override + protected void onCancelled() { + if (mActivity == null || mActivity.isFinishing()) + return; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mActivity.isDestroyed()) + return; + mActivity.mAuthTask = null; + mActivity.showProgress(false); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/sebse/fuplanner/services/newkvv/network/Login.java b/app/src/main/java/de/sebse/fuplanner/services/newkvv/network/Login.java new file mode 100644 index 0000000..5c67b22 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/services/newkvv/network/Login.java @@ -0,0 +1,297 @@ +package de.sebse.fuplanner.services.newkvv.network; + +import android.content.Context; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.sebse.fuplanner.services.KVV.types.LoginToken; +import de.sebse.fuplanner.tools.network.HTTPService; +import de.sebse.fuplanner.tools.network.NetworkCallback; +import de.sebse.fuplanner.tools.network.NetworkError; +import de.sebse.fuplanner.tools.network.NetworkErrorCallback; + +public class Login extends HTTPService { + public Login(Context context) { + super(context); + } + + + public void testLoginToken(@NotNull LoginToken token, @NotNull NetworkCallback callback, @NotNull NetworkErrorCallback errorCallback) { + get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", token.getUsername()), token.getCookies(), response -> { + String body = response.getParsed(); + if (body == null) { + errorCallback.onError(new NetworkError(100172, 403, "Testing login failed!")); + return; + } + try { + JSONObject json = new JSONObject(body); + String displayName = json.getString("displayName"); + String email = json.getString("email"); + token.setAdditionals(displayName, email); + callback.onResponse(token); + } catch (JSONException e) { + errorCallback.onError(new NetworkError(100171, 403, "Cannot parse profile!")); + } + }, error -> errorCallback.onError(new NetworkError(100170, error.networkResponse.statusCode, "Testing login failed!"))); + } + + + + + + + public void doLogin(String username, String password, NetworkCallback callback, NetworkErrorCallback error) { + startKVVSession(success -> { + String kvvJSESSIONID = success.get("JSESSIONID"); + getSAMLRequest(kvvJSESSIONID, success1 -> startIdentSession(success1.get("Location"), success11 -> { + String identJSESSIONID = success11.get("JSESSIONID"); + String ident_idp_authn_lc_key = success11.get("_idp_authn_lc_key"); + String identROUTEID = success11.get("ROUTEID"); + loginIdent(true, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, success111 -> loginIdent(false, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, success11112 -> { + String ident_idp_session = success11112.get("_idp_session"); + getSAMLResponse(identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, ident_idp_session, success1111 -> loginKVV(success1111.get("RelayState"), success1111.get("SAMLResponse"), kvvJSESSIONID, success111112 -> { + LoginToken token = new LoginToken(username, success111112.get("shibsessionKey"), success111112.get("shibsessionName"), kvvJSESSIONID); + finishKVVlogin(token, success11111 -> callback.onResponse(token), error); + }, error), error); + }, error), error); + }, error), error); + }, error); + } + + /* + GET https://kvv.imp.fu-berlin.de/portal/login + -> JSESSIONID 5c10406f-588c-4c16-96e9-c80d115417de.tomcat1 + */ + private void startKVVSession(final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + get("https://kvv.imp.fu-berlin.de/portal/login", null, response -> { + String cookies = response.getHeaders().get("Set-Cookie"); + if (cookies==null) { + errorCallback.onError(new NetworkError(100101, -1, "Error on starting KVV session!")); + return; + } + HashMap object; + try { + object = getCookie(cookies, new String[]{"JSESSIONID"}); + } catch (NoSuchFieldException e) { + errorCallback.onError(new NetworkError(100102, -1, "Error on starting KVV session!")); + return; + } + callback.onResponse(object); + }, error -> errorCallback.onError(new NetworkError(100100, error.networkResponse.statusCode, "Error on starting KVV session!"))); + } + + /* + GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container + <- JSESSIONID + -> (Location-Header) https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO + ?SAMLRequest=fZLLb.....Q8yre3X1IHwkJKE0Mnpy/V9TH4A + &RelayState=ss:mem:7ea01e29157b8bd906f7002176.....0d1a505f2c8bf + */ + private void getSAMLRequest(String JSESSIONID, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + HashMap cookies = new HashMap<>(); + cookies.put("JSESSIONID", JSESSIONID); + get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", cookies, response -> { + String location = response.getHeaders().get("Location"); + if (location==null) { + errorCallback.onError(new NetworkError(100111, -1, "Error on getting SAML request!")); + return; + } + HashMap object = new HashMap<>(); + object.put("Location", location); + callback.onResponse(object); + }, error -> errorCallback.onError(new NetworkError(100110, error.networkResponse.statusCode, "Error on getting SAML request!"))); + } + + /* + GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO + ?SAMLRequest=fZLLbsIwEEV/JfI+cWJAUIsgpbAoEi2IpF10UznxUKw6dupxaPn7hkdb2LD29bkzRzNGUeuGZ63fmjV8toA++K61QX58SEnrDLcCFXIjakDuK55njwvOopg3znpbWU2CDBGcV9ZMrcG2BpeD26kKnteLlGy9b5BT+rHbRapuok0bluC0MpEEmm9VWVoNfhshWnpgM7pa5gUJZt0wyogD9h+iJBiv/P6aomQTbtqSdhNtlIYzZg1SOag8zfMlCeazlLyNqpHsy1gO2V1fVsNBMuqJoUyAJaxXDUaiiyG2MDfohfEpYXEyDJM4ZKxgCe/FPI5fSbA6L36vjFTm/bal8hRC/lAUq/C02gs4PK7VBchkfHDNj8Xuwv5trPhVTiY3BeOf4DG96DmVNvypA89nK6tVtQ8yre3X1IHwkJKE0Mnpy/V9TH4A + &RelayState=ss:mem:7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf + -> JSESSIONID C4B6A428BA1F50746235D03F5D107A57 + -> _idp_authn_lc_key 57a6ae26067f374cc3d0ccfc47e27b04b47752d2a3d4eb2782af0d3994535395 + -> ROUTEID .1 + */ + private void startIdentSession(String url, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + get(url, null, response -> { + String cookies = response.getHeaders().get("Set-Cookie"); + if (cookies==null) { + errorCallback.onError(new NetworkError(100121, -1, "Error on starting Ident session!")); + return; + } + HashMap object; + try { + object = getCookie(cookies, new String[]{"JSESSIONID", "_idp_authn_lc_key", "ROUTEID"}); + } catch (NoSuchFieldException e) { + errorCallback.onError(new NetworkError(100122, -1, "Error on starting Ident session!")); + return; + } + callback.onResponse(object); + }, error -> errorCallback.onError(new NetworkError(100120, error.networkResponse.statusCode, "Error on starting Ident session!"))); + } + + /* + POST https://identity.fu-berlin.de/idp-fub/Authn/UserPassword + <- j_username seedorf96 + <- j_password neinhieristpatrick + <- (Header-"Content-Type") application/x-www-form-urlencoded + <- JSESSIONID + <- _idp_authn_lc_key + <- ROUTEID + -> _idp_session OTMuMTkzLjg1LjMz|LQ==|OGYxOWI4MjA2NTQ4YWUwYzJkOWM4Mjk4YzcwZDMwZmJiZjBmMTdmMzkyZGU2OWIwY2JkNmZlNjlmNTRmNzBlMQ==|wLlzQal7VqyntmG2vLNn06wt8wQ= + */ + private void loginIdent(final boolean first, String username, String password, String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + HashMap cookies = new HashMap<>(); + cookies.put("JSESSIONID", JSESSIONID); + cookies.put("_idp_authn_lc_key", _idp_authn_lc_key); + cookies.put("ROUTEID", ROUTEID); + HashMap body = new HashMap<>(); + body.put("j_username", username); + body.put("j_password", password); + post("https://identity.fu-berlin.de/idp-fub/Authn/UserPassword", cookies, body, response -> { + if (first) { + callback.onResponse(new HashMap<>()); + return; + } + + String cookies1 = response.getHeaders().get("Set-Cookie"); + if (cookies1 ==null) { + errorCallback.onError(new NetworkError(100131, -1, "Error on logging in to Identity Server!")); + return; + } + HashMap object; + try { + object = getCookie(cookies1, new String[]{"_idp_session"}); + } catch (NoSuchFieldException e) { + errorCallback.onError(new NetworkError(100132, -1, "Error on logging in to Identity Server!")); + return; + } + callback.onResponse(object); + }, error -> errorCallback.onError(new NetworkError(100130, error.networkResponse.statusCode, "Error on logging in to Identity Server!"))); + } + + /* + GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO + <- JSESSIONID + <- _idp_authn_lc_key + <- ROUTEID + <- _idp_session + -> (BODY) RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf + -> (BODY) SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg== + */ + private void getSAMLResponse(String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, String _idp_session, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + HashMap cookies = new HashMap<>(); + cookies.put("JSESSIONID", JSESSIONID); + cookies.put("_idp_authn_lc_key", _idp_authn_lc_key); + cookies.put("ROUTEID", ROUTEID); + cookies.put("_idp_session", _idp_session); + get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO", cookies, response -> { + String body = response.getParsed(); + if (body == null) { + errorCallback.onError(new NetworkError(100143, -1, "Error on getting SAML response!")); + return; + } + + HashMap object = new HashMap<>(); + + Pattern pattern = Pattern.compile("ss:mem:([0-9a-f]+)"); + Matcher matcher = pattern.matcher(body); + if (!matcher.find()) { + errorCallback.onError(new NetworkError(100142, -1, "Error on getting SAML response!")); + return; + } + object.put("RelayState", "ss:mem:"+matcher.group(1)); + + pattern = Pattern.compile("name=\"SAMLResponse\" value=\"([0-9a-zA-Z+]+=*)"); + matcher = pattern.matcher(body); + if (!matcher.find()) { + errorCallback.onError(new NetworkError(100141, -1, "Error on getting SAML response!")); + return; + } + object.put("SAMLResponse", matcher.group(1)); + + callback.onResponse(object); + }, error -> errorCallback.onError(new NetworkError(100140, error.networkResponse.statusCode, "Error on getting SAML response!"))); + } + + + /* + POST https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST + <- RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf + <- SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg== + <- JSESSIONID + -> _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468 + _b1912c5a03d733a80bd3fee772bf68d4 + */ + private void loginKVV(String RelayState, String SAMLResponse, String JSESSIONID, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + HashMap cookies = new HashMap<>(); + cookies.put("JSESSIONID", JSESSIONID); + HashMap body = new HashMap<>(); + body.put("RelayState", RelayState); + body.put("SAMLResponse", SAMLResponse); + post("https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST", cookies, body, response -> { + String cookies1 = response.getHeaders().get("Set-Cookie"); + if (cookies1 ==null) { + errorCallback.onError(new NetworkError(100151, -1, "Error on starting KVV session!")); + return; + } + HashMap object = new HashMap<>(); + + + Pattern pattern = Pattern.compile("(_shibsession_[0-9a-f]+)=([^;]+);"); + Matcher matcher = pattern.matcher(cookies1); + if (!matcher.find()) { + errorCallback.onError(new NetworkError(100152, -1, "Error on starting Ident session!")); + } + object.put("shibsessionKey", matcher.group(1)); + object.put("shibsessionName", matcher.group(2)); + + callback.onResponse(object); + }, error -> errorCallback.onError(new NetworkError(100150, error.networkResponse.statusCode, "Error on starting Ident session!"))); + } + + + /* + GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container + <- JSESSIONID + <- _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468 + _b1912c5a03d733a80bd3fee772bf68d4 + */ + private void finishKVVlogin(LoginToken loginToken, final NetworkCallback> callback, final NetworkErrorCallback errorCallback) { + get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", loginToken.getCookies(), response -> callback.onResponse(new HashMap<>()), error -> errorCallback.onError(new NetworkError(100160, error.networkResponse.statusCode, "Cannot finish login process!"))); + } + + + + + + + + + + + + + private String getCookie(String cookies, String name) throws NoSuchFieldException { + Pattern pattern = Pattern.compile(name+"=([^;]+);"); + Matcher matcher = pattern.matcher(cookies); + if (!matcher.find()) { + log.e("GETcookie failed", name); + log.e("GETcookie failed", cookies); + throw new NoSuchFieldException(); + } + return matcher.group(1); + } + + private HashMap getCookie(String cookies, String[] names) throws NoSuchFieldException { + HashMap result = new HashMap<>(); + for (String name: names) { + result.put(name,this.getCookie(cookies, name)); + } + return result; + } +} diff --git a/app/src/main/java/de/sebse/fuplanner/tools/CustomAccountManager.java b/app/src/main/java/de/sebse/fuplanner/tools/CustomAccountManager.java new file mode 100644 index 0000000..25d13d9 --- /dev/null +++ b/app/src/main/java/de/sebse/fuplanner/tools/CustomAccountManager.java @@ -0,0 +1,130 @@ +package de.sebse.fuplanner.tools; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.os.Build; +import android.os.Bundle; + +import java.io.IOException; + +import androidx.annotation.Nullable; +import de.sebse.fuplanner.MainActivity; +import de.sebse.fuplanner.services.KVV.Login; +import de.sebse.fuplanner.tools.logging.Logger; + +public class CustomAccountManager { + private final AccountManager mAccountManager; + private final ActivityInterface mActivityInterface; + private Logger log = new Logger(this); + + public CustomAccountManager(AccountManager manager, ActivityInterface activityInterface) { + mAccountManager = manager; + this.mActivityInterface = activityInterface; + } + + + + + + public void doInvalidateTokenSync(String accountType, String authTokenType) { + Account account = mAccountManager.getAccountsByType(accountType)[0]; + try { + String token = mAccountManager.blockingGetAuthToken(account, authTokenType, true); + mAccountManager.invalidateAuthToken(accountType, token); + } catch (AuthenticatorException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (OperationCanceledException e) { + e.printStackTrace(); + } + } + + public void doInvalidateToken(String accountType, String authTokenType, Login.BooleanInterface callback) { + Account account = mAccountManager.getAccountsByType(accountType)[0]; + mAccountManager.getAuthToken(account, authTokenType, null, true, accountManagerFuture -> { + try { + Bundle bnd = accountManagerFuture.getResult(); + String token = bnd.getString(AccountManager.KEY_AUTHTOKEN); + mAccountManager.invalidateAuthToken(accountType, token); + if (callback != null) + callback.run(true); + return; + } catch (AuthenticatorException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (OperationCanceledException e) { + e.printStackTrace(); + } + if (callback != null) + callback.run(false); + }, null); + } + + public void deleteAccount(String accountType) { + Account[] accounts = mAccountManager.getAccountsByType(accountType); + int[] count = {accounts.length}; + for (Account account: accounts) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + mAccountManager.removeAccount(account, null, null, null); + } else { + mAccountManager.removeAccount(account, null, null); + } + } + } + + public void getTokenByType(String accountType, String authTokenType, @Nullable StringInterface callback) { + //Account account = mAccountManager.getAccountsByType(accountType)[0]; + mAccountManager.getAuthTokenByFeatures(accountType, authTokenType, null, mActivityInterface.get(), null, null, accountManagerFuture -> { + try { + Bundle bnd = accountManagerFuture.getResult(); + final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN); + if (callback != null) + callback.run(authtoken); + return; + } catch (AuthenticatorException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (OperationCanceledException e) { + e.printStackTrace(); + } + if (callback != null) + callback.run(null); + }, null); + } + + public String getTokenByTypeSync(String accountType, String authTokenType) { + Account account = mAccountManager.getAccountsByType(accountType)[0]; + MainActivity activity = this.mActivityInterface.get(); + if (activity != null) { + try { + return mAccountManager.blockingGetAuthToken(account, authTokenType, true); + } catch (AuthenticatorException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (OperationCanceledException e) { + e.printStackTrace(); + } + } + return null; + } + + public boolean hasAccounts(String accountType) { + return mAccountManager.getAccountsByType(accountType).length != 0; + } + + @FunctionalInterface + public interface ActivityInterface { + @Nullable + MainActivity get(); + } + + public interface StringInterface { + void run(@Nullable String string); + } +} diff --git a/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java b/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java index 3ae5cd5..f035efc 100644 --- a/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java +++ b/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java @@ -31,7 +31,7 @@ public class HTTPService { private final EventListener errorResponseListener = new EventListener<>(); private final EventListener successResponseListener = new EventListener<>(); - protected HTTPService(Context context) { + public HTTPService(Context context) { this.mContext = context; requestQueue = Volley.newRequestQueue(context, new BetterHurlStack(false)); } diff --git a/app/src/main/res/layout/activity_fu_authenticator.xml b/app/src/main/res/layout/activity_fu_authenticator.xml new file mode 100644 index 0000000..6cae445 --- /dev/null +++ b/app/src/main/res/layout/activity_fu_authenticator.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index ab9f3ee..93ae522 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" - tools:context="de.sebse.fuplanner.fragments.LoginFragment"> + tools:context="de.sebse.fuplanner.services.newkvv.FUAuthenticatorActivity"> Update News Tips/Tricks Please click BACK again to exit! + Sign in + + Email + Password (optional) + Sign in or register + Sign in + This email address is invalid + This password is too short + This password is incorrect + This field is required + "Contacts permissions are needed for providing email + completions." + diff --git a/app/src/main/res/xml/authenticator.xml b/app/src/main/res/xml/authenticator.xml new file mode 100644 index 0000000..1d552a4 --- /dev/null +++ b/app/src/main/res/xml/authenticator.xml @@ -0,0 +1,6 @@ + +