]> git.basschouten.com Git - openhab-addons.git/blob
1bfabe66a14e3817eeffb2adf3d32687c4c59779
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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 final OAuthFactory oAuthFactory;
50
51     private Optional<Server> server = Optional.empty();
52     private AccessTokenRefreshListener listener;
53     private AccountConfiguration config;
54     private OAuthClientService oacs;
55     private String callbackUrl;
56
57     public CallbackServer(AccessTokenRefreshListener l, HttpClient hc, OAuthFactory oAuthFactory,
58             AccountConfiguration config, String callbackUrl) {
59         this.oAuthFactory = oAuthFactory;
60         oacs = oAuthFactory.createOAuthClientService(config.clientId, Constants.MB_TOKEN_URL, Constants.MB_AUTH_URL,
61                 config.clientId, config.clientSecret, config.getScope(), false);
62         listener = l;
63         AUTH_MAP.put(Integer.valueOf(config.callbackPort), oacs);
64         SERVER_MAP.put(Integer.valueOf(config.callbackPort), this);
65         this.config = config;
66         this.callbackUrl = callbackUrl;
67         INVALID_ACCESS_TOKEN.setAccessToken(Constants.EMPTY);
68     }
69
70     public void dispose() {
71         oAuthFactory.ungetOAuthService(config.clientId);
72         AUTH_MAP.remove(Integer.valueOf(config.callbackPort));
73         SERVER_MAP.remove(Integer.valueOf(config.callbackPort));
74     }
75
76     public void deleteOAuthServiceAndAccessToken() {
77         oAuthFactory.deleteServiceAndAccessToken(config.clientId);
78     }
79
80     public String getAuthorizationUrl() {
81         try {
82             return oacs.getAuthorizationUrl(callbackUrl, null, null);
83         } catch (OAuthException e) {
84             LOGGER.warn("Error creating Authorization URL {}", e.getMessage());
85             return Constants.EMPTY;
86         }
87     }
88
89     public String getScope() {
90         return config.getScope();
91     }
92
93     public boolean start() {
94         LOGGER.debug("Start Callback Server for port {}", config.callbackPort);
95         if (server.isPresent()) {
96             LOGGER.debug("Callback server for port {} already started", config.callbackPort);
97             return true;
98         }
99         server = Optional.of(new Server());
100         ServerConnector connector = new ServerConnector(server.get());
101         connector.setPort(config.callbackPort);
102         server.get().setConnectors(new Connector[] { connector });
103         ServletHandler servletHandler = new ServletHandler();
104         server.get().setHandler(servletHandler);
105         servletHandler.addServletWithMapping(CallbackServlet.class, Constants.CALLBACK_ENDPOINT);
106         try {
107             server.get().start();
108         } catch (Exception e) {
109             LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
110             return false;
111         }
112         return true;
113     }
114
115     public void stop() {
116         LOGGER.debug("Stop Callback Server");
117         try {
118             if (server.isPresent()) {
119                 server.get().stop();
120                 server = Optional.empty();
121             }
122         } catch (Exception e) {
123             LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
124         }
125     }
126
127     public String getToken() {
128         AccessTokenResponse atr = null;
129         try {
130             /*
131              * this will automatically trigger
132              * - return last stored token if it's still valid
133              * - refreshToken if current token is expired
134              * - inform listeners if refresh delivered new token
135              * - store new token in persistence
136              */
137             atr = oacs.getAccessTokenResponse();
138         } catch (OAuthException | IOException | OAuthResponseException e) {
139             LOGGER.warn("Exception getting token {}", e.getMessage());
140         }
141         if (atr == null) {
142             LOGGER.debug("Token empty - Manual Authorization needed at {}", callbackUrl);
143             listener.onAccessTokenResponse(INVALID_ACCESS_TOKEN);
144             return INVALID_ACCESS_TOKEN.getAccessToken();
145         }
146         listener.onAccessTokenResponse(atr);
147         return atr.getAccessToken();
148     }
149
150     /**
151      * Static callback for Servlet calls
152      *
153      * @param port
154      * @param code
155      */
156     public static void callback(int port, String code) {
157         LOGGER.trace("Callback from Servlet {} {}", port, code);
158         try {
159             OAuthClientService oacs = AUTH_MAP.get(port);
160             LOGGER.trace("Get token from code {}", code);
161             // get CallbackServer instance
162             CallbackServer srv = SERVER_MAP.get(port);
163             LOGGER.trace("Deliver token to {}", srv);
164             if (srv != null && oacs != null) {
165                 // token stored and persisted inside oacs
166                 AccessTokenResponse atr = oacs.getAccessTokenResponseByAuthorizationCode(code, srv.callbackUrl);
167                 // inform listener - not done by oacs
168                 srv.listener.onAccessTokenResponse(atr);
169             } else {
170                 LOGGER.warn("Either Callbackserver  {} or Authorization Service {} not found", srv, oacs);
171             }
172         } catch (OAuthException | IOException | OAuthResponseException e) {
173             LOGGER.warn("Exception getting token from code {} {}", code, e.getMessage());
174         }
175     }
176
177     public static String getAuthorizationUrl(int port) {
178         CallbackServer srv = SERVER_MAP.get(port);
179         if (srv != null) {
180             return srv.getAuthorizationUrl();
181         } else {
182             LOGGER.debug("No Callbackserver found for {}", port);
183             return Constants.EMPTY;
184         }
185     }
186
187     public static String getScope(int port) {
188         CallbackServer srv = SERVER_MAP.get(port);
189         if (srv != null) {
190             return srv.getScope();
191         } else {
192             LOGGER.debug("No Callbackserver found for {}", port);
193             return Constants.EMPTY;
194         }
195     }
196 }