Skip to content

Commit

Permalink
[Tesla] Add SSO handler to authenticate against Tesla SSO service (op…
Browse files Browse the repository at this point in the history
…enhab#10259)

Signed-off-by: Christian Güdel <cg@dmesg.ch>
  • Loading branch information
cguedel authored Mar 1, 2021
1 parent d3cfd8d commit d880373
Show file tree
Hide file tree
Showing 14 changed files with 504 additions and 315 deletions.
9 changes: 9 additions & 0 deletions bundles/org.openhab.binding.tesla/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@

<name>openHAB Add-ons :: Bundles :: Tesla Binding</name>

<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,21 @@ public class TeslaBindingConstants {
public static final String PATH_DATA_REQUEST = "data_request/{cmd}";
public static final String PATH_VEHICLE_ID = "/{vid}/";
public static final String PATH_WAKE_UP = "wake_up";
public static final String URI_ACCESS_TOKEN = "oauth/token";
public static final String PATH_ACCESS_TOKEN = "oauth/token";
public static final String URI_EVENT = "https://streaming.vn.teslamotors.com/stream/";
public static final String URI_OWNERS = "https://owner-api.teslamotors.com/";
public static final String URI_OWNERS = "https://owner-api.teslamotors.com";
public static final String VALETPIN = "valetpin";
public static final String VEHICLES = "vehicles";
public static final String VIN = "vin";

// SSO URI constants
public static final String SSO_SCOPES = "openid email offline_access";
public static final String URI_SSO = "https://auth.tesla.com/oauth2/v3";
public static final String PATH_AUTHORIZE = "authorize";
public static final String PATH_TOKEN = "token";
public static final String URI_CALLBACK = "https://auth.tesla.com/void/callback";
public static final String CLIENT_ID = "ownerapi";

// Tesla REST API commands
public static final String COMMAND_ACTUATE_TRUNK = "actuate_trunk";
public static final String COMMAND_AUTO_COND_START = "auto_conditioning_start";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tesla.internal.handler.TeslaAccountHandler;
import org.openhab.binding.tesla.internal.handler.TeslaVehicleHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
Expand Down Expand Up @@ -52,12 +53,14 @@ public class TeslaHandlerFactory extends BaseThingHandlerFactory {
THING_TYPE_MODEL3, THING_TYPE_MODELX, THING_TYPE_MODELY);

private final ClientBuilder clientBuilder;
private final HttpClientFactory httpClientFactory;

@Activate
public TeslaHandlerFactory(@Reference ClientBuilder clientBuilder) {
public TeslaHandlerFactory(@Reference ClientBuilder clientBuilder, @Reference HttpClientFactory httpClientFactory) {
this.clientBuilder = clientBuilder //
.connectTimeout(EVENT_STREAM_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(EVENT_STREAM_READ_TIMEOUT, TimeUnit.SECONDS);
this.httpClientFactory = httpClientFactory;
}

@Override
Expand All @@ -70,7 +73,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (thingTypeUID.equals(THING_TYPE_ACCOUNT)) {
return new TeslaAccountHandler((Bridge) thing, clientBuilder.build());
return new TeslaAccountHandler((Bridge) thing, clientBuilder.build(), httpClientFactory);
} else {
return new TeslaVehicleHandler(thing, clientBuilder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,30 @@
*/
package org.openhab.binding.tesla.internal.command;

import static org.openhab.binding.tesla.internal.TeslaBindingConstants.*;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tesla.internal.TeslaBindingConstants;
import org.openhab.binding.tesla.internal.discovery.TeslaAccountDiscoveryService;
import org.openhab.binding.tesla.internal.protocol.TokenRequest;
import org.openhab.binding.tesla.internal.protocol.TokenRequestPassword;
import org.openhab.binding.tesla.internal.protocol.TokenResponse;
import org.openhab.binding.tesla.internal.handler.TeslaSSOHandler;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.io.console.Console;
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.util.UIDUtils;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

/**
* Console commands for interacting with the Tesla integration
Expand All @@ -61,19 +49,18 @@ public class TeslaCommandExtension extends AbstractConsoleCommandExtension {

private static final String CMD_LOGIN = "login";

private final Logger logger = LoggerFactory.getLogger(TeslaCommandExtension.class);

@Reference(cardinality = ReferenceCardinality.OPTIONAL)
private @Nullable ClientBuilder injectedClientBuilder;

private @Nullable WebTarget tokenTarget;

private final TeslaAccountDiscoveryService teslaAccountDiscoveryService;
private final HttpClientFactory httpClientFactory;

@Activate
public TeslaCommandExtension(@Reference TeslaAccountDiscoveryService teslaAccountDiscoveryService) {
public TeslaCommandExtension(@Reference TeslaAccountDiscoveryService teslaAccountDiscoveryService,
@Reference HttpClientFactory httpClientFactory) {
super("tesla", "Interact with the Tesla integration.");
this.teslaAccountDiscoveryService = teslaAccountDiscoveryService;
this.httpClientFactory = httpClientFactory;
}

@Override
Expand Down Expand Up @@ -120,59 +107,20 @@ public List<String> getUsages() {
}

private void login(Console console, String username, String password) {
try {
Gson gson = new Gson();

TokenRequest token = new TokenRequestPassword(username, password);
String payLoad = gson.toJson(token);

Response response = getTokenTarget().request()
.post(Entity.entity(payLoad, MediaType.APPLICATION_JSON_TYPE));

if (response != null) {
if (response.getStatus() == 200 && response.hasEntity()) {
String responsePayLoad = response.readEntity(String.class);
TokenResponse tokenResponse = gson.fromJson(responsePayLoad.trim(), TokenResponse.class);
console.println("Refresh token: " + tokenResponse.refresh_token);

ThingUID thingUID = new ThingUID(TeslaBindingConstants.THING_TYPE_ACCOUNT,
UIDUtils.encode(username));
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel("Tesla Account")
.withProperty(TeslaBindingConstants.CONFIG_REFRESHTOKEN, tokenResponse.refresh_token)
.withProperty(TeslaBindingConstants.CONFIG_USERNAME, username)
.withRepresentationProperty(TeslaBindingConstants.CONFIG_USERNAME).build();
teslaAccountDiscoveryService.thingDiscovered(result);
} else {
console.println(
"Failure: " + response.getStatus() + " " + response.getStatusInfo().getReasonPhrase());
}
}
} catch (Exception e) {
console.println("Failed to retrieve token: " + e.getMessage());
logger.error("Could not get refresh token.", e);
}
}

private synchronized WebTarget getTokenTarget() {
WebTarget target = this.tokenTarget;
if (target != null) {
return target;
TeslaSSOHandler ssoHandler = new TeslaSSOHandler(httpClientFactory.getCommonHttpClient());

String refreshToken = ssoHandler.authenticate(username, password);
if (refreshToken != null) {
console.println("Refresh token: " + refreshToken);

ThingUID thingUID = new ThingUID(TeslaBindingConstants.THING_TYPE_ACCOUNT, UIDUtils.encode(username));
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel("Tesla Account")
.withProperty(TeslaBindingConstants.CONFIG_REFRESHTOKEN, refreshToken)
.withProperty(TeslaBindingConstants.CONFIG_USERNAME, username)
.withRepresentationProperty(TeslaBindingConstants.CONFIG_USERNAME).build();
teslaAccountDiscoveryService.thingDiscovered(result);
} else {
Client client;
try {
client = ClientBuilder.newBuilder().build();
} catch (Exception e) {
// we seem to have no Jersey, so let's hope for an injected builder by CXF
if (this.injectedClientBuilder != null) {
client = injectedClientBuilder.build();
} else {
throw new IllegalStateException("No JAX RS Client Builder available.");
}
}
WebTarget teslaTarget = client.target(URI_OWNERS);
target = teslaTarget.path(URI_ACCESS_TOKEN);
this.tokenTarget = target;
return target;
console.println("Failed to retrieve refresh token");
}
}
}
Loading

0 comments on commit d880373

Please sign in to comment.