[magentatv] Adapt to new Telekom OAuth flow (required to retrieve the userId) (#10267)

Signed-off-by: Markus Michels <markus7017@gmail.com>
pull/10348/head
Markus Michels 2021-03-16 21:54:55 +01:00 committed by GitHub
parent 754751c19b
commit c582dda1d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 232 additions and 155 deletions

View File

@ -117,7 +117,7 @@ For security reasons the credentials are automatically deleted from the thing co
| |key |String |Send key code to the receiver (see code table below) | | |key |String |Send key code to the receiver (see code table below) |
| |mute |Switch |Mute volume (mute the speaker) | | |mute |Switch |Mute volume (mute the speaker) |
|status |playMode |String |Current play mode - this info is not reliable | |status |playMode |String |Current play mode - this info is not reliable |
| |channelCode |Number  |The channel code from the EPG. | | |channelCode |Number |The channel code from the EPG. |
|program |title |String |Title of the running program or video being played | |program |title |String |Title of the running program or video being played |
| |text |String |Some description (as reported by the receiver, could be empty) | | |text |String |Some description (as reported by the receiver, could be empty) |
| |start |DateTime |Time when the program started | | |start |DateTime |Time when the program started |
@ -327,4 +327,4 @@ to switch it ON and
to switch it off. to switch it off.
After an openHAB restart you need to make sure that OH and receiver are in sync, because the binding can't read the power status at startup. After an openHAB restart you need to make sure that OH and receiver are in sync, because the binding can't read the power status at startup.

View File

@ -20,18 +20,15 @@ import java.io.InputStreamReader;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.ws.rs.client.ClientBuilder;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.magentatv.internal.network.MagentaTVOAuth; import org.openhab.binding.magentatv.internal.network.MagentaTVOAuth;
import org.openhab.core.io.console.Console; import org.openhab.core.io.console.Console;
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension; import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
import org.openhab.core.io.console.extensions.ConsoleCommandExtension; import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
import org.openhab.core.io.net.http.HttpClientFactory;
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.component.annotations.ReferenceCardinality;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -47,14 +44,12 @@ public class MagentaTVConsoleHandler extends AbstractConsoleCommandExtension {
private static final String CMD_LOGIN = "login"; private static final String CMD_LOGIN = "login";
private final Logger logger = LoggerFactory.getLogger(MagentaTVConsoleHandler.class); private final Logger logger = LoggerFactory.getLogger(MagentaTVConsoleHandler.class);
private final MagentaTVOAuth oauth = new MagentaTVOAuth(); private final MagentaTVOAuth oauth;
@Reference(cardinality = ReferenceCardinality.OPTIONAL)
private @Nullable ClientBuilder injectedClientBuilder;
@Activate @Activate
public MagentaTVConsoleHandler() { public MagentaTVConsoleHandler(@Reference HttpClientFactory httpClientFactory) {
super(BINDING_ID, "Interact with the " + BINDING_ID + " integration."); super(BINDING_ID, "Interact with the " + BINDING_ID + " integration.");
oauth = new MagentaTVOAuth(httpClientFactory.getCommonHttpClient());
} }
@Override @Override

View File

@ -19,10 +19,12 @@ import java.util.Map;
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.eclipse.jetty.client.HttpClient;
import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager.MagentaTVDevice; import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager.MagentaTVDevice;
import org.openhab.binding.magentatv.internal.handler.MagentaTVHandler; import org.openhab.binding.magentatv.internal.handler.MagentaTVHandler;
import org.openhab.binding.magentatv.internal.network.MagentaTVNetwork; import org.openhab.binding.magentatv.internal.network.MagentaTVNetwork;
import org.openhab.binding.magentatv.internal.network.MagentaTVPoweroffListener; import org.openhab.binding.magentatv.internal.network.MagentaTVPoweroffListener;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.net.HttpServiceUtil; import org.openhab.core.net.HttpServiceUtil;
import org.openhab.core.net.NetworkAddressService; import org.openhab.core.net.NetworkAddressService;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -51,6 +53,7 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory {
private final MagentaTVNetwork network = new MagentaTVNetwork(); private final MagentaTVNetwork network = new MagentaTVNetwork();
private final MagentaTVDeviceManager manager; private final MagentaTVDeviceManager manager;
private final HttpClient httpClient;
private @Nullable MagentaTVPoweroffListener upnpListener; private @Nullable MagentaTVPoweroffListener upnpListener;
private boolean servletInitialized = false; private boolean servletInitialized = false;
@ -64,11 +67,11 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory {
@Activate @Activate
public MagentaTVHandlerFactory(@Reference NetworkAddressService networkAddressService, public MagentaTVHandlerFactory(@Reference NetworkAddressService networkAddressService,
@Reference MagentaTVDeviceManager manager, ComponentContext componentContext, @Reference HttpClientFactory httpClientFactory, @Reference MagentaTVDeviceManager manager,
Map<String, String> configProperties) throws IOException { ComponentContext componentContext, Map<String, String> configProperties) throws IOException {
super.activate(componentContext); super.activate(componentContext);
this.manager = manager; this.manager = manager;
this.httpClient = httpClientFactory.getCommonHttpClient();
try { try {
logger.debug("Initialize network access"); logger.debug("Initialize network access");
System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("java.net.preferIPv4Stack", "true");
@ -99,7 +102,7 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory {
logger.debug("Create thing type {}", thing.getThingTypeUID().getAsString()); logger.debug("Create thing type {}", thing.getThingTypeUID().getAsString());
if (THING_TYPE_RECEIVER.equals(thingTypeUID)) { if (THING_TYPE_RECEIVER.equals(thingTypeUID)) {
return new MagentaTVHandler(manager, thing, network); return new MagentaTVHandler(manager, thing, network, httpClient);
} }
return null; return null;

View File

@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVException;
import org.openhab.binding.magentatv.internal.config.MagentaTVDynamicConfig; import org.openhab.binding.magentatv.internal.config.MagentaTVDynamicConfig;
import org.openhab.binding.magentatv.internal.network.MagentaTVHttp; import org.openhab.binding.magentatv.internal.network.MagentaTVHttp;
@ -44,7 +45,7 @@ public class MagentaTVControl {
private final MagentaTVNetwork network; private final MagentaTVNetwork network;
private final MagentaTVHttp http = new MagentaTVHttp(); private final MagentaTVHttp http = new MagentaTVHttp();
private final MagentaTVOAuth oauth = new MagentaTVOAuth(); private final MagentaTVOAuth oauth;
private final MagentaTVDynamicConfig config; private final MagentaTVDynamicConfig config;
private boolean initialized = false; private boolean initialized = false;
private String thingId = ""; private String thingId = "";
@ -52,11 +53,13 @@ public class MagentaTVControl {
public MagentaTVControl() { public MagentaTVControl() {
config = new MagentaTVDynamicConfig(); config = new MagentaTVDynamicConfig();
network = new MagentaTVNetwork(); network = new MagentaTVNetwork();
oauth = new MagentaTVOAuth(new HttpClient());
} }
public MagentaTVControl(MagentaTVDynamicConfig config, MagentaTVNetwork network) { public MagentaTVControl(MagentaTVDynamicConfig config, MagentaTVNetwork network, HttpClient httpClient) {
thingId = config.getFriendlyName(); thingId = config.getFriendlyName();
this.network = network; this.network = network;
this.oauth = new MagentaTVOAuth(httpClient);
this.config = config; this.config = config;
this.config.setTerminalID(computeMD5(network.getLocalMAC().toUpperCase() + config.getUDN())); this.config.setTerminalID(computeMD5(network.getLocalMAC().toUpperCase() + config.getUDN()));
this.config.setLocalIP(network.getLocalIP()); this.config.setLocalIP(network.getLocalIP());
@ -391,7 +394,8 @@ public class MagentaTVControl {
// direct key code // direct key code
return key; return key;
} }
return KEY_MAP.getOrDefault(key, ""); String code = KEY_MAP.get(key);
return code != null ? code : "";
} }
/** /**

View File

@ -33,6 +33,7 @@ import javax.measure.Unit;
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.eclipse.jetty.client.HttpClient;
import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager; import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager;
import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVException;
import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.MRPayEvent; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.MRPayEvent;
@ -88,6 +89,7 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe
private final Gson gson; private final Gson gson;
protected final MagentaTVNetwork network; protected final MagentaTVNetwork network;
protected final MagentaTVDeviceManager manager; protected final MagentaTVDeviceManager manager;
private final HttpClient httpClient;
protected MagentaTVControl control = new MagentaTVControl(); protected MagentaTVControl control = new MagentaTVControl();
private String thingId = ""; private String thingId = "";
@ -102,10 +104,12 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe
* @param thing * @param thing
* @param bindingConfig * @param bindingConfig
*/ */
public MagentaTVHandler(MagentaTVDeviceManager manager, Thing thing, MagentaTVNetwork network) { public MagentaTVHandler(MagentaTVDeviceManager manager, Thing thing, MagentaTVNetwork network,
HttpClient httpClient) {
super(thing); super(thing);
this.manager = manager; this.manager = manager;
this.network = network; this.network = network;
this.httpClient = httpClient;
gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new MRProgramInfoEventInstanceCreator()) gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new MRProgramInfoEventInstanceCreator())
.registerTypeAdapter(OAuthTokenResponse.class, new MRProgramStatusInstanceCreator()) .registerTypeAdapter(OAuthTokenResponse.class, new MRProgramStatusInstanceCreator())
.registerTypeAdapter(OAuthAuthenticateResponse.class, new MRShortProgramInfoInstanceCreator()) .registerTypeAdapter(OAuthAuthenticateResponse.class, new MRShortProgramInfoInstanceCreator())
@ -150,7 +154,7 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe
} }
config.setMacAddress(macAddress); config.setMacAddress(macAddress);
} }
control = new MagentaTVControl(config, network); control = new MagentaTVControl(config, network, httpClient);
config.updateNetwork(control.getConfig()); // get network parameters from control config.updateNetwork(control.getConfig()); // get network parameters from control
// Check for emoty credentials (e.g. missing in .things file) // Check for emoty credentials (e.g. missing in .things file)
@ -185,7 +189,7 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe
} }
/** /**
* This routine is called every time the Thing configuration has been changed. * This routine is called every time the Thing configuration has been changed
*/ */
@Override @Override
public void handleConfigurationUpdate(Map<String, Object> configurationParameters) { public void handleConfigurationUpdate(Map<String, Object> configurationParameters) {

View File

@ -20,7 +20,6 @@ import java.net.NetworkInterface;
import java.net.SocketException; import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.apache.commons.net.util.SubnetUtils;
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.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVException;
@ -76,34 +75,6 @@ public class MagentaTVNetwork {
"Unable to get local IP / MAC address, check network settings in openHAB system configuration!"); "Unable to get local IP / MAC address, check network settings in openHAB system configuration!");
} }
/**
* Checks if client ip equals or is in range of ip networks provided by
* semicolon separated list
*
* @param clientIp in numeric form like "192.168.0.10"
* @param ipList like "127.0.0.1;192.168.0.0/24;10.0.0.0/8"
* @return true if client ip from the list os ips and networks
*/
public static boolean isIpInSubnet(String clientIp, String ipList) {
if (ipList.isEmpty()) {
// No ip address provided
return true;
}
String[] subnetMasks = ipList.split(";");
for (String subnetMask : subnetMasks) {
subnetMask = subnetMask.trim();
if (clientIp.equals(subnetMask)) {
return true;
}
if (subnetMask.contains("/")) {
if (new SubnetUtils(subnetMask).getInfo().isInRange(clientIp)) {
return true;
}
}
}
return false;
}
@Nullable @Nullable
public NetworkInterface getLocalInterface() { public NetworkInterface getLocalInterface() {
return localInterface; return localInterface;

View File

@ -13,21 +13,34 @@
package org.openhab.binding.magentatv.internal.network; package org.openhab.binding.magentatv.internal.network;
import static org.openhab.binding.magentatv.internal.MagentaTVBindingConstants.*; import static org.openhab.binding.magentatv.internal.MagentaTVBindingConstants.*;
import static org.openhab.binding.magentatv.internal.MagentaTVUtil.substringAfterLast; import static org.openhab.binding.magentatv.internal.MagentaTVUtil.*;
import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException;
import java.io.IOException; import java.net.HttpCookie;
import java.io.InputStream;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.ws.rs.HttpMethod; import javax.ws.rs.HttpMethod;
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.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVException;
import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponse; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponse;
import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponseInstanceCreator; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponseInstanceCreator;
@ -37,7 +50,6 @@ import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentials;
import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentialsInstanceCreator; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentialsInstanceCreator;
import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthKeyValue; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthKeyValue;
import org.openhab.binding.magentatv.internal.handler.MagentaTVControl; import org.openhab.binding.magentatv.internal.handler.MagentaTVControl;
import org.openhab.core.io.net.http.HttpUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -50,7 +62,7 @@ import com.google.gson.GsonBuilder;
* *
* @author Markus Michels - Initial contribution * @author Markus Michels - Initial contribution
* *
* Deutsche Telekom uses a OAuth-based authentication to access the EPG portal. The * Deutsche Telekom uses an OAuth-based authentication to access the EPG portal. The
* communication between the MR and the remote app requires a pairing before the receiver could be * communication between the MR and the remote app requires a pairing before the receiver could be
* controlled by sending keys etc. The so called userID is not directly derived from any local parameters * controlled by sending keys etc. The so called userID is not directly derived from any local parameters
* (like terminalID as a has from the mac address), but will be returned as a result from the OAuth * (like terminalID as a has from the mac address), but will be returned as a result from the OAuth
@ -63,9 +75,12 @@ import com.google.gson.GsonBuilder;
@NonNullByDefault @NonNullByDefault
public class MagentaTVOAuth { public class MagentaTVOAuth {
private final Logger logger = LoggerFactory.getLogger(MagentaTVOAuth.class); private final Logger logger = LoggerFactory.getLogger(MagentaTVOAuth.class);
final Gson gson; private HttpClient httpClient;
private final Gson gson;
private List<HttpCookie> cookies = new ArrayList<>();
public MagentaTVOAuth() { public MagentaTVOAuth(HttpClient httpClient) {
this.httpClient = httpClient;
gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new OauthCredentialsInstanceCreator()) gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new OauthCredentialsInstanceCreator())
.registerTypeAdapter(OAuthTokenResponse.class, new OAuthTokenResponseInstanceCreator()) .registerTypeAdapter(OAuthTokenResponse.class, new OAuthTokenResponseInstanceCreator())
.registerTypeAdapter(OAuthAuthenticateResponse.class, new OAuthAuthenticateResponseInstanceCreator()) .registerTypeAdapter(OAuthAuthenticateResponse.class, new OAuthAuthenticateResponseInstanceCreator())
@ -78,124 +93,209 @@ public class MagentaTVOAuth {
throw new MagentaTVException("Credentials for OAuth missing, check thing config!"); throw new MagentaTVException("Credentials for OAuth missing, check thing config!");
} }
String step = "initialize";
String url = ""; String url = "";
Properties httpHeader; Properties httpHeader = initHttpHeader();
String postData = ""; String postData = "";
String httpResponse = ""; String httpResponse = "";
InputStream dataStream = null;
// OAuth autentication results // OAuth autentication results
String oAuthScope = ""; String oAuthScope = "";
String oAuthService = ""; String oAuthService = "";
String epghttpsurl = ""; String epghttpsurl = "";
String retcode = "";
String retmsg = "";
// Get credentials
url = OAUTH_GET_CRED_URL + ":" + OAUTH_GET_CRED_PORT + OAUTH_GET_CRED_URI;
httpHeader.setProperty(HttpHeader.HOST.toString(), substringAfterLast(OAUTH_GET_CRED_URL, "/"));
httpResponse = httpRequest(HttpMethod.GET, url, httpHeader, "");
OauthCredentials cred = gson.fromJson(httpResponse, OauthCredentials.class);
epghttpsurl = getString(cred.epghttpsurl);
if (epghttpsurl.isEmpty()) {
throw new MagentaTVException("Unable to determine EPG url");
}
if (!epghttpsurl.contains("/EPG")) {
epghttpsurl = epghttpsurl + "/EPG";
}
logger.debug("OAuth: epghttpsurl = {}", epghttpsurl);
// get OAuth data from response
if (cred.sam3Para != null) {
for (OauthKeyValue si : cred.sam3Para) {
logger.trace("sam3Para.{} = {}", si.key, si.value);
if (si.key.equalsIgnoreCase("oAuthScope")) {
oAuthScope = si.value;
} else if (si.key.equalsIgnoreCase("SAM3ServiceURL")) {
oAuthService = si.value;
}
}
}
if (oAuthScope.isEmpty() || oAuthService.isEmpty()) {
throw new MagentaTVException("OAuth failed: Can't get Scope and Service: " + httpResponse);
}
// Get OAuth token (New flow based on WebTV)
String userId = "";
String terminalId = UUID.randomUUID().toString();
String cnonce = MagentaTVControl.computeMD5(terminalId);
url = oAuthService + "/oauth2/tokens";
postData = MessageFormat.format(
"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",
urlEncode(accountPassword), oAuthScope, urlEncode(accountName));
url = oAuthService + "/oauth2/tokens";
httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData);
OAuthTokenResponse resp = gson.fromJson(httpResponse, OAuthTokenResponse.class);
if (resp.accessToken.isEmpty()) {
String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} ({2})",
accountName, getString(resp.errorDescription), getString(resp.error));
logger.warn("{}", errorMessage);
throw new MagentaTVException(errorMessage);
}
logger.debug("OAuth: Access Token retrieved");
// General authentication
logger.debug("OAuth: Generating CSRF token");
url = "https://api.prod.sngtv.magentatv.de/EPG/JSON/Authenticate";
httpHeader = initHttpHeader();
httpHeader.setProperty(HttpHeader.HOST.toString(), "api.prod.sngtv.magentatv.de");
httpHeader.setProperty("Origin", "https://web.magentatv.de");
httpHeader.setProperty(HttpHeader.REFERER.toString(), "https://web.magentatv.de/");
postData = "{\"areaid\":\"1\",\"cnonce\":\"" + cnonce + "\",\"mac\":\"" + terminalId
+ "\",\"preSharedKeyID\":\"NGTV000001\",\"subnetId\":\"4901\",\"templatename\":\"NGTV\",\"terminalid\":\""
+ terminalId
+ "\",\"terminaltype\":\"WEB-MTV\",\"terminalvendor\":\"WebTV\",\"timezone\":\"Europe/Berlin\",\"usergroup\":\"-1\",\"userType\":3,\"utcEnable\":1}";
httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData);
String csrf = "";
for (HttpCookie c : cookies) { // get CRSF Token
String value = c.getValue();
if (value.contains("CSRFSESSION")) {
csrf = substringBetween(value, "CSRFSESSION" + "=", ";");
}
}
if (csrf.isEmpty()) {
throw new MagentaTVException("OAuth: Unable to get CSRF token!");
}
// Final step: Retrieve userId
url = "https://api.prod.sngtv.magentatv.de/EPG/JSON/DTAuthenticate";
httpHeader = initHttpHeader();
httpHeader.setProperty(HttpHeader.HOST.toString(), "api.prod.sngtv.magentatv.de");
httpHeader.setProperty("Origin", "https://web.magentatv.de");
httpHeader.setProperty(HttpHeader.REFERER.toString(), "https://web.magentatv.de/");
httpHeader.setProperty("X_CSRFToken", csrf);
postData = "{\"areaid\":\"1\",\"cnonce\":\"" + cnonce + "\",\"mac\":\"" + terminalId + "\","
+ "\"preSharedKeyID\":\"NGTV000001\",\"subnetId\":\"4901\",\"templatename\":\"NGTV\","
+ "\"terminalid\":\"" + terminalId + "\",\"terminaltype\":\"WEB-MTV\",\"terminalvendor\":\"WebTV\","
+ "\"timezone\":\"Europe/Berlin\",\"usergroup\":\"\",\"userType\":\"1\",\"utcEnable\":1,"
+ "\"accessToken\":\"" + resp.accessToken
+ "\",\"caDeviceInfo\":[{\"caDeviceId\":\"4ef4d933-9a43-41d3-9e3a-84979f22c9eb\","
+ "\"caDeviceType\":8}],\"connectType\":1,\"osversion\":\"Mac OS 10.15.7\",\"softwareVersion\":\"1.33.4.3\","
+ "\"terminalDetail\":[{\"key\":\"GUID\",\"value\":\"" + terminalId + "\"},"
+ "{\"key\":\"HardwareSupplier\",\"value\":\"WEB-MTV\"},{\"key\":\"DeviceClass\",\"value\":\"TV\"},"
+ "{\"key\":\"DeviceStorage\",\"value\":0},{\"key\":\"DeviceStorageSize\",\"value\":0}]}";
httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData);
OAuthAuthenticateResponse authResp = gson.fromJson(httpResponse, OAuthAuthenticateResponse.class);
if (authResp.userID.isEmpty()) {
String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} {2}",
accountName, getString(authResp.retcode), getString(authResp.desc));
logger.warn("{}", errorMessage);
throw new MagentaTVException(errorMessage);
}
userId = getString(authResp.userID);
if (userId.isEmpty()) {
throw new MagentaTVException("No userID received!");
}
String hashedUserID = MagentaTVControl.computeMD5(userId).toUpperCase();
logger.trace("done, userID = {}", hashedUserID);
return hashedUserID;
}
private String httpRequest(String method, String url, Properties headers, String data) throws MagentaTVException {
String result = "";
try { try {
step = "get credentials"; Request request = httpClient.newRequest(url).method(method).timeout(NETWORK_TIMEOUT_MS,
httpHeader = initHttpHeader(); TimeUnit.MILLISECONDS);
url = OAUTH_GET_CRED_URL + ":" + OAUTH_GET_CRED_PORT + OAUTH_GET_CRED_URI; for (Enumeration<?> e = headers.keys(); e.hasMoreElements();) {
httpHeader.setProperty(HEADER_HOST, substringAfterLast(OAUTH_GET_CRED_URL, "/")); String key = (String) e.nextElement();
logger.trace("{} from {}", step, url); String val = (String) headers.get(key);
httpResponse = HttpUtil.executeUrl(HttpMethod.GET, url, httpHeader, null, null, NETWORK_TIMEOUT_MS); request.header(key, val);
logger.trace("http response = {}", httpResponse);
OauthCredentials cred = gson.fromJson(httpResponse, OauthCredentials.class);
epghttpsurl = getString(cred.epghttpsurl);
if (epghttpsurl.isEmpty()) {
throw new MagentaTVException("Unable to determine EPG url");
} }
if (!epghttpsurl.contains("/EPG")) { if (method.equals(HttpMethod.POST)) {
epghttpsurl = epghttpsurl + "/EPG"; fillPostData(request, data);
} }
logger.debug("epghttpsurl = {}", epghttpsurl); if (cookies.size() > 0) {
// Add cookies
String cookieValue = "";
for (HttpCookie c : cookies) {
cookieValue = cookieValue + substringBefore(c.getValue(), ";") + "; ";
}
request.header("Cookie", substringBeforeLast(cookieValue, ";"));
}
logger.debug("OAuth: HTTP Request\n\tHTTP {} {}\n\tData={}", method, url, data.isEmpty() ? "<none>" : data);
logger.trace("\n\tHeaders={}\tCookies={}", request.getHeaders(), request.getCookies());
// get OAuth data from response ContentResponse contentResponse = request.send();
if (cred.sam3Para != null) { result = contentResponse.getContentAsString().replace("\t", "").replace("\r\n", "").trim();
for (OauthKeyValue si : cred.sam3Para) { int status = contentResponse.getStatus();
logger.trace("sam3Para.{} = {}", si.key, si.value); logger.debug("OAuth: HTTP Response\n\tStatus={} {}\n\tData={}", status, contentResponse.getReason(),
if (si.key.equalsIgnoreCase("oAuthScope")) { result.isEmpty() ? "<none>" : result);
oAuthScope = si.value; logger.trace("\n\tHeaders={}", contentResponse.getHeaders());
} else if (si.key.equalsIgnoreCase("SAM3ServiceURL")) {
oAuthService = si.value; // validate response, API errors are reported as Json
} HttpFields responseHeaders = contentResponse.getHeaders();
for (HttpField f : responseHeaders) {
if (f.getName().equals("Set-Cookie")) {
HttpCookie c = new HttpCookie(f.getName(), f.getValue());
cookies.add(c);
} }
} }
if (oAuthScope.isEmpty() || oAuthService.isEmpty()) { if (status != HttpStatus.OK_200) {
throw new MagentaTVException("OAuth failed: Can't get Scope and Service: " + httpResponse); String error = "HTTP reqaest failed for URL " + url + ", Code=" + contentResponse.getReason() + "("
+ status + ")";
throw new MagentaTVException(error);
} }
} catch (ExecutionException | InterruptedException | TimeoutException e) {
// Get OAuth token String error = "HTTP reqaest failed for URL " + url;
step = "get token"; logger.info("{}", error, e);
url = oAuthService + "/oauth2/tokens"; throw new MagentaTVException(e, error);
logger.debug("{} from {}", step, url);
String userId = "";
String uuid = UUID.randomUUID().toString();
String cnonce = MagentaTVControl.computeMD5(uuid);
// New flow based on WebTV
postData = MessageFormat.format(
"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",
URLEncoder.encode(accountPassword, UTF_8), oAuthScope, URLEncoder.encode(accountName, UTF_8));
url = oAuthService + "/oauth2/tokens";
dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8")));
httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS);
logger.trace("http response={}", httpResponse);
OAuthTokenResponse resp = gson.fromJson(httpResponse, OAuthTokenResponse.class);
if (resp.accessToken.isEmpty()) {
String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} ({2})",
accountName, getString(resp.errorDescription), getString(resp.error));
logger.warn("{}", errorMessage);
throw new MagentaTVException(errorMessage);
}
uuid = "t_" + MagentaTVControl.computeMD5(accountName);
url = "https://web.magentatv.de/EPG/JSON/DTAuthenticate?SID=user&T=Mac_chrome_81";
postData = "{\"userType\":1,\"terminalid\":\"" + uuid + "\",\"mac\":\"" + uuid + "\""
+ ",\"terminaltype\":\"MACWEBTV\",\"utcEnable\":1,\"timezone\":\"Europe/Berlin\","
+ "\"terminalDetail\":[{\"key\":\"GUID\",\"value\":\"" + uuid + "\"},"
+ "{\"key\":\"HardwareSupplier\",\"value\":\"\"},{\"key\":\"DeviceClass\",\"value\":\"PC\"},"
+ "{\"key\":\"DeviceStorage\",\"value\":\"1\"},{\"key\":\"DeviceStorageSize\",\"value\":\"\"}],"
+ "\"softwareVersion\":\"\",\"osversion\":\"\",\"terminalvendor\":\"Unknown\","
+ "\"caDeviceInfo\":[{\"caDeviceType\":6,\"caDeviceId\":\"" + uuid + "\"}]," + "\"accessToken\":\""
+ resp.accessToken + "\",\"preSharedKeyID\":\"PC01P00002\",\"cnonce\":\"" + cnonce + "\"}";
dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8")));
logger.debug("HTTP POST {}, postData={}", url, postData);
httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS);
logger.trace("http response={}", httpResponse);
OAuthAuthenticateResponse authResp = gson.fromJson(httpResponse, OAuthAuthenticateResponse.class);
if (authResp.userID.isEmpty()) {
String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} {2}",
accountName, getString(authResp.retcode), getString(authResp.desc));
logger.warn("{}", errorMessage);
throw new MagentaTVException(errorMessage);
}
userId = getString(authResp.userID);
if (userId.isEmpty()) {
throw new MagentaTVException("No userID received!");
}
String hashedUserID = MagentaTVControl.computeMD5(userId).toUpperCase();
logger.trace("done, userID = {}", hashedUserID);
return hashedUserID;
} catch (IOException e) {
throw new MagentaTVException(e,
"Unable to authenticate {0}: {1} failed; serviceURL={2}, rc={3}/{4}, response={5}", accountName,
step, oAuthService, retcode, retmsg, httpResponse);
} }
return result;
} }
private Properties initHttpHeader() { private Properties initHttpHeader() {
Properties httpHeader = new Properties(); Properties httpHeader = new Properties();
httpHeader.setProperty(HEADER_USER_AGENT, OAUTH_USER_AGENT); httpHeader.setProperty(HttpHeader.ACCEPT.toString(), "*/*");
httpHeader.setProperty(HEADER_ACCEPT, "*/*"); httpHeader.setProperty(HttpHeader.ACCEPT_LANGUAGE.toString(), "en-US,en;q=0.9,de;q=0.8");
httpHeader.setProperty(HEADER_LANGUAGE, "de-de"); httpHeader.setProperty(HttpHeader.CACHE_CONTROL.toString(), "no-cache");
httpHeader.setProperty(HEADER_CACHE_CONTROL, "no-cache");
return httpHeader; return httpHeader;
} }
private void fillPostData(Request request, String data) {
if (!data.isEmpty()) {
StringContentProvider postData;
if (request.getHeaders().contains(HttpHeader.CONTENT_TYPE)) {
String contentType = request.getHeaders().get(HttpHeader.CONTENT_TYPE);
postData = new StringContentProvider(contentType, data, StandardCharsets.UTF_8);
} else {
boolean json = data.startsWith("{");
postData = new StringContentProvider(json ? "application/json" : "application/x-www-form-urlencoded",
data, StandardCharsets.UTF_8);
}
request.content(postData);
request.header(HttpHeader.CONTENT_LENGTH, Long.toString(postData.getLength()));
}
}
private String getString(@Nullable String value) { private String getString(@Nullable String value) {
return value != null ? value : ""; return value != null ? value : "";
} }
private String urlEncode(String url) {
try {
return URLEncoder.encode(url, UTF_8);
} catch (UnsupportedEncodingException e) {
logger.warn("OAuth: Unable to URL encode string {}", url, e);
return "";
}
}
} }

