blob: 9a016cfaa845eddd81ac7ceca845c2a14b7fdf31 [file] [log] [blame]
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.ui.impl;
17
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuska0af26912016-03-21 21:37:30 -070019import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ArrayNode;
21import com.fasterxml.jackson.databind.node.BooleanNode;
22import com.fasterxml.jackson.databind.node.DoubleNode;
23import com.fasterxml.jackson.databind.node.IntNode;
24import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25import com.fasterxml.jackson.databind.node.LongNode;
26import com.fasterxml.jackson.databind.node.NullNode;
27import com.fasterxml.jackson.databind.node.ObjectNode;
28import com.fasterxml.jackson.databind.node.ShortNode;
29import com.fasterxml.jackson.databind.node.TextNode;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080030import com.google.common.collect.ImmutableList;
Thomas Vachuska0af26912016-03-21 21:37:30 -070031import com.google.common.collect.ImmutableMap;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070032import com.google.common.collect.ImmutableSet;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080033import com.google.common.collect.Lists;
34import com.google.common.collect.Maps;
35import org.apache.felix.scr.annotations.Activate;
36import org.apache.felix.scr.annotations.Component;
37import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska51f540f2015-05-27 17:26:57 -070038import org.apache.felix.scr.annotations.Reference;
39import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080040import org.apache.felix.scr.annotations.Service;
Madan Jampanibf8ee802016-05-04 14:07:36 -070041import org.onlab.util.Tools;
Thomas Vachuska51f540f2015-05-27 17:26:57 -070042import org.onosproject.mastership.MastershipService;
Thomas Vachuska0af26912016-03-21 21:37:30 -070043import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani7b93ceb2016-05-04 09:58:40 -070044import org.onosproject.store.service.ConsistentMap;
45import org.onosproject.store.service.MapEvent;
46import org.onosproject.store.service.MapEventListener;
47import org.onosproject.store.service.Serializer;
Thomas Vachuska0af26912016-03-21 21:37:30 -070048import org.onosproject.store.service.StorageService;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080049import org.onosproject.ui.UiExtension;
50import org.onosproject.ui.UiExtensionService;
Thomas Vachuska3553b302015-03-07 14:49:43 -080051import org.onosproject.ui.UiMessageHandlerFactory;
Thomas Vachuska0af26912016-03-21 21:37:30 -070052import org.onosproject.ui.UiPreferencesService;
Simon Hunt1169c952017-06-05 11:20:11 -070053import org.onosproject.ui.UiSessionToken;
54import org.onosproject.ui.UiTokenService;
Simon Hunt6cc86452017-04-27 17:46:22 -070055import org.onosproject.ui.UiTopo2OverlayFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070056import org.onosproject.ui.UiTopoMap;
Steven Burrows3a9a6442016-05-05 15:31:16 +010057import org.onosproject.ui.UiTopoMapFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070058import org.onosproject.ui.UiTopoOverlayFactory;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080059import org.onosproject.ui.UiView;
Simon Hunt1002cd82015-04-23 14:44:03 -070060import org.onosproject.ui.UiViewHidden;
Simon Hunte6f64612017-04-28 00:01:48 -070061import org.onosproject.ui.impl.topo.Topo2TrafficMessageHandler;
Simon Huntd5b96732016-07-08 13:22:27 -070062import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
Simon Hunt6cc86452017-04-27 17:46:22 -070063import org.onosproject.ui.impl.topo.Traffic2Overlay;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080064import org.slf4j.Logger;
65import org.slf4j.LoggerFactory;
66
Simon Hunt1169c952017-06-05 11:20:11 -070067import java.math.BigInteger;
68import java.security.SecureRandom;
Thomas Vachuska0af26912016-03-21 21:37:30 -070069import java.util.LinkedHashMap;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080070import java.util.List;
71import java.util.Map;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070072import java.util.Set;
Madan Jampanibf8ee802016-05-04 14:07:36 -070073import java.util.concurrent.ExecutorService;
74import java.util.concurrent.Executors;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080075
76import static com.google.common.collect.ImmutableList.of;
77import static java.util.stream.Collectors.toSet;
Heedo Kang4a47a302016-02-29 17:40:23 +090078import static org.onosproject.security.AppGuard.checkPermission;
79import static org.onosproject.security.AppPermission.Type.UI_READ;
80import static org.onosproject.security.AppPermission.Type.UI_WRITE;
Thomas Vachuska0af26912016-03-21 21:37:30 -070081import static org.onosproject.ui.UiView.Category.NETWORK;
82import static org.onosproject.ui.UiView.Category.PLATFORM;
Heedo Kang4a47a302016-02-29 17:40:23 +090083
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080084/**
85 * Manages the user interface extensions.
86 */
87@Component(immediate = true)
88@Service
Simon Hunt3678c2a2016-03-28 14:48:07 -070089public class UiExtensionManager
Simon Hunt1169c952017-06-05 11:20:11 -070090 implements UiExtensionService, UiPreferencesService, SpriteService,
91 UiTokenService {
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080092
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070093 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
Simon Hunt3678c2a2016-03-28 14:48:07 -070094
Madan Jampani7b93ceb2016-05-04 09:58:40 -070095 private static final String ONOS_USER_PREFERENCES = "onos-ui-user-preferences";
Simon Hunt1169c952017-06-05 11:20:11 -070096 private static final String ONOS_SESSION_TOKENS = "onos-ui-session-tokens";
Simon Hunte05cae42015-07-23 17:35:24 -070097 private static final String CORE = "core";
Thomas Vachuskafa74dd72016-03-20 19:11:12 -070098 private static final String GUI_ADDED = "guiAdded";
99 private static final String GUI_REMOVED = "guiRemoved";
Thomas Vachuska0af26912016-03-21 21:37:30 -0700100 private static final String UPDATE_PREFS = "updatePrefs";
Simon Hunt3678c2a2016-03-28 14:48:07 -0700101 private static final String SLASH = "/";
102
103 private static final int IDX_USER = 0;
104 private static final int IDX_KEY = 1;
Simon Hunte05cae42015-07-23 17:35:24 -0700105
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800106 private final Logger log = LoggerFactory.getLogger(getClass());
107
108 // List of all extensions
109 private final List<UiExtension> extensions = Lists.newArrayList();
110
111 // Map of views to extensions
112 private final Map<String, UiExtension> views = Maps.newHashMap();
113
114 // Core views & core extension
Thomas Vachuska3553b302015-03-07 14:49:43 -0800115 private final UiExtension core = createCoreExtension();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800116
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected MastershipService mastershipService;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800119
Thomas Vachuska0af26912016-03-21 21:37:30 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected StorageService storageService;
122
123 // User preferences
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700124 private ConsistentMap<String, ObjectNode> prefsConsistentMap;
125 private Map<String, ObjectNode> prefs;
126 private final MapEventListener<String, ObjectNode> prefsListener =
Thomas Vachuska0af26912016-03-21 21:37:30 -0700127 new InternalPrefsListener();
128
Simon Hunt1169c952017-06-05 11:20:11 -0700129 // Session tokens
130 private ConsistentMap<UiSessionToken, String> tokensConsistentMap;
131 private Map<UiSessionToken, String> tokens;
132 private final SessionTokenGenerator tokenGen =
133 new SessionTokenGenerator();
134
Thomas Vachuska0af26912016-03-21 21:37:30 -0700135 private final ObjectMapper mapper = new ObjectMapper();
136
Madan Jampanibf8ee802016-05-04 14:07:36 -0700137 private final ExecutorService eventHandlingExecutor =
Simon Huntd5b96732016-07-08 13:22:27 -0700138 Executors.newSingleThreadExecutor(
139 Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
Madan Jampanibf8ee802016-05-04 14:07:36 -0700140
Thomas Vachuska3553b302015-03-07 14:49:43 -0800141 // Creates core UI extension
Simon Huntc54cd1b2015-05-11 13:43:44 -0700142 private UiExtension createCoreExtension() {
Simon Hunt20e16792015-04-24 14:29:39 -0700143 List<UiView> coreViews = of(
144 new UiView(PLATFORM, "app", "Applications", "nav_apps"),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700145 new UiView(PLATFORM, "settings", "Settings", "nav_settings"),
Simon Hunt20e16792015-04-24 14:29:39 -0700146 new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700147 new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"),
chengfanc5a99dc2017-01-08 19:28:29 +0800148 new UiView(PLATFORM, "partition", "Partitions", "nav_partitions"),
Simon Hunt20e16792015-04-24 14:29:39 -0700149 new UiView(NETWORK, "topo", "Topology", "nav_topo"),
Simon Hunt47f8fb72017-04-11 13:49:31 -0700150 new UiView(NETWORK, "topo2", "Topology 2", "nav_topo2"),
Simon Hunt20e16792015-04-24 14:29:39 -0700151 new UiView(NETWORK, "device", "Devices", "nav_devs"),
152 new UiViewHidden("flow"),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700153 new UiViewHidden("port"),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700154 new UiViewHidden("group"),
Jian Li1f544732015-12-30 23:36:37 -0800155 new UiViewHidden("meter"),
Simon Hunt20e16792015-04-24 14:29:39 -0700156 new UiView(NETWORK, "link", "Links", "nav_links"),
157 new UiView(NETWORK, "host", "Hosts", "nav_hosts"),
chengfanb466a7e2015-08-21 09:59:29 -0500158 new UiView(NETWORK, "intent", "Intents", "nav_intents"),
Simon Hunt986b92f2016-06-03 15:46:59 -0700159 new UiView(NETWORK, "tunnel", "Tunnels", "nav_tunnels")
Simon Hunt20e16792015-04-24 14:29:39 -0700160 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700161
Simon Hunt4c7edd32015-03-11 10:42:53 -0700162 UiMessageHandlerFactory messageHandlerFactory =
163 () -> ImmutableList.of(
Thomas Vachuska0af26912016-03-21 21:37:30 -0700164 new UserPreferencesMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700165 new TopologyViewMessageHandler(),
Simon Huntd5b96732016-07-08 13:22:27 -0700166 new Topo2ViewMessageHandler(),
Simon Hunte6f64612017-04-28 00:01:48 -0700167 new Topo2TrafficMessageHandler(),
Thomas Vachuska26be4f32016-03-31 01:10:27 -0700168 new MapSelectorMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700169 new DeviceViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700170 new LinkViewMessageHandler(),
Thomas Vachuska0fa583c2015-03-30 23:07:41 -0700171 new HostViewMessageHandler(),
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700172 new FlowViewMessageHandler(),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700173 new PortViewMessageHandler(),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700174 new GroupViewMessageHandler(),
Jian Li1f544732015-12-30 23:36:37 -0800175 new MeterViewMessageHandler(),
Bri Prebilic Cole384e8dc2015-04-13 15:51:14 -0700176 new IntentViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700177 new ApplicationViewMessageHandler(),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700178 new SettingsViewMessageHandler(),
chengfanb466a7e2015-08-21 09:59:29 -0500179 new ClusterViewMessageHandler(),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700180 new ProcessorViewMessageHandler(),
chengfanc5a99dc2017-01-08 19:28:29 +0800181 new TunnelViewMessageHandler(),
182 new PartitionViewMessageHandler()
Simon Hunt4c7edd32015-03-11 10:42:53 -0700183 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700184
Simon Hunte05cae42015-07-23 17:35:24 -0700185 UiTopoOverlayFactory topoOverlayFactory =
186 () -> ImmutableList.of(
Andrea Campanella732ea832017-02-06 09:25:59 -0800187 new TrafficOverlay(),
188 new ProtectedIntentOverlay()
Simon Hunte05cae42015-07-23 17:35:24 -0700189 );
190
Simon Hunt6cc86452017-04-27 17:46:22 -0700191 UiTopo2OverlayFactory topo2OverlayFactory =
192 () -> ImmutableList.of(
193 new Traffic2Overlay()
194 );
195
Steven Burrows3a9a6442016-05-05 15:31:16 +0100196 UiTopoMapFactory topoMapFactory =
197 () -> ImmutableList.of(
198 new UiTopoMap("australia", "Australia", "*australia", 1.0),
199 new UiTopoMap("americas", "North, Central and South America", "*americas", 0.7),
200 new UiTopoMap("n_america", "North America", "*n_america", 0.9),
201 new UiTopoMap("s_america", "South America", "*s_america", 0.9),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700202 new UiTopoMap("usa", "United States", "*continental_us", 1.3),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100203 new UiTopoMap("bayareaGEO", "Bay Area, California", "*bayarea", 1.0),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700204 new UiTopoMap("europe", "Europe", "*europe", 10.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100205 new UiTopoMap("italy", "Italy", "*italy", 0.8),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700206 new UiTopoMap("uk", "United Kingdom and Ireland", "*uk", 2.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100207 new UiTopoMap("japan", "Japan", "*japan", 0.8),
208 new UiTopoMap("s_korea", "South Korea", "*s_korea", 0.75),
209 new UiTopoMap("taiwan", "Taiwan", "*taiwan", 0.7),
210 new UiTopoMap("africa", "Africa", "*africa", 0.7),
211 new UiTopoMap("oceania", "Oceania", "*oceania", 0.7),
212 new UiTopoMap("asia", "Asia", "*asia", 0.7)
213 );
214
Simon Hunte05cae42015-07-23 17:35:24 -0700215 return new UiExtension.Builder(CL, coreViews)
216 .messageHandlerFactory(messageHandlerFactory)
217 .topoOverlayFactory(topoOverlayFactory)
Simon Hunt6cc86452017-04-27 17:46:22 -0700218 .topo2OverlayFactory(topo2OverlayFactory)
Steven Burrows3a9a6442016-05-05 15:31:16 +0100219 .topoMapFactory(topoMapFactory)
Simon Hunte05cae42015-07-23 17:35:24 -0700220 .resourcePath(CORE)
221 .build();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800222 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800223
224 @Activate
225 public void activate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700226 Serializer serializer = Serializer.using(KryoNamespaces.API,
Simon Hunt6cc86452017-04-27 17:46:22 -0700227 ObjectNode.class, ArrayNode.class,
228 JsonNodeFactory.class, LinkedHashMap.class,
229 TextNode.class, BooleanNode.class,
230 LongNode.class, DoubleNode.class, ShortNode.class,
Simon Hunt1169c952017-06-05 11:20:11 -0700231 IntNode.class, NullNode.class, UiSessionToken.class);
Thomas Vachuska0af26912016-03-21 21:37:30 -0700232
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700233 prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700234 .withName(ONOS_USER_PREFERENCES)
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700235 .withSerializer(serializer)
236 .withRelaxedReadConsistency()
Thomas Vachuska0af26912016-03-21 21:37:30 -0700237 .build();
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700238 prefsConsistentMap.addListener(prefsListener);
239 prefs = prefsConsistentMap.asJavaMap();
Simon Hunt1169c952017-06-05 11:20:11 -0700240
241 tokensConsistentMap = storageService.<UiSessionToken, String>consistentMapBuilder()
242 .withName(ONOS_SESSION_TOKENS)
243 .withSerializer(serializer)
244 .withRelaxedReadConsistency()
245 .build();
246 tokens = tokensConsistentMap.asJavaMap();
247
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800248 register(core);
249 log.info("Started");
250 }
251
252 @Deactivate
253 public void deactivate() {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700254 prefsConsistentMap.removeListener(prefsListener);
Madan Jampanibf8ee802016-05-04 14:07:36 -0700255 eventHandlingExecutor.shutdown();
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700256 UiWebSocketServlet.closeAll();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800257 unregister(core);
258 log.info("Stopped");
259 }
260
261 @Override
262 public synchronized void register(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900263 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800264 if (!extensions.contains(extension)) {
265 extensions.add(extension);
266 for (UiView view : extension.views()) {
267 views.put(view.id(), extension);
268 }
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700269 UiWebSocketServlet.sendToAll(GUI_ADDED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800270 }
271 }
272
273 @Override
274 public synchronized void unregister(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900275 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800276 extensions.remove(extension);
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700277 extension.views().stream().map(UiView::id).collect(toSet()).forEach(views::remove);
278 UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800279 }
280
281 @Override
282 public synchronized List<UiExtension> getExtensions() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900283 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800284 return ImmutableList.copyOf(extensions);
285 }
286
287 @Override
288 public synchronized UiExtension getViewExtension(String viewId) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900289 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800290 return views.get(viewId);
291 }
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700292
Thomas Vachuska0af26912016-03-21 21:37:30 -0700293 @Override
294 public Set<String> getUserNames() {
295 ImmutableSet.Builder<String> builder = ImmutableSet.builder();
296 prefs.keySet().forEach(k -> builder.add(userName(k)));
297 return builder.build();
298 }
299
300 @Override
301 public Map<String, ObjectNode> getPreferences(String userName) {
302 ImmutableMap.Builder<String, ObjectNode> builder = ImmutableMap.builder();
303 prefs.entrySet().stream()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700304 .filter(e -> e.getKey().startsWith(userName + SLASH))
Thomas Vachuska0af26912016-03-21 21:37:30 -0700305 .forEach(e -> builder.put(keyName(e.getKey()), e.getValue()));
306 return builder.build();
307 }
308
309 @Override
310 public void setPreference(String userName, String preference, ObjectNode value) {
311 prefs.put(key(userName, preference), value);
312 }
313
Simon Huntc54cd1b2015-05-11 13:43:44 -0700314 // =====================================================================
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700315 // Provisional tracking of sprite definitions
Simon Huntc54cd1b2015-05-11 13:43:44 -0700316
317 private final Map<String, JsonNode> sprites = Maps.newHashMap();
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700318
319 @Override
320 public Set<String> getNames() {
321 return ImmutableSet.copyOf(sprites.keySet());
322 }
323
324 @Override
325 public void put(String name, JsonNode spriteData) {
Simon Huntfd8c7d72015-04-14 17:53:37 -0700326 log.info("Registered sprite definition [{}]", name);
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700327 sprites.put(name, spriteData);
328 }
329
330 @Override
331 public JsonNode get(String name) {
332 return sprites.get(name);
333 }
334
Thomas Vachuska0af26912016-03-21 21:37:30 -0700335 private String key(String userName, String keyName) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700336 return userName + SLASH + keyName;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700337 }
338
Simon Hunt3678c2a2016-03-28 14:48:07 -0700339
Thomas Vachuska0af26912016-03-21 21:37:30 -0700340 private String userName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700341 return key.split(SLASH)[IDX_USER];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700342 }
343
344 private String keyName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700345 return key.split(SLASH)[IDX_KEY];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700346 }
347
Simon Hunt1169c952017-06-05 11:20:11 -0700348
349 // =====================================================================
350 // UiTokenService
351
352 @Override
353 public UiSessionToken issueToken(String username) {
354 UiSessionToken token = new UiSessionToken(tokenGen.nextSessionId());
355 tokens.put(token, username);
356 log.debug("UiSessionToken issued: {}", token);
357 return token;
358 }
359
360 @Override
361 public void revokeToken(UiSessionToken token) {
362 if (token != null) {
363 tokens.remove(token);
364 log.debug("UiSessionToken revoked: {}", token);
365 }
366 }
367
368 @Override
369 public boolean isTokenValid(UiSessionToken token) {
370 return token != null && tokens.containsKey(token);
371 }
372
373 private final class SessionTokenGenerator {
374 private final SecureRandom random = new SecureRandom();
375
376 /*
377 This works by choosing 130 bits from a cryptographically secure
378 random bit generator, and encoding them in base-32.
379
380 128 bits is considered to be cryptographically strong, but each
381 digit in a base 32 number can encode 5 bits, so 128 is rounded up
382 to the next multiple of 5.
383
384 This encoding is compact and efficient, with 5 random bits per
385 character. Compare this to a random UUID, which only has 3.4 bits
386 per character in standard layout, and only 122 random bits in total.
387
388 Note that SecureRandom objects are expensive to initialize, so
389 we'll want to keep it around and re-use it.
390 */
391
392 private String nextSessionId() {
393 return new BigInteger(130, random).toString(32);
394 }
395 }
396
Thomas Vachuska0af26912016-03-21 21:37:30 -0700397 // Auxiliary listener to preference map events.
398 private class InternalPrefsListener
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700399 implements MapEventListener<String, ObjectNode> {
Thomas Vachuska0af26912016-03-21 21:37:30 -0700400 @Override
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700401 public void event(MapEvent<String, ObjectNode> event) {
Madan Jampanibf8ee802016-05-04 14:07:36 -0700402 eventHandlingExecutor.execute(() -> {
403 String userName = userName(event.key());
404 if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
405 UiWebSocketServlet.sendToUser(userName, UPDATE_PREFS, jsonPrefs());
406 }
407 });
Thomas Vachuska0af26912016-03-21 21:37:30 -0700408 }
409
410 private ObjectNode jsonPrefs() {
411 ObjectNode json = mapper.createObjectNode();
Simon Hunt1169c952017-06-05 11:20:11 -0700412 prefs.forEach((key, value) -> json.set(keyName(key), value));
Thomas Vachuska0af26912016-03-21 21:37:30 -0700413 return json;
414 }
415 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800416}