Added Blackboard Courses to List

This commit is contained in:
Caesar2011
2019-01-28 02:01:43 +01:00
parent 72234d2439
commit c15fc1339f
18 changed files with 700 additions and 130 deletions

View File

@@ -45,7 +45,7 @@ import de.sebse.fuplanner.services.fulogin.AccountGeneral;
import de.sebse.fuplanner.services.kvv.KVV; import de.sebse.fuplanner.services.kvv.KVV;
import de.sebse.fuplanner.services.kvv.KVVListener; import de.sebse.fuplanner.services.kvv.KVVListener;
import de.sebse.fuplanner.services.kvv.sync.KVVContentProvider; import de.sebse.fuplanner.services.kvv.sync.KVVContentProvider;
import de.sebse.fuplanner.services.kvv.types.LoginToken; import de.sebse.fuplanner.services.kvv.types.LoginTokenKVV;
import de.sebse.fuplanner.services.kvv.types.Modules; import de.sebse.fuplanner.services.kvv.types.Modules;
import de.sebse.fuplanner.services.news.NewsManager; import de.sebse.fuplanner.services.news.NewsManager;
import de.sebse.fuplanner.tools.CustomAccountManager; import de.sebse.fuplanner.tools.CustomAccountManager;
@@ -658,7 +658,7 @@ public class MainActivity extends AppCompatActivity
@Override @Override
public void onLogin(LoginToken token, boolean isOnlyRefresh) { public void onLogin(LoginTokenKVV token, boolean isOnlyRefresh) {
toLoginState(token.getFullName(), token.getEmail(), getDefaultFragmentAfterLogin()); toLoginState(token.getFullName(), token.getEmail(), getDefaultFragmentAfterLogin());
if (!isOnlyRefresh) { if (!isOnlyRefresh) {
registerSync(); registerSync();

View File

@@ -4,5 +4,4 @@ public class AccountGeneral {
public static final String ACCOUNT_TYPE = "de.sebse.fuplanner.fuauth"; public static final String ACCOUNT_TYPE = "de.sebse.fuplanner.fuauth";
public static final String AUTHTOKEN_TYPE_KVV = "KVV"; public static final String AUTHTOKEN_TYPE_KVV = "KVV";
public static final String AUTHTOKEN_TYPE_BLACKBOARD = "Blackboard"; public static final String AUTHTOKEN_TYPE_BLACKBOARD = "Blackboard";
public static final long SYNC_INTERVAL = 6 * 60 * 60; // defined in seconds
} }

View File

@@ -14,9 +14,10 @@ import java.util.concurrent.atomic.AtomicReference;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import de.sebse.fuplanner.R; import de.sebse.fuplanner.R;
import de.sebse.fuplanner.services.kvv.sync.Login; import de.sebse.fuplanner.services.kvv.sync.BBLogin;
import de.sebse.fuplanner.services.kvv.types.LoginToken; import de.sebse.fuplanner.services.kvv.sync.KVVLogin;
import de.sebse.fuplanner.tools.logging.Logger; import de.sebse.fuplanner.tools.logging.Logger;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
/** /**
@@ -34,20 +35,22 @@ public class UserLoginTask extends AsyncTask<Void, Void, String> {
};*/ };*/
static final String PARAM_USER_PASS = "PARAM_USER_PASS"; static final String PARAM_USER_PASS = "PARAM_USER_PASS";
private final String mEmail; private final String mUsername;
private final String mPassword; private final String mPassword;
private final Login mVolleyLogin; private final KVVLogin mKVVLogin;
private final BBLogin mBBLogin;
private String mTokenType; private String mTokenType;
private Logger log = new Logger(this); private Logger log = new Logger(this);
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
@Nullable @Nullable
private FUAuthenticatorActivity mActivity; private FUAuthenticatorActivity mActivity;
UserLoginTask(String email, String password, String tokenType, @NotNull Context context) { UserLoginTask(String username, String password, String tokenType, @NotNull Context context) {
mEmail = email; mUsername = username;
mPassword = password; mPassword = password;
mTokenType = tokenType; mTokenType = tokenType;
mVolleyLogin = new Login(context); mKVVLogin = new KVVLogin(context);
mBBLogin = new BBLogin(context);
if (context instanceof FUAuthenticatorActivity) if (context instanceof FUAuthenticatorActivity)
mActivity = (FUAuthenticatorActivity) context; mActivity = (FUAuthenticatorActivity) context;
} }
@@ -57,30 +60,38 @@ public class UserLoginTask extends AsyncTask<Void, Void, String> {
// TODO: attempt authentication against a network service. // TODO: attempt authentication against a network service.
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
AtomicReference<LoginToken> login = new AtomicReference<>(); AtomicReference<String> login = new AtomicReference<>();
mVolleyLogin.doLogin(mEmail, mPassword, success -> { NetworkErrorCallback errorFunc = error -> {
mVolleyLogin.testLoginToken(success, success1 -> {
login.set(success);
latch.countDown();
}, error -> {
log.e(error);
latch.countDown();
});
}, error -> {
log.e(error); log.e(error);
latch.countDown(); latch.countDown();
}); };
switch (mTokenType) {
case AccountGeneral.AUTHTOKEN_TYPE_KVV:
mKVVLogin.doLogin(mUsername, mPassword, success -> {
mKVVLogin.testLoginToken(success, success1 -> {
login.set(success1.toJsonString());
latch.countDown();
}, errorFunc);
}, errorFunc);
break;
case AccountGeneral.AUTHTOKEN_TYPE_BLACKBOARD:
mBBLogin.doLogin(mUsername, mPassword, success -> {
mBBLogin.testLoginToken(success, success1 -> {
login.set(success1.toJsonString());
latch.countDown();
}, errorFunc);
}, errorFunc);
break;
default:
return null;
}
try { try {
latch.await(); latch.await();
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (login.get() == null) { return login.get();
return null;
} else {
return login.get().toJsonString();
}
} }
@Override @Override
@@ -94,7 +105,7 @@ public class UserLoginTask extends AsyncTask<Void, Void, String> {
if (success != null) { if (success != null) {
final Intent res = new Intent(); final Intent res = new Intent();
res.putExtra(AccountManager.KEY_ACCOUNT_NAME, mEmail); res.putExtra(AccountManager.KEY_ACCOUNT_NAME, mUsername);
res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountGeneral.ACCOUNT_TYPE); res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountGeneral.ACCOUNT_TYPE);
res.putExtra(AccountManager.KEY_AUTHTOKEN, success); res.putExtra(AccountManager.KEY_AUTHTOKEN, success);
res.putExtra(PARAM_USER_PASS, mPassword); res.putExtra(PARAM_USER_PASS, mPassword);

View File

@@ -2,11 +2,11 @@ package de.sebse.fuplanner.services.kvv;
import com.android.volley.NetworkResponse; import com.android.volley.NetworkResponse;
import de.sebse.fuplanner.services.kvv.types.LoginToken; import de.sebse.fuplanner.services.kvv.types.LoginTokenKVV;
import de.sebse.fuplanner.tools.CustomAccountManager; import de.sebse.fuplanner.tools.CustomAccountManager;
public interface KVVListener { public interface KVVListener {
default void onLogin(LoginToken token, boolean isOnlyRefresh) {} default void onLogin(LoginTokenKVV token, boolean isOnlyRefresh) {}
default void onLogout() {} default void onLogout() {}

View File

@@ -1,12 +1,16 @@
package de.sebse.fuplanner.services.kvv; package de.sebse.fuplanner.services.kvv;
import android.content.Context; import android.content.Context;
import android.util.Pair;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import de.sebse.fuplanner.services.fulogin.AccountGeneral; import de.sebse.fuplanner.services.fulogin.AccountGeneral;
import de.sebse.fuplanner.services.kvv.types.LoginToken; import de.sebse.fuplanner.services.kvv.sync.BBLogin;
import de.sebse.fuplanner.services.kvv.sync.KVVLogin;
import de.sebse.fuplanner.services.kvv.types.LoginTokenBB;
import de.sebse.fuplanner.services.kvv.types.LoginTokenKVV;
import de.sebse.fuplanner.tools.CustomAccountManager; import de.sebse.fuplanner.tools.CustomAccountManager;
import de.sebse.fuplanner.tools.NetworkCallbackCollector; import de.sebse.fuplanner.tools.NetworkCallbackCollector;
import de.sebse.fuplanner.tools.network.HTTPService; import de.sebse.fuplanner.tools.network.HTTPService;
@@ -16,9 +20,10 @@ import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public class Login extends HTTPService { public class Login extends HTTPService {
private final KVVListener mListener; private final KVVListener mListener;
@Nullable private LoginToken mToken; @Nullable private LoginTokenKVV mTokenKVV;
@Nullable private LoginTokenBB mTokenBB;
private boolean mLoginPending = false; private boolean mLoginPending = false;
private final NetworkCallbackCollector<LoginToken> mRefreshCallbacks = new NetworkCallbackCollector<>(); private final NetworkCallbackCollector<Pair<LoginTokenKVV, LoginTokenBB>> mRefreshCallbacks = new NetworkCallbackCollector<>();
Login(KVVListener listener, Context context) { Login(KVVListener listener, Context context) {
super(context); super(context);
@@ -31,27 +36,33 @@ public class Login extends HTTPService {
return; return;
} }
mLoginPending = true; mLoginPending = true;
LoginToken.load(mListener.getAccountManager(), token -> { LoginTokenKVV.load(mListener.getAccountManager(), tokenKVV -> {
boolean result = setToken(token); LoginTokenBB.load(mListener.getAccountManager(), tokenBB -> {
mLoginPending = false; boolean result = setToken(tokenKVV, tokenBB);
callback.run(result); mLoginPending = false;
callback.run(result);
});
}); });
} }
public void isOfflineStoredAvailable(BooleanInterface callback) { public void isOfflineStoredAvailable(BooleanInterface callback) {
LoginToken.load(mListener.getAccountManager(), token -> { LoginTokenKVV.load(mListener.getAccountManager(), tokenKVV -> {
callback.run(token != null); LoginTokenBB.load(mListener.getAccountManager(), tokenBB -> {
callback.run(tokenKVV != null && tokenBB != null);
});
}); });
} }
public boolean logout(boolean delete) { public boolean logout(boolean delete) {
if (mLoginPending) if (mLoginPending)
return false; return false;
if (mToken == null) if (mTokenKVV == null || mTokenBB == null)
return true; return true;
if (delete) if (delete) {
mToken.delete(mListener.getAccountManager()); mTokenKVV.delete(mListener.getAccountManager());
mToken = null; mTokenBB.delete(mListener.getAccountManager());
}
mTokenKVV = null;
return handleCallbacks(false); return handleCallbacks(false);
} }
@@ -60,43 +71,57 @@ public class Login extends HTTPService {
} }
public boolean isLoggedIn() { public boolean isLoggedIn() {
return mToken != null; return mTokenKVV != null && mTokenBB != null;
} }
public boolean isInOnlineMode() { public boolean isInOnlineMode() {
return isLoggedIn(); return isLoggedIn();
} }
void testLoginToken(@NotNull NetworkCallback<LoginToken> callback, @NotNull NetworkErrorCallback errorCallback) { void testLoginToken(@NotNull NetworkCallback<Pair<LoginTokenKVV, LoginTokenBB>> callback, @NotNull NetworkErrorCallback errorCallback) {
if (mToken == null) { if (mTokenKVV == null) {
errorCallback.onError(new NetworkError(100173, -1, "Not logged in!")); errorCallback.onError(new NetworkError(100173, -1, "Not logged in!"));
return; return;
} }
testLoginToken(mToken, callback, errorCallback); if (mTokenBB == null) {
errorCallback.onError(new NetworkError(100174, -1, "Not logged in!"));
return;
}
testLoginToken(mTokenKVV, mTokenBB, callback, errorCallback);
} }
private void testLoginToken(@NotNull LoginToken token, @NotNull NetworkCallback<LoginToken> callback, @NotNull NetworkErrorCallback errorCallback) { private void testLoginToken(@NotNull LoginTokenKVV tokenKVV, @NotNull LoginTokenBB tokenBB, @NotNull NetworkCallback<Pair<LoginTokenKVV, LoginTokenBB>> callback, @NotNull NetworkErrorCallback errorCallback) {
new de.sebse.fuplanner.services.kvv.sync.Login(getContext()).testLoginToken(token, callback, errorCallback); new KVVLogin(getContext()).testLoginToken(tokenKVV, tokenKVV1 -> {
new BBLogin(getContext()).testLoginToken(tokenBB, tokenBB1 -> {
callback.onResponse(new Pair<>(tokenKVV1, tokenBB1));
}, errorCallback);
}, errorCallback);
} }
@Nullable public LoginToken getLoginToken() { @Nullable public LoginTokenKVV getLoginTokenKVV() {
return mToken; return mTokenKVV;
} }
void refreshLogin(NetworkCallback<LoginToken> success, NetworkErrorCallback error) { @Nullable public LoginTokenBB getLoginTokenBB() {
return mTokenBB;
}
void refreshLogin(NetworkCallback<Pair<LoginTokenKVV, LoginTokenBB>> success, NetworkErrorCallback error) {
boolean isFirst = mRefreshCallbacks.isEmpty(); boolean isFirst = mRefreshCallbacks.isEmpty();
mRefreshCallbacks.add(success, error); mRefreshCallbacks.add(success, error);
if (!isFirst) if (!isFirst)
return; return;
CustomAccountManager manager = mListener.getAccountManager(); CustomAccountManager manager = mListener.getAccountManager();
manager.doInvalidateToken(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, ignored -> { manager.doInvalidateToken(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, ignored -> {
restoreOnlineLogin(isRestored -> { manager.doInvalidateToken(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_BLACKBOARD, ignored2 -> {
if (isRestored) restoreOnlineLogin(isRestored -> {
testLoginToken(mRefreshCallbacks::responseResponse, mRefreshCallbacks::responseError); if (isRestored)
else { testLoginToken(mRefreshCallbacks::responseResponse, mRefreshCallbacks::responseError);
logout(true); else {
mRefreshCallbacks.responseError(new NetworkError(100180, 403, "Re-login failed!")); logout(true);
} mRefreshCallbacks.responseError(new NetworkError(100180, 403, "Re-login failed!"));
}
});
}); });
}); });
} }
@@ -104,8 +129,8 @@ public class Login extends HTTPService {
private boolean handleCallbacks(boolean isOnlyRefresh) { private boolean handleCallbacks(boolean isOnlyRefresh) {
if (mToken != null) { if (mTokenKVV != null) {
mListener.onLogin(mToken, isOnlyRefresh); mListener.onLogin(mTokenKVV, isOnlyRefresh);
return true; return true;
} else { } else {
mListener.onLogout(); mListener.onLogout();
@@ -113,11 +138,12 @@ public class Login extends HTTPService {
} }
} }
private boolean setToken(@Nullable LoginToken token) { private boolean setToken(@Nullable LoginTokenKVV tokenKVV, @Nullable LoginTokenBB tokenBB) {
if (token == null) if (tokenKVV == null || tokenBB == null)
return false; return false;
boolean isOnlyRefresh = mToken != null; boolean isOnlyRefresh = mTokenKVV != null && tokenBB != null;
mToken = token; mTokenKVV = tokenKVV;
mTokenBB = tokenBB;
return isOnlyRefresh || handleCallbacks(isOnlyRefresh); return isOnlyRefresh || handleCallbacks(isOnlyRefresh);
} }

View File

@@ -33,12 +33,12 @@ public class ModulesAnnouncements extends PartModules<ArrayList<Announcement>> {
} }
@Override @Override
protected void upgrade(final String ID, final NetworkCallback<ArrayList<Announcement>> callback, final NetworkErrorCallback errorCallback) { protected void upgradeKVV(final String ID, final NetworkCallback<ArrayList<Announcement>> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginToken() == null) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) {
errorCallback.onError(new NetworkError(101204, 500, "Currently running in offline mode!")); errorCallback.onError(new NetworkError(101204, 500, "Currently running in offline mode!"));
return; return;
} }
super.get(String.format("https://kvv.imp.fu-berlin.de/direct/announcement/site/%s.json?n=999999&d=999999999", ID), mLogin.getLoginToken().getCookies(), response -> { super.get(String.format("https://kvv.imp.fu-berlin.de/direct/announcement/site/%s.json?n=999999&d=999999999", ID), mLogin.getLoginTokenKVV().getCookies(), response -> {
String body = response.getParsed(); String body = response.getParsed();
if (body == null) { if (body == null) {
errorCallback.onError(new NetworkError(101201, 403, "No announcements retrieved!")); errorCallback.onError(new NetworkError(101201, 403, "No announcements retrieved!"));
@@ -88,4 +88,9 @@ public class ModulesAnnouncements extends PartModules<ArrayList<Announcement>> {
callback.onResponse(announcements); callback.onResponse(announcements);
}, error -> errorCallback.onError(new NetworkError(101203, error.networkResponse.statusCode, "Cannot get announcements!"))); }, error -> errorCallback.onError(new NetworkError(101203, error.networkResponse.statusCode, "Cannot get announcements!")));
} }
@Override
protected void upgradeBB(String ID, NetworkCallback<ArrayList<Announcement>> callback, NetworkErrorCallback errorCallback) {
callback.onResponse(new ArrayList<>());
}
} }

View File

@@ -34,12 +34,12 @@ public class ModulesAssignments extends PartModules<AssignmentList> {
} }
@Override @Override
protected void upgrade(final String ID, final NetworkCallback<AssignmentList> callback, final NetworkErrorCallback errorCallback) { protected void upgradeKVV(final String ID, final NetworkCallback<AssignmentList> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginToken() == null) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) {
errorCallback.onError(new NetworkError(101304, 500, "Currently running in offline mode!")); errorCallback.onError(new NetworkError(101304, 500, "Currently running in offline mode!"));
return; return;
} }
get(String.format("https://kvv.imp.fu-berlin.de/direct/assignment/site/%s.json", ID), mLogin.getLoginToken().getCookies(), response -> { get(String.format("https://kvv.imp.fu-berlin.de/direct/assignment/site/%s.json", ID), mLogin.getLoginTokenKVV().getCookies(), response -> {
String body = response.getParsed(); String body = response.getParsed();
if (body == null) { if (body == null) {
errorCallback.onError(new NetworkError(101301, 403, "No assignments retrieved!")); errorCallback.onError(new NetworkError(101301, 403, "No assignments retrieved!"));
@@ -87,4 +87,9 @@ public class ModulesAssignments extends PartModules<AssignmentList> {
callback.onResponse(assignments); callback.onResponse(assignments);
}, error -> errorCallback.onError(new NetworkError(101303, error.networkResponse.statusCode, "Cannot get assignments!"))); }, error -> errorCallback.onError(new NetworkError(101303, error.networkResponse.statusCode, "Cannot get assignments!")));
} }
@Override
protected void upgradeBB(String ID, NetworkCallback<AssignmentList> callback, NetworkErrorCallback errorCallback) {
callback.onResponse(new AssignmentList());
}
} }

View File

@@ -32,12 +32,12 @@ public class ModulesEvents extends PartModules<EventList> {
} }
@Override @Override
protected void upgrade(final String ID, final NetworkCallback<EventList> callback, final NetworkErrorCallback errorCallback) { protected void upgradeKVV(final String ID, final NetworkCallback<EventList> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginToken() == null) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) {
errorCallback.onError(new NetworkError(101404, 500, "Currently running in offline mode!")); errorCallback.onError(new NetworkError(101404, 500, "Currently running in offline mode!"));
return; return;
} }
get(String.format("https://kvv.imp.fu-berlin.de/direct/calendar/site/%s.json?detailed=true", ID), mLogin.getLoginToken().getCookies(), response -> { get(String.format("https://kvv.imp.fu-berlin.de/direct/calendar/site/%s.json?detailed=true", ID), mLogin.getLoginTokenKVV().getCookies(), response -> {
String body = response.getParsed(); String body = response.getParsed();
if (body == null) { if (body == null) {
errorCallback.onError(new NetworkError(101401, 403, "No events retrieved!")); errorCallback.onError(new NetworkError(101401, 403, "No events retrieved!"));
@@ -80,4 +80,9 @@ public class ModulesEvents extends PartModules<EventList> {
callback.onResponse(events); callback.onResponse(events);
}, error -> errorCallback.onError(new NetworkError(101403, error.networkResponse.statusCode, "Cannot get events!"))); }, error -> errorCallback.onError(new NetworkError(101403, error.networkResponse.statusCode, "Cannot get events!")));
} }
@Override
protected void upgradeBB(String ID, NetworkCallback<EventList> callback, NetworkErrorCallback errorCallback) {
callback.onResponse(new EventList());
}
} }

