blob: 925298c852406e05b1742d9521fc0ffea14a84c0 [file] [log] [blame]
Changhoon Yoonb856b812015-08-10 03:47:19 +09001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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 */
16
17package org.onosproject.security.store;
18
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Sets;
21
22import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.apache.felix.scr.annotations.Service;
28import org.apache.karaf.features.BundleInfo;
29import org.apache.karaf.features.Feature;
30import org.apache.karaf.features.FeaturesService;
31
32import org.onlab.util.KryoNamespace;
33import org.onosproject.app.ApplicationAdminService;
34import org.onosproject.core.Application;
35import org.onosproject.core.ApplicationId;
36import org.onosproject.security.Permission;
37import org.onosproject.store.AbstractStore;
38import org.onosproject.store.serializers.KryoNamespaces;
39import org.onosproject.store.service.ConsistentMap;
40import org.onosproject.store.service.EventuallyConsistentMap;
41import org.onosproject.store.service.LogicalClockService;
42import org.onosproject.store.service.MapEvent;
43import org.onosproject.store.service.MapEventListener;
44import org.onosproject.store.service.Serializer;
45import org.onosproject.store.service.StorageService;
46import org.slf4j.Logger;
47
48import java.util.HashSet;
49import java.util.Set;
50import java.util.concurrent.ConcurrentHashMap;
51import java.util.stream.Collectors;
52
53import static org.onosproject.security.store.SecurityModeState.*;
54import static org.slf4j.LoggerFactory.getLogger;
55
56/**
57 * Manages application permissions granted/requested to applications.
58 * Uses both gossip-based and RAFT-based distributed data store.
59 */
60@Component(immediate = true)
61@Service
62public class DistributedSecurityModeStore
63 extends AbstractStore<SecurityModeEvent, SecurityModeStoreDelegate>
64 implements SecurityModeStore {
65
66 private final Logger log = getLogger(getClass());
67
68 private ConsistentMap<ApplicationId, SecurityInfo> states;
69 private EventuallyConsistentMap<ApplicationId, Set<Permission>> violations;
70
71 private ConcurrentHashMap<String, Set<ApplicationId>> localBundleAppDirectory;
72 private ConcurrentHashMap<ApplicationId, Set<String>> localAppBundleDirectory;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected StorageService storageService;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected LogicalClockService clockService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected ApplicationAdminService applicationAdminService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected FeaturesService featuresService;
85
86 private static final Serializer STATE_SERIALIZER = Serializer.using(new KryoNamespace.Builder()
87 .register(KryoNamespaces.API)
88 .register(SecurityModeState.class)
89 .register(SecurityInfo.class)
90 .register(Permission.class)
91 .build());
92
93 private static final KryoNamespace.Builder VIOLATION_SERIALIZER = KryoNamespace.newBuilder()
94 .register(KryoNamespaces.API)
95 .register(Permission.class);
96
97 @Activate
98 public void activate() {
99 states = storageService.<ApplicationId, SecurityInfo>consistentMapBuilder()
100 .withName("smonos-sdata")
101 .withSerializer(STATE_SERIALIZER)
102 .build();
103
104 states.addListener(new SecurityStateListener());
105
106 violations = storageService.<ApplicationId, Set<Permission>>eventuallyConsistentMapBuilder()
107 .withName("smonos-rperms")
108 .withSerializer(VIOLATION_SERIALIZER)
109 .withTimestampProvider((k, v) -> clockService.getTimestamp())
110 .build();
111
112 localBundleAppDirectory = new ConcurrentHashMap<>();
113 localAppBundleDirectory = new ConcurrentHashMap<>();
114
115 log.info("Started");
116
117 }
118
119 @Deactivate
120 public void deactivate() {
121 violations.destroy();
122 log.info("Stopped");
123 }
124
125
126 @Override
127 public Set<String> getBundleLocations(ApplicationId appId) {
128 Set<String> locations = localAppBundleDirectory.get(appId);
129 return locations != null ? locations : Sets.newHashSet();
130 }
131
132 @Override
133 public Set<ApplicationId> getApplicationIds(String location) {
134 Set<ApplicationId> appIds = localBundleAppDirectory.get(location);
135 return appIds != null ? appIds : Sets.newHashSet();
136 }
137
138 @Override
139 public Set<Permission> getRequestedPermissions(ApplicationId appId) {
140 Set<Permission> permissions = violations.get(appId);
141 return permissions != null ? permissions : ImmutableSet.of();
142 }
143
144 @Override
145 public Set<Permission> getGrantedPermissions(ApplicationId appId) {
146 return states.asJavaMap().getOrDefault(appId, new SecurityInfo(ImmutableSet.of(), null)).getPermissions();
147 }
148
149 @Override
150 public void requestPermission(ApplicationId appId, Permission permission) {
151
152 states.computeIf(appId, securityInfo -> (securityInfo == null || securityInfo.getState() != POLICY_VIOLATED),
153 (id, securityInfo) -> new SecurityInfo(securityInfo.getPermissions(), POLICY_VIOLATED));
154 violations.compute(appId, (k, v) -> v == null ? Sets.newHashSet(permission) : addAndGet(v, permission));
155 }
156
157 private Set<Permission> addAndGet(Set<Permission> oldSet, Permission newPerm) {
158 oldSet.add(newPerm);
159 return oldSet;
160 }
161
162 @Override
163 public boolean isSecured(ApplicationId appId) {
164 SecurityInfo info = states.get(appId).value();
165 return info == null ? false : info.getState().equals(SECURED);
166 }
167
168 @Override
169 public void reviewPolicy(ApplicationId appId) {
170 Application app = applicationAdminService.getApplication(appId);
171 if (app == null) {
172 log.warn("Unknown Application");
173 return;
174 }
175 states.computeIfPresent(appId, (applicationId, securityInfo) -> {
176 if (securityInfo.getState().equals(INSTALLED)) {
177 return new SecurityInfo(ImmutableSet.of(), REVIEWED);
178 }
179 return securityInfo;
180 });
181 }
182
183 @Override
184 public void acceptPolicy(ApplicationId appId, Set<Permission> permissionSet) {
185
186 Application app = applicationAdminService.getApplication(appId);
187 if (app == null) {
188 log.warn("Unknown Application");
189 return;
190 }
191
192 states.computeIf(appId,
193 securityInfo -> (securityInfo != null),
194 (id, securityInfo) -> {
195 switch (securityInfo.getState()) {
196 case POLICY_VIOLATED:
197 System.out.println(
198 "This application has violated the security policy. Please uninstall.");
199 return securityInfo;
200 case SECURED:
201 System.out.println(
202 "The policy has been accepted already. To review policy, review [app.name]");
203 return securityInfo;
204 case INSTALLED:
205 System.out.println("Please review the security policy prior to accept them");
206 log.warn("Application has not been reviewed");
207 return securityInfo;
208 case REVIEWED:
209 return new SecurityInfo(permissionSet, SECURED);
210 default:
211 return securityInfo;
212 }
213 });
214 }
215
216 private final class SecurityStateListener
217 implements MapEventListener<ApplicationId, SecurityInfo> {
218
219 @Override
220 public void event(MapEvent<ApplicationId, SecurityInfo> event) {
221
222 if (delegate == null) {
223 return;
224 }
225 ApplicationId appId = event.key();
226 SecurityInfo info = event.value().value();
227
228 if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
229 switch (info.getState()) {
230 case POLICY_VIOLATED:
231 notifyDelegate(new SecurityModeEvent(SecurityModeEvent.Type.POLICY_VIOLATED, appId));
232 break;
233 case SECURED:
234 notifyDelegate(new SecurityModeEvent(SecurityModeEvent.Type.POLICY_ACCEPTED, appId));
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800235 break;
Changhoon Yoonb856b812015-08-10 03:47:19 +0900236 default:
237 break;
238 }
239 } else if (event.type() == MapEvent.Type.REMOVE) {
240 removeAppFromDirectories(appId);
241 }
242 }
243 }
244
245 private void removeAppFromDirectories(ApplicationId appId) {
246 for (String location : localAppBundleDirectory.get(appId)) {
247 localBundleAppDirectory.get(location).remove(appId);
248 }
249 violations.remove(appId);
250 states.remove(appId);
251 localAppBundleDirectory.remove(appId);
252 }
253
254 @Override
255 public boolean registerApplication(ApplicationId appId) {
256 Application app = applicationAdminService.getApplication(appId);
257 if (app == null) {
258 log.warn("Unknown application.");
259 return false;
260 }
261 localAppBundleDirectory.put(appId, getBundleLocations(app));
262 for (String location : localAppBundleDirectory.get(appId)) {
263 if (!localBundleAppDirectory.containsKey(location)) {
264 localBundleAppDirectory.put(location, new HashSet<>());
265 }
266 if (!localBundleAppDirectory.get(location).contains(appId)) {
267 localBundleAppDirectory.get(location).add(appId);
268 }
269 }
270 states.put(appId, new SecurityInfo(Sets.newHashSet(), INSTALLED));
271 return true;
272 }
273
274 @Override
275 public void unregisterApplication(ApplicationId appId) {
276 if (localAppBundleDirectory.containsKey(appId)) {
277 for (String location : localAppBundleDirectory.get(appId)) {
278 if (localBundleAppDirectory.get(location).size() == 1) {
279 localBundleAppDirectory.remove(location);
280 } else {
281 localBundleAppDirectory.get(location).remove(appId);
282 }
283 }
284 localAppBundleDirectory.remove(appId);
285 }
286 }
287
288 @Override
289 public SecurityModeState getState(ApplicationId appId) {
290 return states.asJavaMap().getOrDefault(appId, new SecurityInfo(null, null)).getState();
291 }
292
293 private Set<String> getBundleLocations(Application app) {
294 Set<String> locations = new HashSet<>();
295 for (String name : app.features()) {
296 try {
297 Feature feature = featuresService.getFeature(name);
298 locations.addAll(
299 feature.getBundles().stream().map(BundleInfo::getLocation).collect(Collectors.toList()));
300 } catch (Exception e) {
301 return locations;
302 }
303 }
304 return locations;
305 }
306
307 @Override
308 public void setDelegate(SecurityModeStoreDelegate delegate) {
309 super.setDelegate(delegate);
310 }
311
312 @Override
313 public void unsetDelegate(SecurityModeStoreDelegate delegate) {
314 super.setDelegate(delegate);
315 }
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800316}