Start refactoring AuthService to handle direct Enedis service call and cleaner code

Signed-off-by: Laurent ARNAL <laurent@clae.net>
This commit is contained in:
Laurent ARNAL 2024-04-17 15:38:29 +02:00
parent cc827a0166
commit 73d36c30a7
7 changed files with 168 additions and 212 deletions

View File

@ -35,7 +35,7 @@ public interface LinkyAccountHandler {
* @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens * @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens
* @return returns the name of the Smartthings user that is authorized * @return returns the name of the Smartthings user that is authorized
*/ */
String authorize(String redirectUrl, String reqCode) throws LinkyException; String authorize(String redirectUrl, String reqState, String reqCode) throws LinkyException;
/** /**
* Formats the Url to use to call Smartthings to authorize the application. * Formats the Url to use to call Smartthings to authorize the application.

View File

@ -17,18 +17,22 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Map; import java.util.List;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext; import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService; import org.osgi.service.http.HttpService;
@ -41,28 +45,38 @@ import org.slf4j.LoggerFactory;
* @author Gaël L'hopital - Initial contribution * @author Gaël L'hopital - Initial contribution
* @author Laurent Arnal - Rewrite addon to use official dataconect API * @author Laurent Arnal - Rewrite addon to use official dataconect API
*/ */
@Component(service = LinkyAuthService.class, configurationPid = "binding.internal.authService")
@NonNullByDefault @NonNullByDefault
public class LinkyAuthService { public class LinkyAuthService implements LinkyAccountHandler {
private static final String TEMPLATE_PATH = "templates/"; private static final String TEMPLATE_PATH = "templates/";
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html"; private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
private static final String ERROR_UKNOWN_BRIDGE = "Returned 'state' by doesn't match any Bridges. Has the bridge been removed?";
private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class); private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class);
private @NonNullByDefault({}) HttpService httpService; private @NonNullByDefault({}) HttpService httpService;
private @NonNullByDefault({}) BundleContext bundleContext; private @NonNullByDefault({}) BundleContext bundleContext;
private @Nullable LinkyAccountHandler accountHandler;
@Activate private final boolean oAuthSupport = true;
protected void activate(ComponentContext componentContext, Map<String, Object> properties) { private @Nullable OAuthClientService oAuthService;
public LinkyAuthService(final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
final @Reference ComponentContext componentContext) {
this.httpService = httpService;
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
LinkyBindingConstants.ENEDIS_API_TOKEN_URL, LinkyBindingConstants.ENEDIS_AUTH_AUTHORIZE_URL,
LinkyBindingConstants.clientId, LinkyBindingConstants.clientSecret, LinkyBindingConstants.LINKY_SCOPES,
true);
try { try {
bundleContext = componentContext.getBundleContext(); bundleContext = componentContext.getBundleContext();
httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, createServlet(), new Hashtable<>(), httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, createServlet(), new Hashtable<>(),
httpService.createDefaultHttpContext()); httpService.createDefaultHttpContext());
httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS, httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
"web", null); "web", null);
} catch (NamespaceException | ServletException | IOException e) { } catch (NamespaceException | ServletException | IOException e) {
logger.warn("Error during linky servlet startup", e); logger.warn("Error during linky servlet startup", e);
} }
@ -104,54 +118,123 @@ public class LinkyAuthService {
} }
} }
/** @Override
* Call with Linky redirect uri returned State and Code values to get the refresh and access tokens and persist public String authorize(String redirectUri, String reqState, String reqCode) throws LinkyException {
* these values // Will work only in case of direct oAuth2 authentification to enedis
// this is not the case in v1 as we go trough MyElectricalData
if (oAuthSupport) {
try {
OAuthClientService oAuthService = this.oAuthService;
if (oAuthService == null) {
throw new OAuthException("OAuth service is not initialized");
}
logger.debug("Make call to Enedis to get access token.");
final AccessTokenResponse credentials = oAuthService
.getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
final String user = updateProperties(credentials);
logger.debug("Authorized for user: {}", user);
return user;
} catch (RuntimeException | OAuthException | IOException e) {
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
} catch (final OAuthResponseException e) {
throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
}
}
// Fallback for MyElectricalData
else {
/*
* String token = EnedisHttpApi.getToken(httpClient, LinkyBindingConstants.clientId, reqCode);
* *
* @param servletBaseURL the servlet base, which will be the Linky redirect url * logger.debug("token: {}", token);
* @param state The Linky returned state value *
* @param code The Linky returned code value * Collection<Thing> col = this.thingRegistry.getAll();
* @return returns the name of the Linky user that is authorized * for (Thing thing : col) {
* if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
* Configuration config = thing.getConfiguration();
* String prmId = (String) config.get("prmId");
*
* if (!prmId.equals(reqCode)) {
* continue;
* }
*
* config.put("token", token);
* LinkyHandler handler = (LinkyHandler) thing.getHandler();
* if (handler != null) {
* handler.saveConfiguration(config);
* }
* }
* }
*
* return token;
*/ */
public String authorize(String servletBaseURL, String state, String code) throws LinkyException {
LinkyAccountHandler accountHandler = getLinkyAccountHandler(); return "";
if (accountHandler == null) {
logger.debug(
"Linky redirected with state '{}' but no matching bridge was found. Possible bridge has been removed.",
state);
throw new LinkyException(ERROR_UKNOWN_BRIDGE);
} else {
return accountHandler.authorize(servletBaseURL, code);
} }
} }
/** @Override
* @param listener Adds the given handler public boolean isAuthorized() {
final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
&& accessTokenResponse.getRefreshToken() != null;
}
private @Nullable AccessTokenResponse getAccessTokenResponse() {
try {
OAuthClientService oAuthService = this.oAuthService;
return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
} catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
logger.debug("Exception checking authorization: ", e);
return null;
}
}
private String updateProperties(AccessTokenResponse credentials) {
return credentials.getAccessToken();
}
@Override
public String formatAuthorizationUrl(String redirectUri) {
// Will work only in case of direct oAuth2 authentification to enedis
// this is not the case in v1 as we go trough MyElectricalData
if (oAuthSupport) {
try {
OAuthClientService oAuthService = this.oAuthService;
if (oAuthService == null) {
throw new OAuthException("OAuth service is not initialized");
}
String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
return uri;
} catch (final OAuthException e) {
logger.debug("Error constructing AuthorizationUrl: ", e);
return "";
}
}
return "";
}
@Override
public List<String> getAllPrmId() {
List<String> result = new ArrayList<String>();
/*
* Collection<Thing> col = this.thingRegistry.getAll();
*
* for (Thing thing : col) {
* if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
* Configuration config = thing.getConfiguration();
*
* String prmId = (String) config.get("prmId");
* result.add(prmId);
* }
* }
*/ */
public void setLinkyAccountHandler(LinkyAccountHandler accountHandler) {
this.accountHandler = accountHandler; return result;
} }
/**
* @param handler Removes the given handler
*/
public void unsetLinkyAccountHandler(LinkyAccountHandler accountHandler) {
this.accountHandler = null;
}
/**
* @param listener Adds the given handler
*/
public @Nullable LinkyAccountHandler getLinkyAccountHandler() {
return this.accountHandler;
}
@Reference
protected void setHttpService(HttpService httpService) {
this.httpService = httpService;
}
protected void unsetHttpService(HttpService httpService) {
this.httpService = null;
}
} }