View File

@@ -33,12 +33,12 @@ public class ModulesGradebook extends PartModules<ArrayList<Grade>> {
} }
@Override @Override
protected void upgrade(final String ID, final NetworkCallback<ArrayList<Grade>> callback, final NetworkErrorCallback errorCallback) { protected void upgradeKVV(final String ID, final NetworkCallback<ArrayList<Grade>> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginToken() == null) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) {
errorCallback.onError(new NetworkError(101504, 500, "Currently running in offline mode!")); errorCallback.onError(new NetworkError(101504, 500, "Currently running in offline mode!"));
return; return;
} }
super.get(String.format("https://kvv.imp.fu-berlin.de/direct/gradebook/site/%s.json", ID), mLogin.getLoginToken().getCookies(), response -> { super.get(String.format("https://kvv.imp.fu-berlin.de/direct/gradebook/site/%s.json", ID), mLogin.getLoginTokenKVV().getCookies(), response -> {
String body = response.getParsed(); String body = response.getParsed();
if (body == null) { if (body == null) {
errorCallback.onError(new NetworkError(101501, 403, "No gradebook retrieved!")); errorCallback.onError(new NetworkError(101501, 403, "No gradebook retrieved!"));
@@ -73,4 +73,9 @@ public class ModulesGradebook extends PartModules<ArrayList<Grade>> {
callback.onResponse(gradebook); callback.onResponse(gradebook);
}, error -> errorCallback.onError(new NetworkError(101503, error.networkResponse.statusCode, "Cannot get gradebook!"))); }, error -> errorCallback.onError(new NetworkError(101503, error.networkResponse.statusCode, "Cannot get gradebook!")));
} }
@Override
protected void upgradeBB(String ID, NetworkCallback<ArrayList<Grade>> callback, NetworkErrorCallback errorCallback) {
callback.onResponse(new ArrayList<>());
}
} }

