Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb75d04e authored by Fahim Salam Chowdhury's avatar Fahim Salam Chowdhury 👽
Browse files

feat: pass OIDC-LOGIN-WITH-TOKEN header if required

for nextcloud app api request with OIDC specific header is required, but
this header should not be passed for dav/ocs requests.

issue: e/backlog#6287
parent fb65112b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -224,7 +224,7 @@ dependencies {
    implementation "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2
    implementation 'org.apache.jackrabbit:jackrabbit-webdav:2.13.5' // remove after entire switch to lib v2
    implementation 'com.google.code.gson:gson:2.10.1'
    implementation("foundation.e:Nextcloud-Android-Library:1.0.7-u2.17-release") {
    implementation("foundation.e:Nextcloud-Android-Library:1.0.8-u2.17-release") {
        exclude group: 'com.gitlab.bitfireAT', module: 'dav4jvm'
        exclude group: 'org.ogce', module: 'xpp3' // unused in Android and brings wrong Junit version
        exclude group: 'com.squareup.okhttp3'
+41 −15
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.nextcloud.android.sso.aidl.IInputStreamService;
@@ -46,6 +47,7 @@ import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManager;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.operations.RemoteOperation;

import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpMethodBase;
@@ -328,7 +330,7 @@ public class InputStreamBinder extends IInputStreamService.Stub {
        }

        // Validate URL
        if (request.getUrl().length() == 0 || request.getUrl().charAt(0) != PATH_SEPARATOR) {
        if (request.getUrl().isEmpty() || request.getUrl().charAt(0) != PATH_SEPARATOR) {
            throw new IllegalStateException(EXCEPTION_INVALID_REQUEST_URL,
                    new IllegalStateException("URL need to start with a /"));
        }
@@ -345,17 +347,22 @@ public class InputStreamBinder extends IInputStreamService.Stub {
        } else {
            method.setQueryString(convertMapToNVP(request.getParameter()));
        }
        method.addRequestHeader("OCS-APIREQUEST", "true");

        for (Map.Entry<String, List<String>> header : request.getHeader().entrySet()) {
            // https://stackoverflow.com/a/3097052
            method.addRequestHeader(header.getKey(), TextUtils.join(",", header.getValue()));

            if ("OCS-APIREQUEST".equalsIgnoreCase(header.getKey())) {
                throw new IllegalStateException(
                        "The 'OCS-APIREQUEST' header will be automatically added by the Nextcloud SSO Library. " +
                                "Please remove the header before making a request");
        }

        method.setRequestHeader(
                RemoteOperation.OCS_API_HEADER,
                RemoteOperation.OCS_API_HEADER_VALUE
        );

        if (shouldAddHeaderForOidcLogin(context, account, request.getUrl())) {
            method.setRequestHeader(
                    RemoteOperation.OIDC_LOGIN_WITH_TOKEN,
                    RemoteOperation.OIDC_LOGIN_WITH_TOKEN_VALUE
            );
        }

        client.setFollowRedirects(true);
@@ -383,6 +390,21 @@ public class InputStreamBinder extends IInputStreamService.Stub {
        }
    }

    /*
    * for non ocs/dav requests (nextcloud app: ex: notes app), when OIDC is used, we need to pass an special header.
    * We should not pass this header for ocs/dav requests as it can cause session cookie not being used for those request.
    *
    * These nextcloud app request paths contain `/index.php/apps/` on them.
     */
    private boolean shouldAddHeaderForOidcLogin(@NonNull Context context, @NonNull Account account, @NonNull String path) {
        boolean isOidcAccount = AccountManagerUtils.isOidcAccount(context, account);
        if (!isOidcAccount) {
            return false;
        }

        return path.contains("/index.php/apps/");
    }

    private static String getUserAgent() {
        return "AccountManager-SSO(" + BuildConfig.VERSION_NAME + ")";
    }
@@ -402,7 +424,7 @@ public class InputStreamBinder extends IInputStreamService.Stub {
        }

        // Validate URL
        if (request.getUrl().length() == 0 || request.getUrl().charAt(0) != PATH_SEPARATOR) {
        if (request.getUrl().isEmpty() || request.getUrl().charAt(0) != PATH_SEPARATOR) {
            throw new IllegalStateException(EXCEPTION_INVALID_REQUEST_URL,
                    new IllegalStateException("URL need to start with a /"));
        }
@@ -420,17 +442,21 @@ public class InputStreamBinder extends IInputStreamService.Stub {
            method.setQueryString(convertMapToNVP(request.getParameter()));
        }

        method.addRequestHeader("OCS-APIREQUEST", "true");

        for (Map.Entry<String, List<String>> header : request.getHeader().entrySet()) {
            // https://stackoverflow.com/a/3097052
            method.addRequestHeader(header.getKey(), TextUtils.join(",", header.getValue()));

            if ("OCS-APIREQUEST".equalsIgnoreCase(header.getKey())) {
                throw new IllegalStateException(
                        "The 'OCS-APIREQUEST' header will be automatically added by the Nextcloud SSO Library. " +
                                "Please remove the header before making a request");
        }

        method.setRequestHeader(
                RemoteOperation.OCS_API_HEADER,
                RemoteOperation.OCS_API_HEADER_VALUE
        );

        if (shouldAddHeaderForOidcLogin(context, account, request.getUrl())) {
            method.setRequestHeader(
                    RemoteOperation.OIDC_LOGIN_WITH_TOKEN,
                    RemoteOperation.OIDC_LOGIN_WITH_TOKEN_VALUE
            );
        }

        client.setFollowRedirects(true);
+8 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import androidx.annotation.Nullable;

import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.settings.AccountSettings;

public final class AccountManagerUtils {

@@ -43,6 +44,13 @@ public final class AccountManagerUtils {
        return null;
    }

    @Nullable
    public static boolean isOidcAccount(@NonNull Context context, @NonNull Account account) {
        AccountManager accountManager = AccountManager.get(context);
        String authState = accountManager.getUserData(account, AccountSettings.KEY_AUTH_STATE);
        return authState != null && !authState.trim().isEmpty();
    }

    @NonNull
    public static Account[] getAccounts(@NonNull Context context) {
        AccountManager accountManager = AccountManager.get(context);