]> git.basschouten.com Git - openhab-addons.git/blob
bdec6d997cdd822d8177f650b1a5fd0e8ff29ee3
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.enphase.internal.handler;
14
15 import java.net.HttpCookie;
16 import java.net.URI;
17 import java.nio.charset.StandardCharsets;
18 import java.util.Base64;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jetty.client.HttpClient;
26 import org.eclipse.jetty.client.api.ContentResponse;
27 import org.eclipse.jetty.client.api.Request;
28 import org.eclipse.jetty.client.util.FormContentProvider;
29 import org.eclipse.jetty.http.HttpHeader;
30 import org.eclipse.jetty.http.HttpMethod;
31 import org.eclipse.jetty.util.Fields;
32 import org.jsoup.Jsoup;
33 import org.jsoup.nodes.Document;
34 import org.jsoup.nodes.Element;
35 import org.jsoup.select.Elements;
36 import org.openhab.binding.enphase.internal.dto.EntrezJwtDTO;
37 import org.openhab.binding.enphase.internal.dto.EntrezJwtDTO.EntrezJwtBodyDTO;
38 import org.openhab.binding.enphase.internal.dto.EntrezJwtDTO.EntrezJwtHeaderDTO;
39 import org.openhab.binding.enphase.internal.exception.EntrezConnectionException;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import com.google.gson.Gson;
44 import com.google.gson.GsonBuilder;
45 import com.google.gson.JsonSyntaxException;
46
47 /**
48  * Connector logic for connecting to Entrez server
49  *
50  * @author Joe Inkenbrandt - Initial contribution
51  */
52 @NonNullByDefault
53 public class EntrezConnector {
54
55     private static final String SESSION_COOKIE_NAME = "SESSION";
56     private static final String ELEMENT_ID_JWT_TOKEN = "#JWTToken";
57     private static final String LOGIN_URL = "https://entrez.enphaseenergy.com/login";
58     private static final String TOKEN_URL = "https://entrez.enphaseenergy.com/entrez_tokens";
59
60     private final Logger logger = LoggerFactory.getLogger(EntrezConnector.class);
61     private final Gson gson = new GsonBuilder().create();
62     private final HttpClient httpClient;
63
64     private static final long CONNECT_TIMEOUT_SECONDS = 10;
65
66     public EntrezConnector(final HttpClient httpClient) {
67         this.httpClient = httpClient;
68     }
69
70     public String retrieveJwt(final String username, final String password, final String siteId, final String serialNum)
71             throws EntrezConnectionException {
72         final String session = login(username, password);
73         final Fields fields = new Fields();
74         fields.put("Site", siteId);
75         fields.put("serialNum", serialNum);
76
77         final URI uri = URI.create(TOKEN_URL);
78         logger.trace("Retrieving jwt from '{}'", uri);
79         final Request request = httpClient.newRequest(uri).method(HttpMethod.POST)
80                 .cookie(new HttpCookie(SESSION_COOKIE_NAME, session)).content(new FormContentProvider(fields))
81                 .timeout(CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
82
83         final ContentResponse response = send(request);
84         final String contentAsString = response.getContentAsString();
85         final Document document = Jsoup.parse(contentAsString);
86         final Elements elements = document.select(ELEMENT_ID_JWT_TOKEN);
87         final Element first = elements.first();
88
89         if (first == null) {
90             logger.debug("Could not select element '{}' in received data from entrez site. Received data: {}",
91                     ELEMENT_ID_JWT_TOKEN, contentAsString);
92             throw new EntrezConnectionException("Could not parse data from entrez site");
93         }
94         return first.text();
95     }
96
97     public EntrezJwtDTO processJwt(final String jwt) throws EntrezConnectionException {
98         try {
99             final String[] parts = jwt.split("\\.", 0);
100             if (parts.length < 2) {
101                 logger.debug("Could not split data into 2 parts. Recevied data: {}", jwt);
102                 throw new EntrezConnectionException("Could not parse data from entrez site");
103             }
104             final EntrezJwtHeaderDTO header = gson.fromJson(
105                     new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8),
106                     EntrezJwtHeaderDTO.class);
107             final EntrezJwtBodyDTO body = gson.fromJson(
108                     new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8),
109                     EntrezJwtBodyDTO.class);
110
111             return new EntrezJwtDTO(header, body);
112         } catch (JsonSyntaxException | IllegalArgumentException e) {
113             throw new EntrezConnectionException("Could not parse data from entrez site:", e);
114         }
115     }
116
117     private String login(final String username, final String password) throws EntrezConnectionException {
118         final Fields fields = new Fields();
119         fields.put("username", username);
120         fields.put("password", password);
121
122         final URI uri = URI.create(LOGIN_URL);
123         logger.trace("Retrieving session id from '{}'", uri);
124         final Request request = httpClient.newRequest(uri).method(HttpMethod.POST)
125                 .content(new FormContentProvider(fields)).timeout(CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
126         final ContentResponse response = send(request);
127
128         if (response.getStatus() == 200 && response.getHeaders().contains(HttpHeader.SET_COOKIE)) {
129             final List<HttpCookie> cookies = HttpCookie.parse(response.getHeaders().get(HttpHeader.SET_COOKIE));
130
131             for (final HttpCookie c : cookies) {
132                 if (SESSION_COOKIE_NAME.equals(c.getName())) {
133                     return c.getValue();
134                 }
135             }
136         }
137         logger.debug("Failed to login to Entrez portal. Portal returned status: {}. Response from Entrez portal: {}",
138                 response.getStatus(), response.getContentAsString());
139         throw new EntrezConnectionException(
140                 "Could not login to Entrez JWT Portal. Status code:" + response.getStatus());
141     }
142
143     private ContentResponse send(final Request request) throws EntrezConnectionException {
144         try {
145             return request.send();
146         } catch (final InterruptedException e) {
147             Thread.currentThread().interrupt();
148             throw new EntrezConnectionException("Interrupted");
149         } catch (final TimeoutException e) {
150             logger.debug("TimeoutException: {}", e.getMessage());
151             throw new EntrezConnectionException("Connection timeout: ", e);
152         } catch (final ExecutionException e) {
153             logger.debug("ExecutionException: {}", e.getMessage(), e);
154             throw new EntrezConnectionException("Could not retrieve data: ", e.getCause());
155         }
156     }
157 }