mirror of
https://github.com/nextcloud/android.git
synced 2024-11-26 23:28:42 +03:00
Merge pull request #4725 from nextcloud/ssoHeaders
SSO: return response headers
This commit is contained in:
commit
cabe2fcff0
5 changed files with 245 additions and 2 deletions
|
@ -1 +1 @@
|
|||
420
|
||||
423
|
||||
|
|
|
@ -24,4 +24,9 @@ interface IInputStreamService {
|
|||
in ParcelFileDescriptor requestBodyParcelFileDescriptor);
|
||||
|
||||
ParcelFileDescriptor performNextcloudRequest(in ParcelFileDescriptor input);
|
||||
|
||||
ParcelFileDescriptor performNextcloudRequestAndBodyStreamV2(in ParcelFileDescriptor input,
|
||||
in ParcelFileDescriptor requestBodyParcelFileDescriptor);
|
||||
|
||||
ParcelFileDescriptor performNextcloudRequestV2(in ParcelFileDescriptor input);
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ public class InputStreamBinder extends IInputStreamService.Stub {
|
|||
private static final int HTTP_STATUS_CODE_MULTIPLE_CHOICES = 300;
|
||||
|
||||
private static final char PATH_SEPARATOR = '/';
|
||||
private static final int ZERO_LENGTH = 0;
|
||||
private Context context;
|
||||
private UserAccountManager accountManager;
|
||||
|
||||
|
@ -112,6 +113,42 @@ public class InputStreamBinder extends IInputStreamService.Stub {
|
|||
return nvp;
|
||||
}
|
||||
|
||||
public ParcelFileDescriptor performNextcloudRequestV2(ParcelFileDescriptor input) {
|
||||
return performNextcloudRequestAndBodyStreamV2(input, null);
|
||||
}
|
||||
|
||||
public ParcelFileDescriptor performNextcloudRequestAndBodyStreamV2(
|
||||
ParcelFileDescriptor input,
|
||||
ParcelFileDescriptor requestBodyParcelFileDescriptor) {
|
||||
// read the input
|
||||
final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
|
||||
|
||||
final InputStream requestBodyInputStream = requestBodyParcelFileDescriptor != null ?
|
||||
new ParcelFileDescriptor.AutoCloseInputStream(requestBodyParcelFileDescriptor) : null;
|
||||
Exception exception = null;
|
||||
Response response = new Response();
|
||||
|
||||
try {
|
||||
// Start request and catch exceptions
|
||||
NextcloudRequest request = deserializeObjectAndCloseStream(is);
|
||||
response = processRequestV2(request, requestBodyInputStream);
|
||||
} catch (Exception e) {
|
||||
Log_OC.e(TAG, "Error during Nextcloud request", e);
|
||||
exception = e;
|
||||
}
|
||||
|
||||
try {
|
||||
// Write exception to the stream followed by the actual network stream
|
||||
InputStream exceptionStream = serializeObjectToInputStreamV2(exception, response.getPlainHeadersString());
|
||||
InputStream resultStream = new java.io.SequenceInputStream(exceptionStream, response.getBody());
|
||||
|
||||
return ParcelFileDescriptorUtil.pipeFrom(resultStream, thread -> Log.d(TAG, "Done sending result"));
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Error while sending response back to client app", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ParcelFileDescriptor performNextcloudRequest(ParcelFileDescriptor input) {
|
||||
return performNextcloudRequestAndBodyStream(input, null);
|
||||
}
|
||||
|
@ -128,7 +165,7 @@ public class InputStreamBinder extends IInputStreamService.Stub {
|
|||
InputStream httpStream = new InputStream() {
|
||||
@Override
|
||||
public int read() {
|
||||
return 0;
|
||||
return ZERO_LENGTH;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -157,6 +194,23 @@ public class InputStreamBinder extends IInputStreamService.Stub {
|
|||
return null;
|
||||
}
|
||||
|
||||
private ByteArrayInputStream serializeObjectToInputStreamV2(Exception exception, String headers) {
|
||||
byte[] baosByteArray = new byte[0];
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
|
||||
oos.writeObject(exception);
|
||||
oos.writeObject(headers);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
|
||||
baosByteArray = baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Error while sending response back to client app", e);
|
||||
}
|
||||
|
||||
return new ByteArrayInputStream(baosByteArray);
|
||||
}
|
||||
|
||||
private <T extends Serializable> ByteArrayInputStream serializeObjectToInputStream(T obj) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
|
@ -315,6 +369,71 @@ public class InputStreamBinder extends IInputStreamService.Stub {
|
|||
}
|
||||
}
|
||||
|
||||
private Response processRequestV2(final NextcloudRequest request, final InputStream requestBodyInputStream)
|
||||
throws UnsupportedOperationException,
|
||||
com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException,
|
||||
OperationCanceledException, AuthenticatorException, IOException {
|
||||
Account account = accountManager.getAccountByName(request.getAccountName());
|
||||
if (account == null) {
|
||||
throw new IllegalStateException(EXCEPTION_ACCOUNT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Validate token
|
||||
if (!isValid(request)) {
|
||||
throw new IllegalStateException(EXCEPTION_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
// Validate URL
|
||||
if (request.getUrl().length() == 0 || request.getUrl().charAt(0) != PATH_SEPARATOR) {
|
||||
throw new IllegalStateException(EXCEPTION_INVALID_REQUEST_URL,
|
||||
new IllegalStateException("URL need to start with a /"));
|
||||
}
|
||||
|
||||
OwnCloudClientManager ownCloudClientManager = OwnCloudClientManagerFactory.getDefaultSingleton();
|
||||
OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
|
||||
OwnCloudClient client = ownCloudClientManager.getClientFor(ocAccount, context);
|
||||
|
||||
HttpMethodBase method = buildMethod(request, client.getBaseUri(), requestBodyInputStream);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
client.setFollowRedirects(request.isFollowRedirects());
|
||||
int status = client.executeMethod(method);
|
||||
|
||||
// Check if status code is 2xx --> https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Success
|
||||
if (status >= HTTP_STATUS_CODE_OK && status < HTTP_STATUS_CODE_MULTIPLE_CHOICES) {
|
||||
return new Response(method.getResponseBodyAsStream(), method.getResponseHeaders());
|
||||
} else {
|
||||
StringBuilder total = new StringBuilder();
|
||||
InputStream inputStream = method.getResponseBodyAsStream();
|
||||
// If response body is available
|
||||
if (inputStream != null) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
total.append(line).append('\n');
|
||||
line = reader.readLine();
|
||||
}
|
||||
Log_OC.e(TAG, total.toString());
|
||||
}
|
||||
throw new IllegalStateException(EXCEPTION_HTTP_REQUEST_FAILED,
|
||||
new IllegalStateException(String.valueOf(status),
|
||||
new IllegalStateException(total.toString())));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValid(NextcloudRequest request) {
|
||||
String callingPackageName = context.getPackageManager().getNameForUid(Binder.getCallingUid());
|
||||
|
||||
|
|
54
src/main/java/com/nextcloud/android/sso/PlainHeader.java
Normal file
54
src/main/java/com/nextcloud/android/sso/PlainHeader.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2019 Tobias Kaminsky
|
||||
* Copyright (C) 2019 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.android.sso;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class PlainHeader implements Serializable {
|
||||
private static final long serialVersionUID = 3284979177401282512L;
|
||||
|
||||
@Getter
|
||||
private String name;
|
||||
@Getter
|
||||
private String value;
|
||||
|
||||
PlainHeader(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream oos) throws IOException {
|
||||
oos.writeObject(name);
|
||||
oos.writeObject(value);
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
name = (String) in.readObject();
|
||||
value = (String) in.readObject();
|
||||
}
|
||||
}
|
65
src/main/java/com/nextcloud/android/sso/Response.java
Normal file
65
src/main/java/com/nextcloud/android/sso/Response.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2019 Tobias Kaminsky
|
||||
* Copyright (C) 2019 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.android.sso;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.apache.commons.httpclient.Header;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class Response {
|
||||
@Getter
|
||||
private InputStream body;
|
||||
private Header[] headers;
|
||||
|
||||
public Response() {
|
||||
headers = new Header[0];
|
||||
body = new InputStream() {
|
||||
@Override
|
||||
public int read() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Response(InputStream inputStream, Header[] headers) {
|
||||
this.body = inputStream;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public String getPlainHeadersString() {
|
||||
List<PlainHeader> arrayList = new ArrayList<>(headers.length);
|
||||
|
||||
for (Header header : headers) {
|
||||
arrayList.add(new PlainHeader(header.getName(), header.getValue()));
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(arrayList);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue