]> git.basschouten.com Git - openhab-addons.git/blob
b10d886cfc224ee672df2aadd64a98ad6ea9de8b
[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.mercedesme.internal.server;
14
15 import java.io.IOException;
16 import java.util.HashMap;
17 import java.util.Map;
18 import java.util.Optional;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jetty.client.HttpClient;
22 import org.eclipse.jetty.server.Connector;
23 import org.eclipse.jetty.server.Server;
24 import org.eclipse.jetty.server.ServerConnector;
25 import org.eclipse.jetty.servlet.ServletHandler;
26 import org.openhab.binding.mercedesme.internal.Constants;
27 import org.openhab.binding.mercedesme.internal.config.AccountConfiguration;
28 import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
29 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
30 import org.openhab.core.auth.client.oauth2.OAuthClientService;
31 import org.openhab.core.auth.client.oauth2.OAuthException;
32 import org.openhab.core.auth.client.oauth2.OAuthFactory;
33 import org.openhab.core.auth.client.oauth2.OAuthResponseException;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * The {@link CallbackServer} class defines an HTTP Server for authentication callbacks
39  *
40  * @author Bernd Weymann - Initial contribution
41  */
42 @NonNullByDefault
43 public class CallbackServer {
44     private static final Logger LOGGER = LoggerFactory.getLogger(CallbackServer.class);
45     private static final Map<Integer, OAuthClientService> AUTH_MAP = new HashMap<Integer, OAuthClientService>();
46     private static final Map<Integer, CallbackServer> SERVER_MAP = new HashMap<Integer, CallbackServer>();
47     private static final AccessTokenResponse INVALID_ACCESS_TOKEN = new AccessTokenResponse();
48
49     private Optional<Server> server = Optional.empty();
50     private AccessTokenRefreshListener listener;
51     private AccountConfiguration config;
52     private OAuthClientService oacs;
53     private String callbackUrl;
54
55     public CallbackServer(AccessTokenRefreshListener l, HttpClient hc, OAuthFactory oAuthFactory,
56             AccountConfiguration config, String callbackUrl) {
57         oacs = oAuthFactory.createOAuthClientService(config.clientId, Constants.MB_TOKEN_URL, Constants.MB_AUTH_URL,
58                 config.clientId, config.clientSecret, config.getScope(), false);
59         listener = l;
60         AUTH_MAP.put(Integer.valueOf(config.callbackPort), oacs);
61         SERVER_MAP.put(Integer.valueOf(config.callbackPort), this);
62         this.config = config;
63         this.callbackUrl = callbackUrl;
64         INVALID_ACCESS_TOKEN.setAccessToken(Constants.EMPTY);
65     }
66
67     public String getAuthorizationUrl() {
68         try {
69             return oacs.getAuthorizationUrl(callbackUrl, null, null);
70         } catch (OAuthException e) {
71             LOGGER.warn("Error creating Authorization URL {}", e.getMessage());
72             return Constants.EMPTY;
73         }
74     }
75
76     public String getScope() {
77         return config.getScope();
78     }
79
80     public boolean start() {
81         LOGGER.debug("Start Callback Server for port {}", config.callbackPort);
82         if (!server.isEmpty()) {
83             LOGGER.debug("Callback server for port {} already started", config.callbackPort);
84             return true;
85         }
86         server = Optional.of(new Server());
87         ServerConnector connector = new ServerConnector(server.get());
88         connector.setPort(config.callbackPort);
89         server.get().setConnectors(new Connector[] { connector });
90         ServletHandler servletHandler = new ServletHandler();
91         server.get().setHandler(servletHandler);
92         servletHandler.addServletWithMapping(CallbackServlet.class, Constants.CALLBACK_ENDPOINT);
93         try {
94             server.get().start();
95         } catch (Exception e) {
96             LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
97             return false;
98         }
99         return true;
100     }
101
102     public void stop() {
103         LOGGER.debug("Stop Callback Server");
104         try {
105             if (!server.isEmpty()) {
106                 server.get().stop();
107                 server = Optional.empty();
108             }
109         } catch (Exception e) {
110             LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
111         }
112     }
113
114     public String getToken() {
115         AccessTokenResponse atr = null;
116         try {
117             /*
118              * this will automatically trigger
119              * - return last stored token if it's still valid
120              * - refreshToken if current token is expired
121              * - inform listeners if refresh delivered new token
122              * - store new token in persistence
123              */
124             atr = oacs.getAccessTokenResponse();
125         } catch (OAuthException | IOException | OAuthResponseException e) {
126             LOGGER.warn("Exception getting token {}", e.getMessage());
127         }
128         if (atr == null) {
129             LOGGER.debug("Token empty - Manual Authorization needed at {}", callbackUrl);
130             listener.onAccessTokenResponse(INVALID_ACCESS_TOKEN);
131             return INVALID_ACCESS_TOKEN.getAccessToken();
132         }
133         listener.onAccessTokenResponse(atr);
134         return atr.getAccessToken();
135     }
136
137     /**
138      * Static callback for Servlet calls
139      *
140      * @param port
141      * @param code
142      */
143     public static void callback(int port, String code) {
144         LOGGER.trace("Callback from Servlet {} {}", port, code);
145         try {
146             OAuthClientService oacs = AUTH_MAP.get(port);
147             LOGGER.trace("Get token from code {}", code);
148             // get CallbackServer instance
149             CallbackServer srv = SERVER_MAP.get(port);
150             LOGGER.trace("Deliver token to {}", srv);
151             if (srv != null && oacs != null) {
152                 // token stored and persisted inside oacs
153                 AccessTokenResponse atr = oacs.getAccessTokenResponseByAuthorizationCode(code, srv.callbackUrl);
154                 // inform listener - not done by oacs
155                 srv.listener.onAccessTokenResponse(atr);
156             } else {
157                 LOGGER.warn("Either Callbackserver  {} or Authorization Service {} not found", srv, oacs);
158             }
159         } catch (OAuthException | IOException | OAuthResponseException e) {
160             LOGGER.warn("Exception getting token from code {} {}", code, e.getMessage());
161         }
162     }
163
164     public static String getAuthorizationUrl(int port) {
165         CallbackServer srv = SERVER_MAP.get(port);
166         if (srv != null) {
167             return srv.getAuthorizationUrl();
168         } else {
169             LOGGER.debug("No Callbackserver found for {}", port);
170             return Constants.EMPTY;
171         }
172     }
173
174     public static String getScope(int port) {
175         CallbackServer srv = SERVER_MAP.get(port);
176         if (srv != null) {
177             return srv.getScope();
178         } else {
179             LOGGER.debug("No Callbackserver found for {}", port);
180             return Constants.EMPTY;
181         }
182     }
183 }