View File

@ -9,8 +9,8 @@ thing-type.magentatv.receiver.description = Media Receiver zum Epmfang von Magen
# Thing configuration # Thing configuration
thing-type.config.magentatv.receiver.ipAddress.label = IP-Adresse thing-type.config.magentatv.receiver.ipAddress.label = IP-Adresse
thing-type.config.magentatv.ipAddress.description = IP Adresse des Media Receivers thing-type.config.magentatv.ipAddress.description = IP Adresse des Media Receivers
thing-type.config.magentatv.receiver.userId.label = UID thing-type.config.magentatv.receiver.userId.label = User ID
thing-type.config.magentatv.receiver.userId.description = Technische Benutzerkennung (User ID), siehe Dokumentation; wird automatisch gefüllt, wenn Login-Name und Passwort angegeben sind. thing-type.config.magentatv.receiver.userId.description = Technische Benutzerkennung, siehe Dokumentation
thing-type.config.magentatv.receiver.accountName.label = Login-Name thing-type.config.magentatv.receiver.accountName.label = Login-Name
thing-type.config.magentatv.receiver.accountName.description = Login-Name (E-Mail) zur Anmeldung im Telekom Kundencenter thing-type.config.magentatv.receiver.accountName.description = Login-Name (E-Mail) zur Anmeldung im Telekom Kundencenter
thing-type.config.magentatv.receiver.accountPassword.label = Passwort thing-type.config.magentatv.receiver.accountPassword.label = Passwort

View File

@ -20,8 +20,8 @@
</channel-group> </channel-group>
</channel-groups> </channel-groups>
<representation-property>macAddress</representation-property> <representation-property>macAddress</representation-property>
<config-description uri="thing-type:magentatv:receiver"> <config-description uri="thing-type:magentatv:receiver">
<parameter name="ipAddress" type="text"> <parameter name="ipAddress" type="text">
<label>Device IP Address</label> <label>Device IP Address</label>
@ -40,7 +40,7 @@
</parameter> </parameter>
<parameter name="userId" type="text"> <parameter name="userId" type="text">
<label>User ID</label> <label>User ID</label>
<description>Technical User ID required for pairing process, auto-filled when account credentials are given</description> <description>Technical User ID required for pairing process</description>
</parameter> </parameter>
<parameter name="udn" type="text"> <parameter name="udn" type="text">
<label>Unique Device Name</label> <label>Unique Device Name</label>