2 * Copyright (c) 2010-2020 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.io.hueemulation.internal.rest;
15 import java.time.LocalDateTime;
16 import java.time.format.DateTimeFormatter;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.UUID;
21 import javax.ws.rs.DELETE;
22 import javax.ws.rs.GET;
23 import javax.ws.rs.POST;
24 import javax.ws.rs.Path;
25 import javax.ws.rs.PathParam;
26 import javax.ws.rs.Produces;
27 import javax.ws.rs.core.Context;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.Response;
30 import javax.ws.rs.core.UriInfo;
32 import org.eclipse.jdt.annotation.NonNullByDefault;
33 import org.openhab.core.common.registry.DefaultAbstractManagedProvider;
34 import org.openhab.core.storage.StorageService;
35 import org.openhab.io.hueemulation.internal.ConfigStore;
36 import org.openhab.io.hueemulation.internal.NetworkUtils;
37 import org.openhab.io.hueemulation.internal.dto.HueUserAuth;
38 import org.openhab.io.hueemulation.internal.dto.HueUserAuthWithSecrets;
39 import org.openhab.io.hueemulation.internal.dto.changerequest.HueCreateUser;
40 import org.openhab.io.hueemulation.internal.dto.response.HueResponse;
41 import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseCreateUser;
42 import org.osgi.service.component.annotations.Activate;
43 import org.osgi.service.component.annotations.Component;
44 import org.osgi.service.component.annotations.Reference;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import com.google.gson.reflect.TypeToken;
50 import io.swagger.annotations.ApiOperation;
51 import io.swagger.annotations.ApiParam;
52 import io.swagger.annotations.ApiResponse;
53 import io.swagger.annotations.ApiResponses;
56 * Manages users of this emulated HUE bridge. Stores users in the frameworks storage backend.
58 * This is an OSGi component. Usage:
62 * UserManagement userManagment;
65 * @author David Graeff - Initial contribution
67 @Component(immediate = false, service = { UserManagement.class }, property = "com.eclipsesource.jaxrs.publish=false")
70 @Produces(MediaType.APPLICATION_JSON)
71 public class UserManagement extends DefaultAbstractManagedProvider<HueUserAuthWithSecrets, String> {
72 private final Logger logger = LoggerFactory.getLogger(UserManagement.class);
74 protected final ConfigStore cs;
77 public UserManagement(final @Reference StorageService storageService, final @Reference ConfigStore cs) {
78 super(storageService);
81 for (HueUserAuthWithSecrets userAuth : getAll()) {
82 cs.ds.config.whitelist.put(userAuth.getUID(), userAuth);
87 * Checks if the username exists in the whitelist
89 @SuppressWarnings("null")
90 public boolean authorizeUser(String userName) {
91 HueUserAuth userAuth = cs.ds.config.whitelist.get(userName);
93 if (cs.ds.config.linkbutton && cs.ds.config.createNewUserOnEveryEndpoint) {
94 addUser(userName, userName, "On-the-go-user");
95 userAuth = cs.ds.config.whitelist.get(userName);
98 if (userAuth != null) {
99 userAuth.lastUseDate = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
100 update((HueUserAuthWithSecrets) userAuth);
103 return userAuth != null;
107 * Adds a user to the whitelist and persist the user file
109 * @param apiKey The hue "username" which is actually an API key
110 * @param clientKey The UDP/DTLS client key
111 * @param The user visible name
113 private void addUser(String apiKey, String clientKey, String label) {
114 if (cs.ds.config.whitelist.containsKey(apiKey)) {
117 logger.debug("APIKey {} added", apiKey);
118 String l[] = label.split("#");
119 HueUserAuthWithSecrets hueUserAuth = new HueUserAuthWithSecrets(l[0], l.length == 2 ? l[1] : "openhab", apiKey,
121 cs.ds.config.whitelist.put(apiKey, hueUserAuth);
125 @SuppressWarnings("null")
126 private synchronized void removeUser(String apiKey) {
127 HueUserAuth userAuth = cs.ds.config.whitelist.remove(apiKey);
128 if (userAuth != null) {
129 logger.debug("APIKey {} removed", apiKey);
135 protected String getStorageName() {
136 return "hueEmulationUsers";
140 protected String keyToString(String key) {
145 public Response illegalGetUserAccessApi(@Context UriInfo uri) {
146 return NetworkUtils.singleError(cs.gson, uri, HueResponse.METHOD_NOT_ALLOWED, "Not Authorized");
150 @ApiOperation(value = "Create an API Key")
151 @ApiResponses(value = { @ApiResponse(code = 200, message = "API Key created"),
152 @ApiResponse(code = 403, message = "Link button not pressed") })
153 public Response createNewUser(@Context UriInfo uri, String body) {
154 if (!cs.ds.config.linkbutton) {
155 return NetworkUtils.singleError(cs.gson, uri, HueResponse.LINK_BUTTON_NOT_PRESSED,
156 "link button not pressed");
159 final HueCreateUser userRequest;
160 userRequest = cs.gson.fromJson(body, HueCreateUser.class);
161 if (userRequest.devicetype.isEmpty()) {
162 return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON,
163 "Invalid request: No devicetype set");
166 String apiKey = UUID.randomUUID().toString();
167 String clientKey = UUID.randomUUID().toString();
168 addUser(apiKey, clientKey, userRequest.devicetype);
169 HueSuccessResponseCreateUser h = new HueSuccessResponseCreateUser(apiKey, clientKey);
170 String result = cs.gson.toJson(Collections.singleton(new HueResponse(h)), new TypeToken<List<?>>() {
173 return Response.ok(result).build();
177 @Path("{username}/config/whitelist/{userid}")
178 @ApiOperation(value = "Return a user")
179 @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
180 public Response getUserApi(@Context UriInfo uri,
181 @PathParam("username") @ApiParam(value = "username") String username,
182 @PathParam("userid") @ApiParam(value = "User ID") String userid) {
183 if (!authorizeUser(username)) {
184 return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
186 return Response.ok(cs.gson.toJson(cs.ds.config.whitelist.get(userid))).build();
190 @Path("{username}/config/whitelist")
191 @ApiOperation(value = "Return all users")
192 @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
193 public Response getAllUsersApi(@Context UriInfo uri,
194 @PathParam("username") @ApiParam(value = "username") String username) {
195 if (!authorizeUser(username)) {
196 return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
198 return Response.ok(cs.gson.toJson(cs.ds.config.whitelist)).build();
202 @Path("{username}/config/whitelist/{id}")
203 @ApiOperation(value = "Deletes a user")
204 @ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"),
205 @ApiResponse(code = 403, message = "Access denied") })
206 public Response removeUserApi(@Context UriInfo uri,
207 @PathParam("username") @ApiParam(value = "username") String username,
208 @PathParam("id") @ApiParam(value = "User to remove") String id) {
209 if (!authorizeUser(username)) {
210 return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
212 if (!username.equals(id)) {
213 return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED,
214 "You can only remove yourself not someone else!");
217 removeUser(username);
219 return NetworkUtils.singleSuccess(cs.gson, "/config/whitelist/" + username + " deleted.");