Add support for token-based authentication.

This commit is contained in:
Sergio Martínez Portela 2020-10-20 11:23:50 +02:00
parent 13fa966557
commit fc4c9139c5
6 changed files with 78 additions and 7 deletions

View File

@ -10,6 +10,7 @@ public class ConfigManager {
private static final String PREFERENCES_NAME = "MINICARDS_PREFERENCES";
private static final String TOKEN_KEY = "PROGRAMAKER_API_TOKEN";
private static final String BRIDGE_ID_KEY = "PROGRAMAKER_BRIDGE_ID";
private static final String BRIDGE_AUTHENTICATION_TOKEN_KEY = "BRIDGE_AUTHENTICATION_TOKEN";
private static final String BRIDGE_CONNECTION_ID_KEY = "PROGRAMAKER_BRIDGE_CONNECTION_ID";
public ConfigManager(Context ctx) {
@ -63,4 +64,17 @@ public class ConfigManager {
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
return preferences.getString(BRIDGE_CONNECTION_ID_KEY, null);
}
public void setBridgeAuthenticationToken(String bridgeAuthToken) {
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
SharedPreferences.Editor edit = preferences.edit();
edit.putString(BRIDGE_AUTHENTICATION_TOKEN_KEY, bridgeAuthToken);
edit.commit();
}
public String getBridgeAuthenticationToken() {
SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE);
return preferences.getString(BRIDGE_AUTHENTICATION_TOKEN_KEY, null);
}
}

View File

@ -15,8 +15,8 @@ public class ProgramakerAndroidBridge {
private StatusStreamer statusStreamer = null;
// Static
public static ProgramakerAndroidBridge configure(Context ctx, String userId, String bridgeId) {
return new ProgramakerAndroidBridge(ctx, userId, bridgeId);
public static ProgramakerAndroidBridge configure(Context ctx, String userId, String bridgeId, String bridgeToken) {
return new ProgramakerAndroidBridge(ctx, userId, bridgeId, bridgeToken);
}
public static String GetBridgeName(Context ctx) {
@ -29,11 +29,13 @@ public class ProgramakerAndroidBridge {
private final Context ctx;
private final String userId;
private final String bridgeId;
private final String bridgeToken;
private ProgramakerAndroidBridge(Context ctx, String userId, String bridgeId) {
private ProgramakerAndroidBridge(Context ctx, String userId, String bridgeId, String bridgeToken) {
this.ctx = ctx;
this.userId = userId;
this.bridgeId = bridgeId;
this.bridgeToken = bridgeToken;
}
public void start(Runnable onReady, Runnable onComplete) {
@ -43,7 +45,7 @@ public class ProgramakerAndroidBridge {
DefaultAndroidBlocks.GetBuilder(this.ctx).Build()
);
this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration, onReady, onComplete);
this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, this.bridgeToken, configuration, onReady, onComplete);
this.bridgeRunner.run();
this.statusStreamer = new StatusStreamer(this.ctx, this.bridgeRunner);

View File

@ -30,6 +30,7 @@ enum ServiceState {
public class ProgramakerBridgeService extends Service {
public static final String BridgeUserNotificationChannel = "PROGRAMAKER_BRIDGE_USER_NOTIFICATION";
public static final CharSequence BridgeUserNotificationChannelName = "User notifications";
private static final String AUTOGENERATED_TOKEN_NAME = "Auto-generated";
public static String BridgeUserVibrationNotificationChannel = "PROGRAMAKER_BRIDGE_USER_VIBRATION_NOTIFICATIONS";
public static CharSequence BridgeUserVibrationNotificationChannelName = "User-triggered vibration";
@ -160,12 +161,21 @@ public class ProgramakerBridgeService extends Service {
bridgeIdCheck = api.createBridge(ProgramakerAndroidBridge.GetBridgeName(this));
config.setBridgeId(bridgeIdCheck);
}
String bridgeAuthCheck = config.getBridgeAuthenticationToken();
if (bridgeAuthCheck == null) {
bridgeAuthCheck = api.createBridgeAuthenticationToken(bridgeIdCheck, ProgramakerBridgeService.AUTOGENERATED_TOKEN_NAME);
config.setBridgeAuthenticationToken(bridgeAuthCheck);
}
final String bridgeId = bridgeIdCheck;
final String bridgeAuth = bridgeAuthCheck;
ProgramakerBridgeService.this.bridge = ProgramakerAndroidBridge.configure(
this,
userId,
bridgeId);
bridgeId,
bridgeAuth);
ProgramakerBridgeService.this.bridge.start(
() -> { // On ready
setBridgeStatusNotification(getString(R.string.bridge_service_online), ServiceState.RUNNING, null);

View File

@ -211,6 +211,29 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
return result.getBridgeId()
}
fun createBridgeAuthenticationToken(bridgeId: String, tokenName: String): String {
val conn = URL(getCreateBridgeAuthenticationTokenUrl(bridgeId)).openConnection() as HttpURLConnection
conn.setRequestProperty("Content-Type", "application/json")
addAuthHeader(conn)
conn.requestMethod = "POST";
conn.doOutput = true;
val postData = JSONObject(hashMapOf(
"name" to tokenName
) as Map<*, *>)
val wr = DataOutputStream(conn.outputStream)
wr.writeBytes(postData.toString());
wr.flush();
wr.close();
val result: ProgramakerFullBridgeInfo
result = parseJson(conn.inputStream, ProgramakerFullBridgeInfo::class.java)
return result.key
}
fun establishConnection(bridgeId: String): Boolean {
// NOTE: This establishes a connection to a bridge as long as it can be done without interaction
@ -292,9 +315,14 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api")
return "$ApiRoot/v0/users/$userName/bridges"
}
private fun getCreateBridgeAuthenticationTokenUrl(bridgeId: String): String {
this.withUserId()
return "$ApiRoot/v0/bridges/by-id/$bridgeId/tokens"
}
private fun getPrepareConnectionUrl(bridgeId: String): String {
this.withUserName()
return "$ApiRoot/v0/users/$userName/services/id/$bridgeId/how-to-enable"
this.withUserId()
return "$ApiRoot/v0/services/by-id/$bridgeId/how-to-enable"
}
private fun getEstablishConnectionUrl(bridgeId: String): String {

View File

@ -0,0 +1,6 @@
package com.programaker.api.data.api_results
class ProgramakerFullBridgeInfo (
val key: String
) {
}

View File

@ -14,6 +14,7 @@ import java.util.concurrent.TimeUnit
class ProgramakerBridge(
private val bridge_id: String,
private val user_id: String,
private val bridge_token: String,
private val config: ProgramakerBridgeConfiguration,
private val onReady: Runnable,
private val onComplete: Runnable
@ -49,6 +50,16 @@ class ProgramakerBridge(
// Websocket management
override fun onOpen(webSocket: WebSocket, response: Response) {
this.webSocket = webSocket
val auth = JSONObject(hashMapOf(
"type" to "AUTHENTICATION",
"value" to hashMapOf (
"token" to this.bridge_token
)
) as Map<*, *>).toString()
webSocket.send(auth)
webSocket.send(config.serialize())
onReady.run()
}