Skip to content

Commit

Permalink
chore: Don't use JWT when Authentication is disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
wba2hi committed Feb 27, 2024
1 parent 9ee0962 commit c8daff1
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@

package org.eclipse.kuksa.testapp.databroker;

import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.util.Log;

import androidx.annotation.NonNull;

Expand All @@ -39,115 +36,67 @@
import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse;
import org.eclipse.kuksa.proto.v1.Types;
import org.eclipse.kuksa.proto.v1.Types.Datapoint;
import org.eclipse.kuksa.testapp.databroker.model.Certificate;
import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo;
import org.eclipse.kuksa.testapp.extension.ConnectionInfoExtensionKt;
import org.eclipse.kuksa.vsscore.model.VssSpecification;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import io.grpc.ChannelCredentials;
import io.grpc.Grpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.TlsChannelCredentials;
import kotlin.io.TextStreamsKt;
import kotlin.text.Charsets;

public class JavaDataBrokerEngine implements DataBrokerEngine {
private static final String TAG = JavaDataBrokerEngine.class.getSimpleName();
private static final long TIMEOUT_CONNECTION = 5;

@Nullable
private DataBrokerConnection dataBrokerConnection = null;

private final Set<DisconnectListener> disconnectListeners = new HashSet<>();

// Too many to usefully handle: Checked Exceptions: IOE, RuntimeExceptions: UOE, ISE, IAE, ...
@SuppressWarnings("TooGenericExceptionCaught")
public void connect(
@NonNull Context context,
@NonNull ConnectionInfo connectionInfo,
@NonNull CoroutineCallback<DataBrokerConnection> callback
) {
if (connectionInfo.isTlsEnabled()) {
connectSecure(context, connectionInfo, callback);
} else {
connectInsecure(context, connectionInfo, callback);
try {
if (connectionInfo.isTlsEnabled()) {
connectSecure(context, connectionInfo, callback);
} else {
connectInsecure(context, connectionInfo, callback);
}
} catch (Exception e) {
callback.onError(e);
}
}

private void connectInsecure(
@NonNull Context context,
@NonNull ConnectionInfo connectionInfo,
@NonNull CoroutineCallback<DataBrokerConnection> callback
) {
try {
ManagedChannel managedChannel = ManagedChannelBuilder
.forAddress(connectionInfo.getHost(), connectionInfo.getPort())
.usePlaintext()
.build();

String jwtUriPath = connectionInfo.getJwtUriPath();
JsonWebToken jsonWebToken = null;
if (jwtUriPath != null) {
jsonWebToken = loadJsonWebToken(context, jwtUriPath);
}
) throws IOException {
ManagedChannel insecureChannel = ConnectionInfoExtensionKt.createInsecureManagedChannel(connectionInfo);
JsonWebToken jsonWebToken = ConnectionInfoExtensionKt.loadJsonWebToken(connectionInfo, context);

connect(managedChannel, jsonWebToken, callback);
} catch (IllegalArgumentException e) {
callback.onError(e);
}
connect(insecureChannel, jsonWebToken, callback);
}

private void connectSecure(
@NotNull Context context,
@NotNull ConnectionInfo connectionInfo,
@NotNull CoroutineCallback<DataBrokerConnection> callback
) {
Certificate certificate = connectionInfo.getCertificate();

ChannelCredentials tlsCredentials;
try {
InputStream rootCertFile = context.getContentResolver().openInputStream(certificate.getUri());
if (rootCertFile == null) return;

tlsCredentials = TlsChannelCredentials.newBuilder()
.trustManager(rootCertFile)
.build();
} catch (IOException e) {
Log.w(TAG, "Could not find file for certificate: " + certificate);

return;
}

try {
ManagedChannelBuilder<?> channelBuilder = Grpc
.newChannelBuilderForAddress(connectionInfo.getHost(), connectionInfo.getPort(), tlsCredentials);

String overrideAuthority = certificate.getOverrideAuthority().trim();
boolean hasOverrideAuthority = !overrideAuthority.isEmpty();
if (hasOverrideAuthority) {
channelBuilder.overrideAuthority(overrideAuthority);
}

String jwtUriPath = connectionInfo.getJwtUriPath();
JsonWebToken jsonWebToken = null;
if (jwtUriPath != null) {
jsonWebToken = loadJsonWebToken(context, jwtUriPath);
}
) throws IOException {
ManagedChannel secureChannel = ConnectionInfoExtensionKt.createSecureManagedChannel(connectionInfo, context);
JsonWebToken jsonWebToken = ConnectionInfoExtensionKt.loadJsonWebToken(connectionInfo, context);

ManagedChannel managedChannel = channelBuilder.build();
connect(managedChannel, jsonWebToken, callback);
} catch (IllegalArgumentException e) {
callback.onError(e);
}
connect(secureChannel, jsonWebToken, callback);
}

private void connect(
Expand Down Expand Up @@ -298,18 +247,4 @@ public void unsubscribe(@NonNull Property property, @NonNull PropertyListener pr
dataBrokerConnection.unsubscribe(property, propertyListener);
}
}

public JsonWebToken loadJsonWebToken(Context context, String uriPath) {
Uri uri = Uri.parse(uriPath);

ContentResolver contentResolver = context.getContentResolver();
try (InputStream inputStream = contentResolver.openInputStream(uri)) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charsets.UTF_8);
String token = TextStreamsKt.readText(inputStreamReader);

return new JsonWebToken(token);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,8 @@
package org.eclipse.kuksa.testapp.databroker

import android.content.Context
import android.net.Uri
import androidx.lifecycle.LifecycleCoroutineScope
import io.grpc.ChannelCredentials
import io.grpc.Grpc
import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import io.grpc.TlsChannelCredentials
import kotlinx.coroutines.launch
import org.eclipse.kuksa.CoroutineCallback
import org.eclipse.kuksa.DataBrokerConnection
Expand All @@ -42,8 +37,10 @@ import org.eclipse.kuksa.proto.v1.KuksaValV1.GetResponse
import org.eclipse.kuksa.proto.v1.KuksaValV1.SetResponse
import org.eclipse.kuksa.proto.v1.Types.Datapoint
import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo
import org.eclipse.kuksa.testapp.extension.createInsecureManagedChannel
import org.eclipse.kuksa.testapp.extension.createSecureManagedChannel
import org.eclipse.kuksa.testapp.extension.loadJsonWebToken
import org.eclipse.kuksa.vsscore.model.VssSpecification
import java.io.IOException

@Suppress("complexity:TooManyFunctions")
class KotlinDataBrokerEngine(
Expand All @@ -53,15 +50,21 @@ class KotlinDataBrokerEngine(

private val disconnectListeners = mutableSetOf<DisconnectListener>()

// Too many to usefully handle: Checked Exceptions: IOE, RuntimeExceptions: UOE, ISE, IAE, ...
@Suppress("TooGenericExceptionCaught")
override fun connect(
context: Context,
connectionInfo: ConnectionInfo,
callback: CoroutineCallback<DataBrokerConnection>,
) {
if (connectionInfo.isTlsEnabled) {
connectSecure(context, connectionInfo, callback)
} else {
connectInsecure(context, connectionInfo, callback)
try {
if (connectionInfo.isTlsEnabled) {
connectSecure(context, connectionInfo, callback)
} else {
connectInsecure(context, connectionInfo, callback)
}
} catch (e: Exception) {
callback.onError(e)
}
}

Expand All @@ -70,61 +73,21 @@ class KotlinDataBrokerEngine(
connectionInfo: ConnectionInfo,
callback: CoroutineCallback<DataBrokerConnection>,
) {
try {
val managedChannel = ManagedChannelBuilder
.forAddress(connectionInfo.host, connectionInfo.port)
.usePlaintext()
.build()

val jsonWebToken = connectionInfo.jwtUriPath?.let {
loadJsonWebToken(context, it)
}
val insecureManagedChannel = connectionInfo.createInsecureManagedChannel()
val jsonWebToken: JsonWebToken? = connectionInfo.loadJsonWebToken(context)

connect(managedChannel, jsonWebToken, callback)
} catch (e: IllegalArgumentException) {
callback.onError(e)
}
connect(insecureManagedChannel, jsonWebToken, callback)
}

private fun connectSecure(
context: Context,
connectionInfo: ConnectionInfo,
callback: CoroutineCallback<DataBrokerConnection>,
) {
val certificate = connectionInfo.certificate

val tlsCredentials: ChannelCredentials
try {
val rootCertFile = context.contentResolver.openInputStream(certificate.uri)
tlsCredentials = TlsChannelCredentials.newBuilder()
.trustManager(rootCertFile)
.build()
} catch (e: IOException) {
callback.onError(e)
return
}
val secureManagedChannel = connectionInfo.createSecureManagedChannel(context)
val jsonWebToken: JsonWebToken? = connectionInfo.loadJsonWebToken(context)

try {
val host = connectionInfo.host.trim()
val port = connectionInfo.port
val channelBuilder = Grpc
.newChannelBuilderForAddress(host, port, tlsCredentials)

val overrideAuthority = certificate.overrideAuthority.trim()
val hasOverrideAuthority = overrideAuthority.isNotEmpty()
if (hasOverrideAuthority) {
channelBuilder.overrideAuthority(overrideAuthority)
}

val jsonWebToken = connectionInfo.jwtUriPath?.let {
loadJsonWebToken(context, it)
}

val managedChannel = channelBuilder.build()
connect(managedChannel, jsonWebToken, callback)
} catch (e: IllegalArgumentException) {
callback.onError(e)
}
connect(secureManagedChannel, jsonWebToken, callback)
}

private fun connect(
Expand Down Expand Up @@ -222,17 +185,6 @@ class KotlinDataBrokerEngine(
dataBrokerConnection?.disconnectListeners?.unregister(listener)
}

private fun loadJsonWebToken(context: Context, uriPath: String): JsonWebToken {
val uri = Uri.parse(uriPath)

val contentResolver = context.contentResolver
val token: String = contentResolver.openInputStream(uri)?.use {
it.reader().readText()
} ?: error("Could not read jwt")

return JsonWebToken(token)
}

companion object {
const val TIMEOUT_CONNECTION_SEC = 5L
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.testapp.extension

import android.content.Context
import android.net.Uri
import io.grpc.ChannelCredentials
import io.grpc.Grpc
import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import io.grpc.TlsChannelCredentials
import org.eclipse.kuksa.authentication.JsonWebToken
import org.eclipse.kuksa.testapp.databroker.model.ConnectionInfo
import java.io.IOException

fun ConnectionInfo.createInsecureManagedChannel(): ManagedChannel {
val host = host.trim()

return ManagedChannelBuilder
.forAddress(host, port)
.usePlaintext()
.build()
}

@Throws(IOException::class)
fun ConnectionInfo.createSecureManagedChannel(context: Context): ManagedChannel {
val rootCertFile = context.contentResolver.openInputStream(certificate.uri)

val tlsCredentials: ChannelCredentials = TlsChannelCredentials.newBuilder()
.trustManager(rootCertFile)
.build()

val host = host.trim()
val port = port
val channelBuilder = Grpc
.newChannelBuilderForAddress(host, port, tlsCredentials)

val overrideAuthority = certificate.overrideAuthority.trim()
val hasOverrideAuthority = overrideAuthority.isNotEmpty()
if (hasOverrideAuthority) {
channelBuilder.overrideAuthority(overrideAuthority)
}

return channelBuilder.build()
}

@Throws(IOException::class)
fun ConnectionInfo.loadJsonWebToken(context: Context): JsonWebToken? {
if (!isAuthenticationEnabled || jwtUriPath == null) {
return null
}

val uri: Uri = Uri.parse(jwtUriPath)
val token = uri.readAsText(context)

return JsonWebToken(token)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package org.eclipse.kuksa.testapp.extension
import android.content.Context
import android.net.Uri
import android.provider.OpenableColumns
import java.io.FileNotFoundException

fun Uri.fetchFileName(context: Context): String? {
var fileName: String? = null
Expand All @@ -35,3 +36,11 @@ fun Uri.fetchFileName(context: Context): String? {
}
return fileName
}

@Throws(FileNotFoundException::class)
fun Uri.readAsText(context: Context): String {
val contentResolver = context.contentResolver
return contentResolver.openInputStream(this)?.use {
it.reader().readText()
} ?: error("Could not read file from uri")
}
Loading

0 comments on commit c8daff1

Please sign in to comment.