2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.zoneminder.internal.handler;
15 import java.io.UnsupportedEncodingException;
16 import java.net.URLEncoder;
17 import java.nio.charset.StandardCharsets;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.zoneminder.internal.dto.AuthResponseDTO;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
25 import com.google.gson.Gson;
28 * The {@link ZmAuth} manages the authentication process when Zoneminder
29 * authentication is enabled. This class requests access and refresh tokens based
30 * on the expiration times provided by the Zoneminder server.
32 * @author Mark Hilbush - Initial contribution
37 private final Logger logger = LoggerFactory.getLogger(ZmAuth.class);
39 private final ZmBridgeHandler bridgeHandler;
40 private final String authContent;
41 private final boolean usingAuthorization;
42 private boolean isAuthorized;
44 private @Nullable String refreshToken;
45 private long refreshTokenExpiresAt;
46 private @Nullable String accessToken;
47 private long accessTokenExpiresAt;
49 public ZmAuth(ZmBridgeHandler handler) {
50 this(handler, null, null);
53 public ZmAuth(ZmBridgeHandler handler, @Nullable String user, @Nullable String pass) {
54 this.bridgeHandler = handler;
55 if (user == null || pass == null) {
56 logger.debug("ZmAuth: Authorization is disabled");
57 usingAuthorization = false;
61 logger.debug("ZmAuth: Authorization is enabled");
62 usingAuthorization = true;
64 String encodedUser = null;
65 String encodedPass = null;
67 encodedUser = URLEncoder.encode(user, StandardCharsets.UTF_8.name());
68 encodedPass = URLEncoder.encode(pass, StandardCharsets.UTF_8.name());
69 } catch (UnsupportedEncodingException e) {
70 logger.warn("ZmAuth: Unable to encode user name and password");
72 authContent = encodedUser == null ? ""
73 : String.format("user=%s&pass=%s&stateful=1", encodedUser, encodedPass);
77 public String getAccessToken() {
78 String localAccessToken = accessToken;
79 return localAccessToken != null ? localAccessToken : "";
82 public boolean usingAuthorization() {
83 return usingAuthorization;
86 public boolean isAuthorized() {
87 if (usingAuthorization()) {
93 private void checkTokens() {
94 if (isExpired(refreshTokenExpiresAt)) {
96 } else if (isExpired(accessTokenExpiresAt)) {
101 @SuppressWarnings("null")
102 private synchronized void getNewRefreshToken() {
103 // First check to see if another thread has updated it
104 if (!isExpired(refreshTokenExpiresAt)) {
107 String url = bridgeHandler.buildLoginUrl();
108 logger.debug("ZmAuth: Update expired REFRESH token using url '{}'", url);
109 String response = bridgeHandler.executePost(url, authContent, "application/x-www-form-urlencoded");
110 if (response != null) {
111 Gson gson = bridgeHandler.getGson();
112 AuthResponseDTO auth = gson.fromJson(response, AuthResponseDTO.class);
113 if (auth != null && auth.exception == null && auth.refreshToken != null && auth.accessToken != null) {
114 updateRefreshToken(auth);
115 updateAccessToken(auth);
120 isAuthorized = false;
123 @SuppressWarnings("null")
124 private synchronized void getNewAccessToken() {
125 // First check to see if another thread has updated it
126 if (!isExpired(accessTokenExpiresAt)) {
129 String url = bridgeHandler.buildLoginUrl(String.format("?token=%s", refreshToken));
130 logger.debug("ZmAuth: Update expired ACCESS token using url '{}'", url);
131 String response = bridgeHandler.executeGet(url);
132 if (response != null) {
133 Gson gson = bridgeHandler.getGson();
134 AuthResponseDTO auth = gson.fromJson(response, AuthResponseDTO.class);
135 if (auth != null && auth.exception == null && auth.accessToken != null) {
136 updateAccessToken(auth);
141 isAuthorized = false;
144 private void updateAccessToken(AuthResponseDTO auth) {
145 accessToken = auth.accessToken;
146 accessTokenExpiresAt = getExpiresAt(auth.accessTokenExpires);
147 logger.trace("ZmAuth: New access token: {}", accessToken);
148 logger.trace("ZmAuth: New access token expires in {} sec", getExpiresIn(accessTokenExpiresAt));
151 private void updateRefreshToken(AuthResponseDTO auth) {
152 refreshToken = auth.refreshToken;
153 refreshTokenExpiresAt = getExpiresAt(auth.refreshTokenExpires);
154 logger.trace("ZmAuth: New refresh token: {}", refreshToken);
155 logger.trace("ZmAuth: New refresh token expires in {} sec", getExpiresIn(refreshTokenExpiresAt));
158 private boolean isExpired(long expiresAt) {
159 return (System.currentTimeMillis() / 1000) > expiresAt;
162 private long getExpiresAt(String expiresInSeconds) {
164 return (System.currentTimeMillis() / 1000) + (Integer.parseInt(expiresInSeconds) - 300);
165 } catch (NumberFormatException e) {
170 private long getExpiresIn(long expiresAtSeconds) {
171 return expiresAtSeconds - (System.currentTimeMillis() / 1000);