View File

@ -83,25 +83,19 @@ public class LinkyAuthServlet extends HttpServlet {
try { try {
handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString()); handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
resp.setContentType(CONTENT_TYPE); resp.setContentType(CONTENT_TYPE);
StringBuffer optionBuffer = new StringBuffer(); StringBuffer optionBuffer = new StringBuffer();
if (accountHandler != null) { List<String> prmIds = linkyAuthService.getAllPrmId();
List<String> prmIds = accountHandler.getAllPrmId();
for (String prmId : prmIds) { for (String prmId : prmIds) {
optionBuffer.append("<option value=\"" + prmId + "\">" + prmId + "</option>"); optionBuffer.append("<option value=\"" + prmId + "\">" + prmId + "</option>");
} }
}
replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString()); replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure); replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK"); replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
if (accountHandler != null) { replaceMap.put(KEY_AUTHORIZE_URI, linkyAuthService.formatAuthorizationUrl(servletBaseURLSecure));
replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
}
resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap)); resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
resp.getWriter().close(); resp.getWriter().close();
} catch (LinkyException ex) { } catch (LinkyException ex) {

View File

@ -30,6 +30,9 @@ public class LinkyBindingConstants {
public static final String BINDING_ID = "linky"; public static final String BINDING_ID = "linky";
public static final String clientId = "_88uJnEjEs_IMf4bjGZJV6gGxYga";
public static final String clientSecret = "6lsPfCmu0fEXuKYy3e0e6w8ydIca";
// List of all Thing Type UIDs // List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_LINKY = new ThingTypeUID(BINDING_ID, "linky"); public static final ThingTypeUID THING_TYPE_LINKY = new ThingTypeUID(BINDING_ID, "linky");
@ -69,16 +72,20 @@ public class LinkyBindingConstants {
/** /**
* Smartthings scopes needed by this binding to work. * Smartthings scopes needed by this binding to work.
*/ */
public static final String LINKY_SCOPES = Stream.of("r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*", public static final String LINKY_SCOPES = Stream.of("am_application_scope", "default")
"r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*", .collect(Collectors.joining(" "));
"r:installedapps", "w:installedapps").collect(Collectors.joining(" ")); // "r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*",
// "r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
// "r:installedapps", "w:installedapps"
// List of Linky services related urls, information // List of Linky services related urls, information
public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/"; public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize"; public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize";
public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token"; public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token";
public static final String ENEDIS_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/"; public static final String ENEDIS_API_ACCOUNT_URL = "https://ext.prod-sandbox.api.enedis.fr/";
public static final String ENEDIS_AUTHORIZE_URL = ENEDIS_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize"; public static final String ENEDIS_API_TOKEN_URL = ENEDIS_API_ACCOUNT_URL + "oauth2/v3/token";
public static final String ENEDIS_API_TOKEN_URL = ENEDIS_ACCOUNT_URL + "token";
public static final String ENEDIS_AUTH_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
public static final String ENEDIS_AUTH_AUTHORIZE_URL = ENEDIS_AUTH_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
} }

View File

@ -25,6 +25,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
public class LinkyConfiguration { public class LinkyConfiguration {
public String token = ""; public String token = "";
public String prmId = ""; public String prmId = "";
public String clientId = "";
public String clientSecret = "";
public boolean seemsValid() { public boolean seemsValid() {
return !prmId.isBlank(); return !prmId.isBlank();

View File

@ -12,16 +12,12 @@
*/ */
package org.openhab.binding.linky.internal; package org.openhab.binding.linky.internal;
import java.io.IOException;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
@ -30,19 +26,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
import org.openhab.binding.linky.internal.handler.LinkyHandler; import org.openhab.binding.linky.internal.handler.LinkyHandler;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory; import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.TrustAllTrustManager; import org.openhab.core.io.net.http.TrustAllTrustManager;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandler;
@ -51,6 +40,7 @@ import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -67,7 +57,7 @@ import com.google.gson.JsonDeserializer;
*/ */
@NonNullByDefault @NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky") @Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
public class LinkyHandlerFactory extends BaseThingHandlerFactory implements LinkyAccountHandler { public class LinkyHandlerFactory extends BaseThingHandlerFactory {
private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX"); private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
private static final DateTimeFormatter LINKY_LOCALDATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd"); private static final DateTimeFormatter LINKY_LOCALDATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd");
private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
@ -75,9 +65,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
private static final int REQUEST_BUFFER_SIZE = 8000; private static final int REQUEST_BUFFER_SIZE = 8000;
private static final int RESPONSE_BUFFER_SIZE = 200000; private static final int RESPONSE_BUFFER_SIZE = 200000;
private final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
private final String clientSecret = "";
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class); private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
private final Gson gson = new GsonBuilder() private final Gson gson = new GsonBuilder()
.registerTypeAdapter(ZonedDateTime.class, .registerTypeAdapter(ZonedDateTime.class,
@ -100,43 +87,36 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
private final LocaleProvider localeProvider; private final LocaleProvider localeProvider;
private final HttpClient httpClient; private final HttpClient httpClient;
private final LinkyAuthService authService;
private final boolean oAuthSupport = false;
private @Nullable OAuthClientService oAuthService;
private final ThingRegistry thingRegistry;
@Activate @Activate
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider, public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
final @Reference HttpClientFactory httpClientFactory, final @Reference LinkyAuthService authService, final @Reference HttpClientFactory httpClientFactory, final @Reference OAuthFactory oAuthFactory,
final @Reference OAuthFactory oAuthFactory, final @Reference ThingRegistry thingRegistry) { final @Reference HttpService httpService, ComponentContext componentContext) {
this.localeProvider = localeProvider; this.localeProvider = localeProvider;
SslContextFactory sslContextFactory = new SslContextFactory.Client(); SslContextFactory sslContextFactory = new SslContextFactory.Client();
try { try {
SSLContext sslContext = SSLContext.getInstance("SSL"); SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null); sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
sslContextFactory.setSslContext(sslContext); sslContextFactory.setSslContext(sslContext);
LinkyAuthService authService = new LinkyAuthService(oAuthFactory, httpService, componentContext);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(), logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
e); e);
} catch (KeyManagementException e) { } catch (KeyManagementException e) {
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e); logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
} }
this.thingRegistry = thingRegistry;
this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory); this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
httpClient.setFollowRedirects(false); httpClient.setFollowRedirects(false);
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE); httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
this.authService = authService;
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
LinkyBindingConstants.LINKY_API_TOKEN_URL, LinkyBindingConstants.LINKY_AUTHORIZE_URL, clientId,
clientSecret, LinkyBindingConstants.LINKY_SCOPES, true);
this.authService.setLinkyAccountHandler(this);
} }
@Override @Override
protected void activate(ComponentContext componentContext) { protected void activate(ComponentContext componentContext) {
super.activate(componentContext); super.activate(componentContext);
try { try {
httpClient.start(); httpClient.start();
} catch (Exception e) { } catch (Exception e) {
logger.warn("Unable to start Jetty HttpClient {}", e.getMessage()); logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
@ -168,124 +148,4 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
return null; return null;
} }
// ===========================================================================
@Override
public boolean isAuthorized() {
final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
&& accessTokenResponse.getRefreshToken() != null;
}
private @Nullable AccessTokenResponse getAccessTokenResponse() {
try {
OAuthClientService oAuthService = this.oAuthService;
return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
} catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
logger.debug("Exception checking authorization: ", e);
return null;
}
}
@Override
public String authorize(String redirectUri, String reqCode) throws LinkyException {
// Will work only in case of direct oAuth2 authentification to enedis
// this is not the case in v1 as we go trough MyElectricalData
if (oAuthSupport) {
try {
OAuthClientService oAuthService = this.oAuthService;
if (oAuthService == null) {
throw new OAuthException("OAuth service is not initialized");
}
logger.debug("Make call to Smartthings to get access token.");
final AccessTokenResponse credentials = oAuthService.getAccessTokenResponseByAuthorizationCode(reqCode,
redirectUri);
final String user = updateProperties(credentials);
logger.debug("Authorized for user: {}", user);
return user;
} catch (RuntimeException | OAuthException | IOException e) {
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
} catch (final OAuthResponseException e) {
throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
}
}
// Fallback for MyElectricalData
else {
String token = EnedisHttpApi.getToken(httpClient, clientId, reqCode);
logger.debug("token: {}", token);
Collection<Thing> col = this.thingRegistry.getAll();
for (Thing thing : col) {
if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
Configuration config = thing.getConfiguration();
String prmId = (String) config.get("prmId");
if (!prmId.equals(reqCode)) {
continue;
}
config.put("token", token);
LinkyHandler handler = (LinkyHandler) thing.getHandler();
if (handler != null) {
handler.saveConfiguration(config);
}
}
}
return token;
}
}
private String updateProperties(AccessTokenResponse credentials) {
return "";
}
@Override
public String formatAuthorizationUrl(String redirectUri) {
// Will work only in case of direct oAuth2 authentification to enedis
// this is not the case in v1 as we go trough MyElectricalData
if (oAuthSupport) {
try {
OAuthClientService oAuthService = this.oAuthService;
if (oAuthService == null) {
throw new OAuthException("OAuth service is not initialized");
}
String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
return uri;
} catch (final OAuthException e) {
logger.debug("Error constructing AuthorizationUrl: ", e);
return "";
}
}
// Fallback for MyElectricalData
else {
String uri = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL;
uri = uri + "?";
uri = uri + "&client_id=" + clientId;
uri = uri + "&duration=" + "P36M";
uri = uri + "&response_type=" + "code";
return uri;
}
}
@Override
public List<String> getAllPrmId() {
Collection<Thing> col = this.thingRegistry.getAll();
List<String> result = new ArrayList<String>();
for (Thing thing : col) {
if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
Configuration config = thing.getConfiguration();
String prmId = (String) config.get("prmId");
result.add(prmId);
}
}
return result;
}
} }

View File

@ -35,6 +35,16 @@
<description>Your Enedis token (can be left empty, use the connection page to automatically fill it <description>Your Enedis token (can be left empty, use the connection page to automatically fill it
http://youopenhab/connectlinky)</description> http://youopenhab/connectlinky)</description>
</parameter> </parameter>
<parameter name="clientId" type="text" required="false">
<label>clientId</label>
<description>Your Enedis clientId</description>
</parameter>
<parameter name="clientSecret" type="text" required="false">
<label>clientSecret</label>
<description>Your Enedis clientSecret</description>
</parameter>
</config-description> </config-description>