]> git.basschouten.com Git - openhab-addons.git/blob
edd4a392ffdb9fd07b96ccc788b990f1bce72a32
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.magentatv.internal.network;
14
15 import static org.openhab.binding.magentatv.internal.MagentaTVBindingConstants.*;
16 import static org.openhab.binding.magentatv.internal.MagentaTVUtil.substringAfterLast;
17
18 import java.io.ByteArrayInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URLEncoder;
22 import java.nio.charset.Charset;
23 import java.text.MessageFormat;
24 import java.util.Properties;
25 import java.util.UUID;
26
27 import javax.ws.rs.HttpMethod;
28
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.openhab.binding.magentatv.internal.MagentaTVException;
32 import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponse;
33 import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponseInstanceCreator;
34 import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthTokenResponse;
35 import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthTokenResponseInstanceCreator;
36 import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentials;
37 import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentialsInstanceCreator;
38 import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthKeyValue;
39 import org.openhab.binding.magentatv.internal.handler.MagentaTVControl;
40 import org.openhab.core.io.net.http.HttpUtil;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import com.google.gson.Gson;
45 import com.google.gson.GsonBuilder;
46
47 /**
48  * The {@link MagentaTVOAuth} class implements the OAuth authentication, which
49  * is used to query the userID from the Telekom platform.
50  *
51  * @author Markus Michels - Initial contribution
52  *
53  *         Deutsche Telekom uses a OAuth-based authentication to access the EPG portal. The
54  *         communication between the MR and the remote app requires a pairing before the receiver could be
55  *         controlled by sending keys etc. The so called userID is not directly derived from any local parameters
56  *         (like terminalID as a has from the mac address), but will be returned as a result from the OAuth
57  *         authentication. This will be performed in 3 steps
58  *         1. Get OAuth credentials -> Service URL, Scope, Secret, Client ID
59  *         2. Get OAth Token -> authentication token for step 3
60  *         3. Authenticate, which then provides the userID (beside other parameters)
61  *
62  */
63 @NonNullByDefault
64 public class MagentaTVOAuth {
65     private final Logger logger = LoggerFactory.getLogger(MagentaTVOAuth.class);
66     final Gson gson;
67
68     public MagentaTVOAuth() {
69         gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new OauthCredentialsInstanceCreator())
70                 .registerTypeAdapter(OAuthTokenResponse.class, new OAuthTokenResponseInstanceCreator())
71                 .registerTypeAdapter(OAuthAuthenticateResponse.class, new OAuthAuthenticateResponseInstanceCreator())
72                 .create();
73     }
74
75     public String getUserId(String accountName, String accountPassword) throws MagentaTVException {
76         logger.debug("Authenticate with account {}", accountName);
77         if (accountName.isEmpty() || accountPassword.isEmpty()) {
78             throw new MagentaTVException("Credentials for OAuth missing, check thing config!");
79         }
80
81         String step = "initialize";
82         String url = "";
83         Properties httpHeader;
84         String postData = "";
85         String httpResponse = "";
86         InputStream dataStream = null;
87
88         // OAuth autentication results
89         String oAuthScope = "";
90         String oAuthService = "";
91         String epghttpsurl = "";
92         String retcode = "";
93         String retmsg = "";
94
95         try {
96             step = "get credentials";
97             httpHeader = initHttpHeader();
98             url = OAUTH_GET_CRED_URL + ":" + OAUTH_GET_CRED_PORT + OAUTH_GET_CRED_URI;
99             httpHeader.setProperty(HEADER_HOST, substringAfterLast(OAUTH_GET_CRED_URL, "/"));
100             logger.trace("{} from {}", step, url);
101             httpResponse = HttpUtil.executeUrl(HttpMethod.GET, url, httpHeader, null, null, NETWORK_TIMEOUT_MS);
102             logger.trace("http response = {}", httpResponse);
103             OauthCredentials cred = gson.fromJson(httpResponse, OauthCredentials.class);
104             epghttpsurl = getString(cred.epghttpsurl);
105             if (epghttpsurl.isEmpty()) {
106                 throw new MagentaTVException("Unable to determine EPG url");
107             }
108             if (!epghttpsurl.contains("/EPG")) {
109                 epghttpsurl = epghttpsurl + "/EPG";
110             }
111             logger.debug("epghttpsurl = {}", epghttpsurl);
112
113             // get OAuth data from response
114             if (cred.sam3Para != null) {
115                 for (OauthKeyValue si : cred.sam3Para) {
116                     logger.trace("sam3Para.{} = {}", si.key, si.value);
117                     if (si.key.equalsIgnoreCase("oAuthScope")) {
118                         oAuthScope = si.value;
119                     } else if (si.key.equalsIgnoreCase("SAM3ServiceURL")) {
120                         oAuthService = si.value;
121                     }
122                 }
123             }
124
125             if (oAuthScope.isEmpty() || oAuthService.isEmpty()) {
126                 throw new MagentaTVException("OAuth failed: Can't get Scope and Service: " + httpResponse);
127             }
128
129             // Get OAuth token
130             step = "get token";
131             url = oAuthService + "/oauth2/tokens";
132             logger.debug("{} from {}", step, url);
133
134             String userId = "";
135             String uuid = UUID.randomUUID().toString();
136             String cnonce = MagentaTVControl.computeMD5(uuid);
137             // New flow based on WebTV
138             postData = MessageFormat.format(
139                     "password={0}&scope={1}+offline_access&grant_type=password&username={2}&x_telekom.access_token.format=CompactToken&x_telekom.access_token.encoding=text%2Fbase64&client_id=10LIVESAM30000004901NGTVWEB0000000000000",
140                     URLEncoder.encode(accountPassword, UTF_8), oAuthScope, URLEncoder.encode(accountName, UTF_8));
141             url = oAuthService + "/oauth2/tokens";
142             dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8")));
143             httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS);
144             logger.trace("http response={}", httpResponse);
145             OAuthTokenResponse resp = gson.fromJson(httpResponse, OAuthTokenResponse.class);
146             if (resp.accessToken.isEmpty()) {
147                 String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} ({2})",
148                         accountName, getString(resp.errorDescription), getString(resp.error));
149                 logger.warn("{}", errorMessage);
150                 throw new MagentaTVException(errorMessage);
151             }
152
153             uuid = "t_" + MagentaTVControl.computeMD5(accountName);
154             url = "https://web.magentatv.de/EPG/JSON/DTAuthenticate?SID=user&T=Mac_chrome_81";
155             postData = "{\"userType\":1,\"terminalid\":\"" + uuid + "\",\"mac\":\"" + uuid + "\""
156                     + ",\"terminaltype\":\"MACWEBTV\",\"utcEnable\":1,\"timezone\":\"Europe/Berlin\","
157                     + "\"terminalDetail\":[{\"key\":\"GUID\",\"value\":\"" + uuid + "\"},"
158                     + "{\"key\":\"HardwareSupplier\",\"value\":\"\"},{\"key\":\"DeviceClass\",\"value\":\"PC\"},"
159                     + "{\"key\":\"DeviceStorage\",\"value\":\"1\"},{\"key\":\"DeviceStorageSize\",\"value\":\"\"}],"
160                     + "\"softwareVersion\":\"\",\"osversion\":\"\",\"terminalvendor\":\"Unknown\","
161                     + "\"caDeviceInfo\":[{\"caDeviceType\":6,\"caDeviceId\":\"" + uuid + "\"}]," + "\"accessToken\":\""
162                     + resp.accessToken + "\",\"preSharedKeyID\":\"PC01P00002\",\"cnonce\":\"" + cnonce + "\"}";
163             dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8")));
164             logger.debug("HTTP POST {}, postData={}", url, postData);
165             httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS);
166
167             logger.trace("http response={}", httpResponse);
168             OAuthAuthenticateResponse authResp = gson.fromJson(httpResponse, OAuthAuthenticateResponse.class);
169             if (authResp.userID.isEmpty()) {
170                 String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} {2}",
171                         accountName, getString(authResp.retcode), getString(authResp.desc));
172                 logger.warn("{}", errorMessage);
173                 throw new MagentaTVException(errorMessage);
174             }
175             userId = getString(authResp.userID);
176             if (userId.isEmpty()) {
177                 throw new MagentaTVException("No userID received!");
178             }
179             String hashedUserID = MagentaTVControl.computeMD5(userId).toUpperCase();
180             logger.trace("done, userID = {}", hashedUserID);
181             return hashedUserID;
182         } catch (IOException e) {
183             throw new MagentaTVException(e,
184                     "Unable to authenticate {0}: {1} failed; serviceURL={2}, rc={3}/{4}, response={5}", accountName,
185                     step, oAuthService, retcode, retmsg, httpResponse);
186         }
187     }
188
189     private Properties initHttpHeader() {
190         Properties httpHeader = new Properties();
191         httpHeader.setProperty(HEADER_USER_AGENT, OAUTH_USER_AGENT);
192         httpHeader.setProperty(HEADER_ACCEPT, "*/*");
193         httpHeader.setProperty(HEADER_LANGUAGE, "de-de");
194         httpHeader.setProperty(HEADER_CACHE_CONTROL, "no-cache");
195         return httpHeader;
196     }
197
198     private String getString(@Nullable String value) {
199         return value != null ? value : "";
200     }
201 }