View File

@@ -9,8 +9,10 @@ import org.json.JSONObject;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.concurrent.CountDownLatch;
import java.util.regex.MatchResult; import java.util.regex.MatchResult;
import de.sebse.fuplanner.services.kvv.types.Lecturer; import de.sebse.fuplanner.services.kvv.types.Lecturer;
@@ -61,7 +63,10 @@ public class ModulesList extends HTTPService {
} }
private void find(String moduleID, NetworkCallback<Modules.Module> moduleNetworkCallback, NetworkErrorCallback errorCallback, int retries) { private void find(String moduleID, NetworkCallback<Modules.Module> moduleNetworkCallback, NetworkErrorCallback errorCallback, int retries) {
if (mModules != null && mLogin.getLoginToken() != null && mLogin.getLoginToken().isOtherUser(mModules.getUsername())) if (mModules != null &&
mLogin.getLoginTokenKVV() != null && mLogin.getLoginTokenKVV().isOtherUser(mModules.getUsername()) &&
mLogin.getLoginTokenBB() != null && mLogin.getLoginTokenBB().isOtherUser(mModules.getUsername())
)
delete(); delete();
if (retries < 0) { if (retries < 0) {
errorCallback.onError(new NetworkError(101107, -1, "Too many retries!")); errorCallback.onError(new NetworkError(101107, -1, "Too many retries!"));
@@ -114,7 +119,10 @@ public class ModulesList extends HTTPService {
} }
private void recv(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh, final int retries) { private void recv(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh, final int retries) {
if (mModules != null && mLogin.getLoginToken() != null && mLogin.getLoginToken().isOtherUser(mModules.getUsername())) if (mModules != null &&
mLogin.getLoginTokenKVV() != null && mLogin.getLoginTokenKVV().isOtherUser(mModules.getUsername()) &&
mLogin.getLoginTokenBB() != null && mLogin.getLoginTokenBB().isOtherUser(mModules.getUsername())
)
delete(); delete();
mQueue.add(() -> { mQueue.add(() -> {
if (this.mModules != null && !forceRefresh) { if (this.mModules != null && !forceRefresh) {
@@ -122,19 +130,10 @@ public class ModulesList extends HTTPService {
mQueue.next(); mQueue.next();
return; return;
} }
this.upgrade(success -> { NetworkErrorCallback errorFunc = (error -> {
if (this.mModules == null)
this.mModules = success;
else if(this.mModules.updateList(success)) {
mListener.onModuleListChange();
store();
}
callback.onResponse(this.mModules);
mQueue.next();
}, error -> {
if (retries > 0 && (error.getHttpStatus() == 401 || error.getHttpStatus() == 403)) { if (retries > 0 && (error.getHttpStatus() == 401 || error.getHttpStatus() == 403)) {
mLogin.refreshLogin(success -> { mLogin.refreshLogin(success -> {
recv(callback, errorCallback, forceRefresh, retries-1); recv(callback, errorCallback, forceRefresh, retries - 1);
mQueue.next(); mQueue.next();
}, error1 -> { }, error1 -> {
errorCallback.onError(error1); errorCallback.onError(error1);
@@ -145,28 +144,40 @@ public class ModulesList extends HTTPService {
errorCallback.onError(error); errorCallback.onError(error);
mQueue.next(); mQueue.next();
}); });
this.upgradeKVV(successKVV -> {
this.upgradeBlackboard(successKVV, success -> {
if (this.mModules == null)
this.mModules = success;
else if (this.mModules.updateList(success)) {
mListener.onModuleListChange();
store();
}
callback.onResponse(this.mModules);
mQueue.next();
}, errorFunc);
}, errorFunc);
}); });
} }
private void upgrade(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback) { private void upgradeKVV(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginToken() == null) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) {
errorCallback.onError(new NetworkError(101105, 500, "Currently running in offline mode!")); errorCallback.onError(new NetworkError(101110, 500, "Currently running in offline mode!"));
return; return;
} }
get("https://kvv.imp.fu-berlin.de/direct/site.json", mLogin.getLoginToken().getCookies(), response -> { get("https://kvv.imp.fu-berlin.de/direct/site.json", mLogin.getLoginTokenKVV().getCookies(), response -> {
String body = response.getParsed(); String body = response.getParsed();
if (body == null) { if (body == null) {
errorCallback.onError(new NetworkError(101101, 403, "No module list retrieved!")); errorCallback.onError(new NetworkError(101111, 403, "No module list retrieved!"));
return; return;
} }
Modules modules = new Modules(mLogin.getLoginToken().getUsername()); Modules modules = new Modules(mLogin.getLoginTokenKVV().getUsername());
JSONArray sites; JSONArray sites;
try { try {
JSONObject json = new JSONObject(body); JSONObject json = new JSONObject(body);
sites = json.getJSONArray("site_collection"); sites = json.getJSONArray("site_collection");
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace(); e.printStackTrace();
errorCallback.onError(new NetworkError(101102, 403, "Cannot parse module list!")); errorCallback.onError(new NetworkError(101112, 403, "Cannot parse module list!"));
return; return;
} }
for (int i = 0; i < sites.length(); i++) { for (int i = 0; i < sites.length(); i++) {
@@ -192,13 +203,13 @@ public class ModulesList extends HTTPService {
String description = site.optString("description", ""); String description = site.optString("description", "");
description = String.valueOf(PartModules.fromHtml(description)); description = String.valueOf(PartModules.fromHtml(description));
String id = site.getString("id"); String id = site.getString("id");
modules.addModule(semester, lvNumbers, title, lecturers, type, description, id); modules.addModule(semester, lvNumbers, title, lecturers, type, description, id, Modules.TYPE_KVV);
} catch (JSONException e) { } catch (JSONException e) {
log.e(new NetworkError(101103, 403, "Cannot parse module list!")); log.e(new NetworkError(101113, 403, "Cannot parse module list!"));
log.e("ID:", i, "JSON:", sites); log.e("ID:", i, "JSON:", sites);
e.printStackTrace(); e.printStackTrace();
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
log.e(new NetworkError(101106, 403, "Cannot parse module list!")); log.e(new NetworkError(101114, 403, "Cannot parse module list!"));
log.e("ID:", i, "JSON:", sites); log.e("ID:", i, "JSON:", sites);
e.printStackTrace(); e.printStackTrace();
} }
@@ -208,6 +219,76 @@ public class ModulesList extends HTTPService {
mLogin.testLoginToken(token -> callback.onResponse(modules), errorCallback); mLogin.testLoginToken(token -> callback.onResponse(modules), errorCallback);
else else
callback.onResponse(modules); callback.onResponse(modules);
}, error -> errorCallback.onError(new NetworkError(101104, error.networkResponse.statusCode, "Cannot get module list!"))); }, error -> errorCallback.onError(new NetworkError(101115, error.networkResponse.statusCode, "Cannot get module list!")));
}
private void upgradeBlackboard(final Modules modulesKVV, final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenBB() == null) {
errorCallback.onError(new NetworkError(101120, 500, "Currently running in offline mode!"));
return;
}
get(String.format("https://lms.fu-berlin.de/learn/api/public/v1/users/%s/courses", mLogin.getLoginTokenBB().getId()), mLogin.getLoginTokenBB().getCookies(), response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(101121, 403, "No module list retrieved!"));
return;
}
final JSONArray[] sites = new JSONArray[1];
try {
JSONObject json = new JSONObject(body);
sites[0] = json.getJSONArray("results");
} catch (JSONException e) {
e.printStackTrace();
errorCallback.onError(new NetworkError(101122, 403, "Cannot parse module list!"));
return;
}
final int[] latch = {sites[0].length()};
for (int i = 0; i < sites[0].length(); i++) {
try {
JSONObject site = sites[0].getJSONObject(i);
String courseId = site.getString("courseId");
get(String.format("https://lms.fu-berlin.de/learn/api/v1/courses/%s", courseId), mLogin.getLoginTokenBB().getCookies(), response1 -> {
String body1 = response1.getParsed();
if (body1 == null) {
errorCallback.onError(new NetworkError(101124, 403, "No module list retrieved!"));
return;
}
try {
JSONObject json = new JSONObject(body1);
String name = json.getString("name");
String type = Regex.regex("[A-Za-z0-9]*_([A-Za-z0-9]*)_([A-Za-z0-9]*)_([0-9]*)([A-Z]*)", json.getString("courseId"), 1);
String lvNumber = Regex.regex("[A-Za-z0-9]*_([A-Za-z0-9]*)_([A-Za-z0-9]*)_([0-9]*)([A-Z]*)", json.getString("courseId"), 2);
String semYear = Regex.regex("[A-Za-z0-9]*_([A-Za-z0-9]*)_([A-Za-z0-9]*)_([0-9]*)([A-Z]*)", json.getString("courseId"), 3);
String semType = Regex.regex("[A-Za-z0-9]*_([A-Za-z0-9]*)_([A-Za-z0-9]*)_([0-9]*)([A-Z]*)", json.getString("courseId"), 4);
boolean found = false;
for (Modules.Module module: modulesKVV) {
if (module.lvNumber.contains(lvNumber)) {
found = true;
break;
}
}
if (!found)
modulesKVV.addModule(new Semester(semType.equals("W") ? Semester.SEM_WS : Semester.SEM_SS, Integer.valueOf(semYear)), new HashSet<>(), name, new LinkedHashSet<>(), type, "", courseId, Modules.TYPE_BB);
latch[0]--;
if (latch[0] == 0)
callback.onResponse(modulesKVV);
} catch (JSONException e) {
e.printStackTrace();
errorCallback.onError(new NetworkError(101125, 403, "Cannot parse module list!"));
return;
} catch (NoSuchFieldException e) {
e.printStackTrace();
errorCallback.onError(new NetworkError(101127, 403, "Cannot parse module list!"));
return;
}
}, error -> errorCallback.onError(new NetworkError(101126, error.networkResponse.statusCode, "Cannot get module list!")));
} catch (JSONException e) {
log.e(new NetworkError(101123, 403, "Cannot parse module list!"));
log.e("ID:", i, "JSON:", sites[0]);
e.printStackTrace();
}
}
}, error -> errorCallback.onError(new NetworkError(101125, error.networkResponse.statusCode, "Cannot get module list!")));
} }
} }

View File

@@ -42,12 +42,12 @@ public class ModulesResources extends PartModules<ArrayList<Resource>> {
} }
@Override @Override
protected void upgrade(final String ID, final NetworkCallback<ArrayList<Resource>> callback, final NetworkErrorCallback errorCallback) { protected void upgradeKVV(final String ID, final NetworkCallback<ArrayList<Resource>> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginToken() == null) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) {
errorCallback.onError(new NetworkError(101604, 500, "Currently running in offline mode!")); errorCallback.onError(new NetworkError(101604, 500, "Currently running in offline mode!"));
return; return;
} }
get(String.format("https://kvv.imp.fu-berlin.de/direct/content/site/%s.json", ID), mLogin.getLoginToken().getCookies(), response -> { get(String.format("https://kvv.imp.fu-berlin.de/direct/content/site/%s.json", ID), mLogin.getLoginTokenKVV().getCookies(), response -> {
String body = response.getParsed(); String body = response.getParsed();
if (body == null) { if (body == null) {
errorCallback.onError(new NetworkError(101601, 403, "No resources retrieved!")); errorCallback.onError(new NetworkError(101601, 403, "No resources retrieved!"));
@@ -124,6 +124,10 @@ public class ModulesResources extends PartModules<ArrayList<Resource>> {
}, error -> errorCallback.onError(new NetworkError(101603, error.networkResponse.statusCode, "Cannot get resources!"))); }, error -> errorCallback.onError(new NetworkError(101603, error.networkResponse.statusCode, "Cannot get resources!")));
} }
@Override
protected void upgradeBB(String ID, NetworkCallback<ArrayList<Resource>> callback, NetworkErrorCallback errorCallback) {
callback.onResponse(new ArrayList<>());
}
@@ -160,11 +164,11 @@ public class ModulesResources extends PartModules<ArrayList<Resource>> {
} }
private void fileUpgrade(String filename, String url, String modulename, final NetworkCallback<String> callback, final NetworkErrorCallback errorCallback) { private void fileUpgrade(String filename, String url, String modulename, final NetworkCallback<String> callback, final NetworkErrorCallback errorCallback) {
if (!mLogin.isInOnlineMode() || mLogin.getLoginToken() == null) { if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null) {
errorCallback.onError(new NetworkError(101604, 500, "Currently running in offline mode!")); errorCallback.onError(new NetworkError(101604, 500, "Currently running in offline mode!"));
return; return;
} }
get(url, mLogin.getLoginToken().getCookies(), response -> { get(url, mLogin.getLoginTokenKVV().getCookies(), response -> {
if (Regex.has("\\.[Uu][Rr][Ll]$", url)){ if (Regex.has("\\.[Uu][Rr][Ll]$", url)){
// Return redirected URL // Return redirected URL
String path = response.getHeaders().get("Location"); String path = response.getHeaders().get("Location");

View File

@@ -25,7 +25,7 @@ abstract class PartModules<T> extends Part<Modules.Module> {
mQueue.next(); mQueue.next();
return; return;
} }
upgrade(module.getID(), success -> { upgrade(module.getModuleType(), module.getID(), success -> {
if (setPart(module, success)) { if (setPart(module, success)) {
this.mList.store(); this.mList.store();
} }
@@ -61,5 +61,18 @@ abstract class PartModules<T> extends Part<Modules.Module> {
protected abstract boolean setPart(Modules.Module module, T part); protected abstract boolean setPart(Modules.Module module, T part);
protected abstract void upgrade(final String ID, final NetworkCallback<T> callback, final NetworkErrorCallback errorCallback); protected void upgrade(final int moduleType, final String ID, final NetworkCallback<T> callback, final NetworkErrorCallback errorCallback) {
switch (moduleType) {
case Modules.TYPE_KVV:
upgradeKVV(ID, callback, errorCallback);
break;
case Modules.TYPE_BB:
upgradeBB(ID, callback, errorCallback);
break;
}
}
protected abstract void upgradeKVV(final String ID, final NetworkCallback<T> callback, final NetworkErrorCallback errorCallback);
protected abstract void upgradeBB(final String ID, final NetworkCallback<T> callback, final NetworkErrorCallback errorCallback);
} }

View File

@@ -0,0 +1,266 @@
package de.sebse.fuplanner.services.kvv.sync;
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.LoginTokenBB;
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 BBLogin extends HTTPService {
public BBLogin(Context context) {
super(context);
}
public void testLoginToken(@NotNull LoginTokenBB token, @NotNull NetworkCallback<LoginTokenBB> callback, @NotNull NetworkErrorCallback errorCallback) {
get(String.format("https://lms.fu-berlin.de/learn/api/public/v1/users/?userName=%s", token.getUsername()), token.getCookies(), response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(100272, 403, "Testing login failed!"));
return;
}
try {
JSONObject json = new JSONObject(body);
json = json.getJSONArray("results").getJSONObject(0);
String id = json.getString("id");
String studentId = json.getString("studentId");
token.setAdditionals(id, studentId);
callback.onResponse(token);
} catch (JSONException e) {
errorCallback.onError(new NetworkError(100271, 403, "Cannot parse profile!"));
}
}, error -> errorCallback.onError(new NetworkError(100270, error.networkResponse.statusCode, "Testing login failed!")));
}
public void doLogin(String username, String password, NetworkCallback<LoginTokenBB> callback, NetworkErrorCallback error) {
step1(success1 -> {
String samlLocation = success1.get("Location");
step2(samlLocation, success2 -> {
String fuJSESSIONID = success2.get("JSESSIONID");
step3(fuJSESSIONID, success3 -> {
step4(username, password, fuJSESSIONID, success4 -> {
String samlResponse = success4.get("SAMLResponse");
step5(samlResponse, success5 -> {
String shibsessionKey = success5.get("shibsessionKey");
String shibsessionName = success5.get("shibsessionName");
step6(shibsessionKey, shibsessionName, success6 -> {
String s_session_id = success6.get("s_session_id");
String session_id = success6.get("session_id");
LoginTokenBB token = new LoginTokenBB(username, s_session_id, session_id);
callback.onResponse(token);
}, error);
}, error);
}, error);
}, error);
}, error);
}, error);
}
/*
1= GET https://lms.fu-berlin.de/lms-apps/login/sso/index.php
-> Location-Header: https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO?SAMLResponse=[SAMLResponse]&RelayState=[RelayState]
*/
private void step1(final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
get("https://lms.fu-berlin.de/lms-apps/login/sso/index.php", null, response -> {
String location = response.getHeaders().get("Location");
if (location==null) {
errorCallback.onError(new NetworkError(100211, -1, "Error on getting SAML request!"));
return;
}
HashMap<String, String> object = new HashMap<>();
object.put("Location", location);
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100210, error.networkResponse.statusCode, "Error on getting SAML request!")));
}
/*
2= GET [Location-Header 1]
-> Set-Cookie: JSESSIONID=[JSESSION-FU]
-> Location: /idp-fub/profile/SAML2/Redirect/SSO?execution=e1s1
*/
private void step2(String url, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
get(url, null, response -> {
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies==null) {
errorCallback.onError(new NetworkError(100221, -1, "Error on starting FU session!"));
return;
}
HashMap<String, String> object;
try {
object = getCookie(cookies, new String[]{"JSESSIONID"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100222, -1, "Error on starting FU session!"));
return;
}
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100220, error.networkResponse.statusCode, "Error on starting FU session!")));
}
/*
3= GET [Location-Header 2]
+ Cookie: JSESSIONID=[JSESSION-FU]
*/
private void step3(String JSESSIONID_FU, final NetworkCallback<Boolean> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", JSESSIONID_FU);
head("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO?execution=e1s1", cookies, response -> {
callback.onResponse(true);
}, error -> errorCallback.onError(new NetworkError(100230, error.networkResponse.statusCode, "Error starting login page!")));
}
/*
4= POST [Location-Header 2]
+ Body: j_username=[USERNAME]&j_password=[PASSWORD]&_eventId_proceed=
+ Header: Content-Type: application/x-www-form-urlencoded
+ Header: Referer: [Location-Header 2]
+ Cookie: JSESSIONID=[JSESSION-FU]
-> Set-Cookie: shib_idp_session=[SHIB-IDP-SESSION]
-> Body SAMLResponse-Input-value
*/
private void step4(String username, String password, String JSESSIONID_FU, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", JSESSIONID_FU);
HashMap<String, String> body = new HashMap<>();
body.put("j_username", username);
body.put("j_password", password);
body.put("_eventId_proceed", "");
post("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO?execution=e1s1", cookies, body, response -> {
String cookies1 = response.getHeaders().get("Set-Cookie");
if (cookies1 ==null) {
errorCallback.onError(new NetworkError(100241, -1, "Error on logging in to FU Identity Server!"));
return;
}
HashMap<String, String> object;
try {
object = getCookie(cookies1, new String[]{"shib_idp_session"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100242, -1, "Error on logging in to FU Identity Server!"));
return;
}
String content = response.getParsed();
if (content == null) {
errorCallback.onError(new NetworkError(100243, -1, "Error on getting SAML response!"));
return;
}
Pattern pattern = Pattern.compile("name=\"SAMLResponse\" value=\"([0-9a-zA-Z+]+=*)");
Matcher matcher = pattern.matcher(content);
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100244, -1, "Error on getting SAML response!"));
return;
}
object.put("SAMLResponse", matcher.group(1));
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100245, error.networkResponse.statusCode, "Error on logging in to FU Identity Server!")));
}
/*
5= POST https://lms.fu-berlin.de/Shibboleth.sso/SAML2/POST
+ Body: SAMLResponse=[SAML-RESPONSE]
+ Header: Content-Type: application/x-www-form-urlencoded
-> Set-Cookie: _shibsession_[SESS-NR]: [SESS-VALUE]
*/
private void step5(String SAMLResponse, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> body = new HashMap<>();
body.put("SAMLResponse", SAMLResponse);
post("https://lms.fu-berlin.de/Shibboleth.sso/SAML2/POST", null, body, response -> {
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies ==null) {
errorCallback.onError(new NetworkError(100251, -1, "Error on starting KVV session!"));
return;
}
HashMap<String, String> object = new HashMap<>();
Pattern pattern = Pattern.compile("(_shibsession_[0-9a-f]+)=([^;]+);");
Matcher matcher = pattern.matcher(cookies);
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100252, -1, "Error on starting KVV session!"));
}
object.put("shibsessionKey", matcher.group(1));
object.put("shibsessionName", matcher.group(2));
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100250, error.networkResponse.statusCode, "Error on starting KVV session!")));
}
/*
6= https://lms.fu-berlin.de/webapps/bb-auth-provider-shibboleth-bb_bb60/execute/shibbolethLogin?returnUrl=https://lms.fu-berlin.de/webapps/portal/execute/defaultTab&authProviderId=_3_1
+ Cookie: _shibsession_[SESS-NR]: [SESS-VALUE]
-> Set-Cookie: JSESSIONID: [JSESSION-KVV]
*/
private void step6(String shibsessionKey, String shibsessionName, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>();
cookies.put(shibsessionKey, shibsessionName);
log.d(shibsessionKey, shibsessionName);
get("https://lms.fu-berlin.de/webapps/bb-auth-provider-shibboleth-bb_bb60/execute/shibbolethLogin?returnUrl=https://lms.fu-berlin.de/webapps/portal/execute/defaultTab&authProviderId=_3_1", cookies, response -> {
String cookies1 = response.getHeaders().get("Set-Cookie");
if (cookies1 ==null) {
errorCallback.onError(new NetworkError(100261, -1, "Cannot finish login process!"));
return;
}
HashMap<String, String> object;
try {
object = getCookie(cookies1, new String[]{"session_id", "s_session_id"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100262, -1, "Cannot finish login process!"));
return;
}
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100260, 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<String, String> getCookie(String cookies, String[] names) throws NoSuchFieldException {
HashMap<String, String> result = new HashMap<>();
for (String name: names) {
result.put(name,this.getCookie(cookies, name));
}
return result;
}
}

View File

@@ -10,19 +10,19 @@ import java.util.HashMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import de.sebse.fuplanner.services.kvv.types.LoginToken; import de.sebse.fuplanner.services.kvv.types.LoginTokenKVV;
import de.sebse.fuplanner.tools.network.HTTPService; import de.sebse.fuplanner.tools.network.HTTPService;
import de.sebse.fuplanner.tools.network.NetworkCallback; import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError; import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback; import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public class Login extends HTTPService { public class KVVLogin extends HTTPService {
public Login(Context context) { public KVVLogin(Context context) {
super(context); super(context);
} }
public void testLoginToken(@NotNull LoginToken token, @NotNull NetworkCallback<LoginToken> callback, @NotNull NetworkErrorCallback errorCallback) { public void testLoginToken(@NotNull LoginTokenKVV token, @NotNull NetworkCallback<LoginTokenKVV> callback, @NotNull NetworkErrorCallback errorCallback) {
get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", token.getUsername()), token.getCookies(), response -> { get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", token.getUsername()), token.getCookies(), response -> {
String body = response.getParsed(); String body = response.getParsed();
if (body == null) { if (body == null) {
@@ -55,7 +55,7 @@ public class Login extends HTTPService {
public void doLogin(String username, String password, NetworkCallback<LoginToken> callback, NetworkErrorCallback error) { public void doLogin(String username, String password, NetworkCallback<LoginTokenKVV> callback, NetworkErrorCallback error) {
step1(success1 -> { step1(success1 -> {
String samlLocation = success1.get("Location"); String samlLocation = success1.get("Location");
step2(samlLocation, success2 -> { step2(samlLocation, success2 -> {
@@ -68,7 +68,7 @@ public class Login extends HTTPService {
String shibsessionName = success5.get("shibsessionName"); String shibsessionName = success5.get("shibsessionName");
step6(shibsessionKey, shibsessionName, success6 -> { step6(shibsessionKey, shibsessionName, success6 -> {
String kvvJSESSIONID = success6.get("JSESSIONID"); String kvvJSESSIONID = success6.get("JSESSIONID");
LoginToken token = new LoginToken(username, kvvJSESSIONID); LoginTokenKVV token = new LoginTokenKVV(username, kvvJSESSIONID);
callback.onResponse(token); callback.onResponse(token);
}, error); }, error);
}, error); }, error);
@@ -80,7 +80,7 @@ public class Login extends HTTPService {
/* /*
1= GET https://kvv.imp.fu-berlin.de/Shibboleth.sso/Login?entityID=https://identity.fu-berlin.de/idp-fub 1= GET https://kvv.imp.fu-berlin.de/Shibboleth.sso/Login?entityID=https://identity.fu-berlin.de/idp-fub
-> Location-Header: https://identity.fu-berlin.de:9443/idp-fub-qa/profile/SAML2/Redirect/SSO?SAMLResponse=[SAMLResponse]&RelayState=[RelayState] -> Location-Header: https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO?SAMLResponse=[SAMLResponse]&RelayState=[RelayState]
*/ */
private void step1(final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) { private void step1(final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
get("https://kvv.imp.fu-berlin.de/Shibboleth.sso/Login?entityID=https://identity.fu-berlin.de/idp-fub", null, response -> { get("https://kvv.imp.fu-berlin.de/Shibboleth.sso/Login?entityID=https://identity.fu-berlin.de/idp-fub", null, response -> {
@@ -98,7 +98,7 @@ public class Login extends HTTPService {
/* /*
2= GET [Location-Header 1] 2= GET [Location-Header 1]
-> Set-Cookie: JSESSIONID=[JSESSION-FU] -> Set-Cookie: JSESSIONID=[JSESSION-FU]
-> Location: /idp-fub-qa/profile/SAML2/Redirect/SSO?execution=e1s1 -> Location: /idp-fub/profile/SAML2/Redirect/SSO?execution=e1s1
*/ */
private void step2(String url, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) { private void step2(String url, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
get(url, null, response -> { get(url, null, response -> {
@@ -125,7 +125,7 @@ public class Login extends HTTPService {
private void step3(String JSESSIONID_FU, final NetworkCallback<Boolean> callback, final NetworkErrorCallback errorCallback) { private void step3(String JSESSIONID_FU, final NetworkCallback<Boolean> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>(); HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", JSESSIONID_FU); cookies.put("JSESSIONID", JSESSIONID_FU);
get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO?execution=e1s1", cookies, response -> { head("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO?execution=e1s1", cookies, response -> {
callback.onResponse(true); callback.onResponse(true);
}, error -> errorCallback.onError(new NetworkError(100130, error.networkResponse.statusCode, "Error starting login page!"))); }, error -> errorCallback.onError(new NetworkError(100130, error.networkResponse.statusCode, "Error starting login page!")));
} }

View File

@@ -0,0 +1,133 @@
package de.sebse.fuplanner.services.kvv.types;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.sebse.fuplanner.services.fulogin.AccountGeneral;
import de.sebse.fuplanner.tools.CustomAccountManager;
/**
* Created by sebastian on 29.10.17.
*/
public class LoginTokenBB {
private final String s_session_id;
private final String session_id;
private final String username;
@Nullable private String id;
@Nullable private String studentId;
public LoginTokenBB(String username, String s_session_id, String session_id) {
this.username = username;
this.s_session_id = s_session_id;
this.session_id = session_id;
}
public static void load(CustomAccountManager manager, LoginTokenInterface callback) {
if (!manager.hasAccounts(AccountGeneral.ACCOUNT_TYPE)) {
callback.run(null);
return;
}
manager.getTokenByType(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_BLACKBOARD, tokenString -> {
if (tokenString == null) {
callback.run(null);
return;
}
callback.run(LoginTokenBB.fromJsonString(tokenString));
});
}
public void delete(CustomAccountManager manager) {
manager.deleteAccount(AccountGeneral.ACCOUNT_TYPE);
}
public void setAdditionals(String id, String studentId) {
this.id = id;
this.studentId = studentId;
}
public String getUsername() {
return username;
}
private String getSessionId() {
return session_id;
}
public String getSSessionId() {
return s_session_id;
}
@Nullable
public String getId() {
return id;
}
@Nullable
public String getStudentId() {
return studentId;
}
public HashMap<String, String> getCookies() {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("session_id", getSessionId());
cookies.put("s_session_id", getSSessionId());
return cookies;
}
public boolean isOtherUser(String username) {
return !this.getUsername().equals(username);
}
@NonNull
@Override
public String toString() {
StringBuilder result = new StringBuilder();
HashMap<String, String> cookies = this.getCookies();
for (String header: cookies.keySet()) {
result.append(header).append("=").append(cookies.get(header)).append(";");
}
return result.substring(0, result.length()-1);
}
public String toJsonString() {
JSONObject json = new JSONObject();
try {
json.put("s_session_id", s_session_id);
json.put("session_id", session_id);
json.put("username", username);
json.put("id", id);
json.put("studentId", studentId);
} catch (JSONException e) {
return null;
}
return json.toString();
}
private static LoginTokenBB fromJsonString(String tokenString) {
try {
JSONObject json = new JSONObject(tokenString);
LoginTokenBB token = new LoginTokenBB(
json.getString("username"),
json.getString("s_session_id"),
json.getString("session_id"));
if (!json.isNull("id"))
token.setAdditionals(
json.getString("id"),
json.getString("studentId")
);
return token;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
public interface LoginTokenInterface {
void run(LoginTokenBB token);
}
}

View File

@@ -3,26 +3,24 @@ package de.sebse.fuplanner.services.kvv.types;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import de.sebse.fuplanner.services.fulogin.AccountGeneral; import de.sebse.fuplanner.services.fulogin.AccountGeneral;
import de.sebse.fuplanner.tools.CustomAccountManager; import de.sebse.fuplanner.tools.CustomAccountManager;
import de.sebse.fuplanner.tools.logging.Logger;
/** /**
* Created by sebastian on 29.10.17. * Created by sebastian on 29.10.17.
*/ */
public class LoginToken implements Serializable { public class LoginTokenKVV {
private final String username; private final String username;
private final String JSESSIONID; private final String JSESSIONID;
@Nullable private String fullName; @Nullable private String fullName;
@Nullable private String email; @Nullable private String email;
public LoginToken(String username, String JSESSIONID) { public LoginTokenKVV(String username, String JSESSIONID) {
this.username = username; this.username = username;
this.JSESSIONID = JSESSIONID; this.JSESSIONID = JSESSIONID;
} }
@@ -37,7 +35,7 @@ public class LoginToken implements Serializable {
callback.run(null); callback.run(null);
return; return;
} }
callback.run(LoginToken.fromJsonString(tokenString)); callback.run(LoginTokenKVV.fromJsonString(tokenString));
}); });
} }
@@ -103,10 +101,10 @@ public class LoginToken implements Serializable {
return json.toString(); return json.toString();
} }
private static LoginToken fromJsonString(String tokenString) { private static LoginTokenKVV fromJsonString(String tokenString) {
try { try {
JSONObject json = new JSONObject(tokenString); JSONObject json = new JSONObject(tokenString);
LoginToken token = new LoginToken( LoginTokenKVV token = new LoginTokenKVV(
json.getString("username"), json.getString("username"),
json.getString("JSESSIONID")); json.getString("JSESSIONID"));
if (!json.isNull("fullName")) if (!json.isNull("fullName"))
@@ -122,6 +120,6 @@ public class LoginToken implements Serializable {
} }
public interface LoginTokenInterface { public interface LoginTokenInterface {
void run(LoginToken token); void run(LoginTokenKVV token);
} }
} }

View File

@@ -25,6 +25,9 @@ import androidx.annotation.Nullable;
*/ */
public class Modules implements Iterable<Modules.Module>, Serializable { public class Modules implements Iterable<Modules.Module>, Serializable {
public static final int TYPE_KVV = 1;
public static final int TYPE_BB = 2;
private SortedListModule list; private SortedListModule list;
private final String mUsername; private final String mUsername;
private transient long mLastTimestamp = 0; private transient long mLastTimestamp = 0;
@@ -37,8 +40,8 @@ public class Modules implements Iterable<Modules.Module>, Serializable {
this.list = new SortedListModule(); this.list = new SortedListModule();
} }
public void addModule(@Nullable Semester semester, HashSet<String> lvNumber, String title, LinkedHashSet<Lecturer> lecturer, String type, String description, String ID) { public void addModule(@Nullable Semester semester, HashSet<String> lvNumber, String title, LinkedHashSet<Lecturer> lecturer, String type, String description, String ID, int moduleType) {
Module m = new Module(semester, lvNumber, title, lecturer, type, description, ID); Module m = new Module(semester, lvNumber, title, lecturer, type, description, ID, moduleType);
this.list.add(m); this.list.add(m);
} }
@@ -154,14 +157,16 @@ public class Modules implements Iterable<Modules.Module>, Serializable {
} }
public class Module implements Serializable { public class Module implements Serializable {
@Nullable public final Semester semester; @Nullable public final Semester semester;
@NotNull final HashSet<String> lvNumber; @NotNull public final HashSet<String> lvNumber;
@NotNull public final String title; @NotNull public final String title;
@NotNull @NotNull
public final ArrayList<Lecturer> lecturer; public final ArrayList<Lecturer> lecturer;
@Nullable public final String type; @Nullable public final String type;
@Nullable public final String description; @Nullable public final String description;
@NotNull private final String ID; @NotNull private final String ID;
@NotNull private final int moduleType;
@Nullable public ArrayList<Announcement> announcements; @Nullable public ArrayList<Announcement> announcements;
@Nullable public AssignmentList assignments; @Nullable public AssignmentList assignments;
@Nullable public EventList events; @Nullable public EventList events;
@@ -182,7 +187,7 @@ public class Modules implements Iterable<Modules.Module>, Serializable {
return userPoint/maxPoint; return userPoint/maxPoint;
} }
private Module(@Nullable Semester semester, @NotNull HashSet<String> lvNumber, @NotNull String title, @NotNull LinkedHashSet<Lecturer> lecturer, @Nullable String type, @Nullable String description, @NotNull String ID) { private Module(@Nullable Semester semester, @NotNull HashSet<String> lvNumber, @NotNull String title, @NotNull LinkedHashSet<Lecturer> lecturer, @Nullable String type, @Nullable String description, @NotNull String ID, @NotNull int moduleType) {
title = title.replaceAll("(.*?) (S[0-9]{2}|W[0-9/]{5})", "$1"); title = title.replaceAll("(.*?) (S[0-9]{2}|W[0-9/]{5})", "$1");
@@ -193,6 +198,7 @@ public class Modules implements Iterable<Modules.Module>, Serializable {
this.type = type; this.type = type;
this.description = description; this.description = description;
this.ID = ID; this.ID = ID;
this.moduleType = moduleType;
} }
@NonNull @NonNull
@@ -213,7 +219,11 @@ public class Modules implements Iterable<Modules.Module>, Serializable {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(semester, lvNumber, title, lecturer, type, description, ID); return Objects.hashCode(semester, lvNumber, title, lecturer, type, description, ID, moduleType);
}
public int getModuleType() {
return moduleType;
} }
} }
} }

View File

@@ -52,8 +52,17 @@ public class HTTPService {
successResponseListener.remove(id); successResponseListener.remove(id);
} }
protected void head(String url, @Nullable final HashMap<String, String> cookies, Response.Listener<Result> response, Response.ErrorListener error) {
get(url, cookies, response, error, true);
}
protected void get(String url, @Nullable final HashMap<String, String> cookies, Response.Listener<Result> response, Response.ErrorListener error) { protected void get(String url, @Nullable final HashMap<String, String> cookies, Response.Listener<Result> response, Response.ErrorListener error) {
HttpRequest request = new HttpRequest(Request.Method.GET, url, response, error) { get(url, cookies, response, error, false);
}
protected void get(String url, @Nullable final HashMap<String, String> cookies, Response.Listener<Result> response, Response.ErrorListener error, boolean noBody) {
int requestMethod = noBody ? Request.Method.HEAD : Request.Method.GET;
HttpRequest request = new HttpRequest(requestMethod, url, response, error) {
@Override @Override
public void deliverError(VolleyError error) { public void deliverError(VolleyError error) {
if (error == null) { if (error == null) {