Merge "Prevent ConfigPusher from killing its thread"
[controller.git] / opendaylight / usermanager / implementation / src / main / java / org / opendaylight / controller / usermanager / internal / UserManager.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.usermanager.internal;
10
11 import java.io.File;
12 import java.io.FileInputStream;
13 import java.io.FileNotFoundException;
14 import java.io.IOException;
15 import java.io.ObjectInputStream;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.EnumSet;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26
27 import org.apache.commons.lang3.StringUtils;
28 import org.eclipse.osgi.framework.console.CommandInterpreter;
29 import org.eclipse.osgi.framework.console.CommandProvider;
30 import org.opendaylight.controller.clustering.services.CacheConfigException;
31 import org.opendaylight.controller.clustering.services.CacheExistException;
32 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
33 import org.opendaylight.controller.clustering.services.IClusterServices;
34 import org.opendaylight.controller.configuration.ConfigurationObject;
35 import org.opendaylight.controller.configuration.IConfigurationAware;
36 import org.opendaylight.controller.configuration.IConfigurationService;
37 import org.opendaylight.controller.containermanager.IContainerAuthorization;
38 import org.opendaylight.controller.sal.authorization.AuthResultEnum;
39 import org.opendaylight.controller.sal.authorization.IResourceAuthorization;
40 import org.opendaylight.controller.sal.authorization.UserLevel;
41 import org.opendaylight.controller.sal.utils.IObjectReader;
42 import org.opendaylight.controller.sal.utils.Status;
43 import org.opendaylight.controller.sal.utils.StatusCode;
44 import org.opendaylight.controller.usermanager.AuthResponse;
45 import org.opendaylight.controller.usermanager.AuthenticatedUser;
46 import org.opendaylight.controller.usermanager.AuthorizationConfig;
47 import org.opendaylight.controller.usermanager.IAAAProvider;
48 import org.opendaylight.controller.usermanager.ISessionManager;
49 import org.opendaylight.controller.usermanager.IUserManager;
50 import org.opendaylight.controller.usermanager.ServerConfig;
51 import org.opendaylight.controller.usermanager.UserConfig;
52 import org.opendaylight.controller.usermanager.security.SessionManager;
53 import org.opendaylight.controller.usermanager.security.UserSecurityContextRepository;
54 import org.osgi.framework.BundleContext;
55 import org.osgi.framework.FrameworkUtil;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58 import org.springframework.security.authentication.AuthenticationProvider;
59 import org.springframework.security.authentication.AuthenticationServiceException;
60 import org.springframework.security.authentication.BadCredentialsException;
61 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
62 import org.springframework.security.core.Authentication;
63 import org.springframework.security.core.AuthenticationException;
64 import org.springframework.security.core.userdetails.User;
65 import org.springframework.security.core.userdetails.UserDetails;
66 import org.springframework.security.core.userdetails.UsernameNotFoundException;
67 import org.springframework.security.web.context.SecurityContextRepository;
68
69 /**
70  * The internal implementation of the User Manager.
71  */
72 public class UserManager implements IUserManager, IObjectReader,
73         IConfigurationAware, CommandProvider, AuthenticationProvider {
74     private static final Logger logger = LoggerFactory.getLogger(UserManager.class);
75     private static final String DEFAULT_ADMIN = "admin";
76     private static final String DEFAULT_ADMIN_PASSWORD = "admin";
77     private static final String DEFAULT_ADMIN_ROLE = UserLevel.NETWORKADMIN.toString();
78     private static final String USERS_FILE_NAME = "users.conf";
79     private static final String SERVERS_FILE_NAME = "servers.conf";
80     private static final String AUTH_FILE_NAME = "authorization.conf";
81     private static final String RECOVERY_FILE = "NETWORK_ADMIN_PASSWORD_RECOVERY";
82     private ConcurrentMap<String, UserConfig> localUserConfigList;
83     private ConcurrentMap<String, ServerConfig> remoteServerConfigList;
84     // local authorization info for remotely authenticated users
85     private ConcurrentMap<String, AuthorizationConfig> authorizationConfList;
86     private ConcurrentMap<String, AuthenticatedUser> activeUsers;
87     private ConcurrentMap<String, IAAAProvider> authProviders;
88     private IClusterGlobalServices clusterGlobalService = null;
89     private IConfigurationService configurationService;
90     private SecurityContextRepository securityContextRepo = new UserSecurityContextRepository();
91     private IContainerAuthorization containerAuthorizationClient;
92     private Set<IResourceAuthorization> applicationAuthorizationClients;
93     private ISessionManager sessionMgr = new SessionManager();
94     protected enum Command {
95         ADD("add", "added"),
96         MODIFY("modify", "modified"),
97         REMOVE("remove", "removed");
98         private String action;
99         private String postAction;
100         private Command(String action, String postAction) {
101             this.action = action;
102             this.postAction = postAction;
103         }
104
105         public String getAction() {
106             return action;
107         }
108
109         public String getPostAction() {
110             return postAction;
111         }
112     }
113
114     public boolean addAAAProvider(IAAAProvider provider) {
115         if (provider == null || provider.getName() == null
116                 || provider.getName().trim().isEmpty()) {
117             return false;
118         }
119         if (authProviders.get(provider.getName()) != null) {
120             return false;
121         }
122
123         authProviders.put(provider.getName(), provider);
124         return true;
125     }
126
127     public void removeAAAProvider(IAAAProvider provider) {
128         authProviders.remove(provider.getName());
129     }
130
131     public IAAAProvider getAAAProvider(String name) {
132         return authProviders.get(name);
133     }
134
135     @Override
136     public Set<String> getAAAProviderNames() {
137         return authProviders.keySet();
138     }
139
140     private void allocateCaches() {
141         this.applicationAuthorizationClients = Collections.synchronizedSet(new HashSet<IResourceAuthorization>());
142         if (clusterGlobalService == null) {
143             logger.error("un-initialized clusterGlobalService, can't create cache");
144             return;
145         }
146
147         try {
148             clusterGlobalService.createCache("usermanager.localUserConfigList",
149                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
150
151             clusterGlobalService.createCache(
152                     "usermanager.remoteServerConfigList",
153                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
154
155             clusterGlobalService.createCache(
156                     "usermanager.authorizationConfList",
157                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
158
159             clusterGlobalService.createCache("usermanager.activeUsers",
160                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
161         } catch (CacheConfigException cce) {
162             logger.error("Cache configuration invalid - check cache mode");
163         } catch (CacheExistException ce) {
164             logger.debug("Skipping cache creation as already present");
165         }
166     }
167
168     @SuppressWarnings({ "unchecked" })
169     private void retrieveCaches() {
170         if (clusterGlobalService == null) {
171             logger.error("un-initialized clusterService, can't retrieve cache");
172             return;
173         }
174
175         activeUsers = (ConcurrentMap<String, AuthenticatedUser>) clusterGlobalService
176                 .getCache("usermanager.activeUsers");
177         if (activeUsers == null) {
178             logger.error("Failed to get cache for activeUsers");
179         }
180
181         localUserConfigList = (ConcurrentMap<String, UserConfig>) clusterGlobalService
182                 .getCache("usermanager.localUserConfigList");
183         if (localUserConfigList == null) {
184             logger.error("Failed to get cache for localUserConfigList");
185         }
186
187         remoteServerConfigList = (ConcurrentMap<String, ServerConfig>) clusterGlobalService
188                 .getCache("usermanager.remoteServerConfigList");
189         if (remoteServerConfigList == null) {
190             logger.error("Failed to get cache for remoteServerConfigList");
191         }
192
193         authorizationConfList = (ConcurrentMap<String, AuthorizationConfig>) clusterGlobalService
194                 .getCache("usermanager.authorizationConfList");
195         if (authorizationConfList == null) {
196             logger.error("Failed to get cache for authorizationConfList");
197         }
198     }
199
200     private void loadConfigurations() {
201         // To encode and decode user and server configuration objects
202         loadSecurityKeys();
203         /*
204          * Do not load local startup file if we are not the coordinator
205          */
206         loadUserConfig();
207         loadServerConfig();
208         loadAuthConfig();
209     }
210
211     private void loadSecurityKeys() {
212
213     }
214
215     private void checkDefaultNetworkAdmin() {
216         /*
217          * If startup config is not there, it's old or it was deleted or if a
218          * password recovery was run, need to add Default Network Admin User
219          */
220         if (!localUserConfigList.containsKey(DEFAULT_ADMIN)) {
221             List<String> roles = new ArrayList<String>(1);
222             roles.add(DEFAULT_ADMIN_ROLE);
223             // Need to skip the strong password check for the default admin
224             UserConfig defaultAdmin = UserConfig.getUncheckedUserConfig(UserManager.DEFAULT_ADMIN,
225                     UserManager.DEFAULT_ADMIN_PASSWORD, roles);
226             localUserConfigList.put(UserManager.DEFAULT_ADMIN, defaultAdmin);
227         }
228     }
229
230     private void checkPasswordRecovery() {
231         final String fileDescription = "Default Network Administrator password recovery file";
232         try {
233             FileInputStream fis = new FileInputStream(UserManager.RECOVERY_FILE);
234             /*
235              * Recovery file detected, remove current default network
236              * administrator entry from local users configuration list.
237              * Warn user and delete recovery file.
238              */
239             this.localUserConfigList.remove(UserManager.DEFAULT_ADMIN);
240             logger.info("Default Network Administrator password has been reset to factory default.");
241             logger.info("Please change the default Network Administrator password as soon as possible");
242             File filePointer = new File(UserManager.RECOVERY_FILE);
243             boolean status = filePointer.delete();
244             if (!status) {
245                 logger.warn("Failed to delete {}", fileDescription);
246             } else {
247                 logger.trace("{} deleted", fileDescription);
248             }
249             fis.close();
250         } catch (FileNotFoundException fnf) {
251             logger.trace("{} not present", fileDescription);
252         } catch (IOException e) {
253             logger.warn("Failed to close file stream for {}", fileDescription);
254         }
255     }
256
257     @Override
258     public AuthResultEnum authenticate(String userName, String password) {
259         IAAAProvider aaaClient;
260         AuthResponse rcResponse = null;
261         AuthenticatedUser result;
262         boolean remotelyAuthenticated = false;
263         boolean authorizationInfoIsPresent = false;
264         boolean authorized = false;
265
266         /*
267          * Attempt remote authentication first if server is configured
268          */
269         for (ServerConfig aaaServer : remoteServerConfigList.values()) {
270             String protocol = aaaServer.getProtocol();
271             aaaClient = this.getAAAProvider(protocol);
272             if (aaaClient != null) {
273                 rcResponse = aaaClient.authService(userName, password,
274                         aaaServer.getAddress(), aaaServer.getSecret());
275                 if (rcResponse.getStatus() == AuthResultEnum.AUTH_ACCEPT) {
276                     logger.trace(
277                             "Remote Authentication Succeeded for User: \"{}\", by Server: {}",
278                             userName, aaaServer.getAddress());
279                     remotelyAuthenticated = true;
280                     break;
281                 } else if (rcResponse.getStatus() == AuthResultEnum.AUTH_REJECT) {
282                     logger.trace(
283                             "Remote Authentication Rejected User: \"{}\", from Server: {}, Reason:{}",
284                             new Object[] { userName, aaaServer.getAddress(),
285                                     rcResponse.getStatus().toString() });
286                 } else {
287                     logger.trace(
288                             "Remote Authentication Failed for User: \"{}\", from Server: {}, Reason:{}",
289                             new Object[] { userName, aaaServer.getAddress(),
290                                     rcResponse.getStatus().toString() });
291                 }
292             }
293         }
294
295         if (!remotelyAuthenticated) {
296             UserConfig localUser = this.localUserConfigList.get(userName);
297             if (localUser == null) {
298                 logger.info(
299                         "Local Authentication Failed for User:\"{}\", Reason: "
300                                 + "user not found in Local Database", userName);
301                 return (AuthResultEnum.AUTH_INVALID_LOC_USER);
302             }
303             rcResponse = localUser.authenticate(password);
304             if (rcResponse.getStatus() != AuthResultEnum.AUTH_ACCEPT_LOC) {
305                 logger.info(
306                         "Local Authentication Failed for User: \"{}\", Reason: {}",
307                         userName, rcResponse.getStatus().toString());
308
309                 return (rcResponse.getStatus());
310             }
311             logger.info("Local Authentication Succeeded for User: \"{}\"",
312                     userName);
313         }
314
315         /*
316          * Authentication succeeded
317          */
318         result = new AuthenticatedUser(userName);
319
320         /*
321          * Extract attributes from response All the information we are
322          * interested in is in the first Cisco VSA (vendor specific attribute).
323          * Just process the first VSA and return
324          */
325         String attributes = (rcResponse.getData() != null && !rcResponse
326                 .getData().isEmpty()) ? rcResponse.getData().get(0) : null;
327
328         /*
329          * Check if the authorization information is present
330          */
331         authorizationInfoIsPresent = checkAuthorizationInfo(attributes);
332
333         /*
334          * The AAA server was only used to perform the authentication Look for
335          * locally stored authorization info for this user If found, add the
336          * data to the rcResponse
337          */
338         if (remotelyAuthenticated && !authorizationInfoIsPresent) {
339             logger.info(
340                     "No Remote Authorization Info provided by Server for User: \"{}\"",
341                     userName);
342             logger.info(
343                     "Looking for Local Authorization Info for User: \"{}\"",
344                     userName);
345
346             AuthorizationConfig resource = authorizationConfList.get(userName);
347             if (resource != null) {
348                 logger.info("Found Local Authorization Info for User: \"{}\"",
349                         userName);
350                 attributes = resource.getRolesString();
351
352             }
353             authorizationInfoIsPresent = checkAuthorizationInfo(attributes);
354         }
355
356         /*
357          * Common response parsing for local & remote authenticated user Looking
358          * for authorized resources, detecting attributes' validity
359          */
360         if (authorizationInfoIsPresent) {
361             // Identifying the administrative role
362             result.setRoleList(attributes.split(" "));
363             authorized = true;
364         } else {
365             logger.info("Not able to find Authorization Info for User: \"{}\"",
366                     userName);
367         }
368
369         /*
370          * Add profile for authenticated user
371          */
372         putUserInActiveList(userName, result);
373         if (authorized) {
374             logger.info("User \"{}\" authorized for the following role(s): {}",
375                     userName, result.getUserRoles());
376         } else {
377             logger.info("User \"{}\" Not Authorized for any role ", userName);
378         }
379
380         return rcResponse.getStatus();
381     }
382
383     // Check in the attributes string whether or not authorization information
384     // is present
385     private boolean checkAuthorizationInfo(String attributes) {
386         return (attributes != null && !attributes.isEmpty());
387     }
388
389     private void putUserInActiveList(String user, AuthenticatedUser result) {
390         activeUsers.put(user, result);
391     }
392
393     private void removeUserFromActiveList(String user) {
394         if (!activeUsers.containsKey(user)) {
395             // as cookie persists in cache, we can get logout for unexisting
396             // active users
397             return;
398         }
399         activeUsers.remove(user);
400     }
401
402     @Override
403     public Status saveLocalUserList() {
404         return saveLocalUserListInternal();
405     }
406
407     private Status saveLocalUserListInternal() {
408         return configurationService.persistConfiguration(
409                 new ArrayList<ConfigurationObject>(localUserConfigList.values()), USERS_FILE_NAME);
410     }
411
412     @Override
413     public Status saveAAAServerList() {
414         return saveAAAServerListInternal();
415     }
416
417     private Status saveAAAServerListInternal() {
418         return configurationService.persistConfiguration(
419                 new ArrayList<ConfigurationObject>(remoteServerConfigList.values()), SERVERS_FILE_NAME);
420     }
421
422     @Override
423     public Status saveAuthorizationList() {
424         return saveAuthorizationListInternal();
425     }
426
427     private Status saveAuthorizationListInternal() {
428         return configurationService.persistConfiguration(
429                 new ArrayList<ConfigurationObject>(authorizationConfList.values()), AUTH_FILE_NAME);
430     }
431
432     @Override
433     public Object readObject(ObjectInputStream ois)
434             throws FileNotFoundException, IOException, ClassNotFoundException {
435         // Perform the class deserialization locally, from inside the package
436         // where the class is defined
437         return ois.readObject();
438     }
439
440     private void loadUserConfig() {
441         for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, USERS_FILE_NAME)) {
442             addRemoveLocalUserInternal((UserConfig) conf, false);
443         }
444     }
445
446     private void loadServerConfig() {
447         for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, SERVERS_FILE_NAME)) {
448             addAAAServer((ServerConfig) conf);
449         }
450     }
451
452     private void loadAuthConfig() {
453         for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, AUTH_FILE_NAME)) {
454             addAuthInfo((AuthorizationConfig) conf);
455         }
456     }
457
458     /*
459      * Interaction with GUI START
460      */
461     private Status changeLocalUser(UserConfig AAAconf, Command command) {
462         // UserConfig Validation check
463         Status validCheck = AAAconf.validate();
464         if (!validCheck.isSuccess()) {
465             return validCheck;
466         }
467
468         String user = AAAconf.getUser();
469
470         // Check default admin user
471         if (user.equals(UserManager.DEFAULT_ADMIN)) {
472             String msg = String.format("Invalid Request: Default Network Admin  User cannot be %s", command.getPostAction());
473             logger.debug(msg);
474             return new Status(StatusCode.NOTALLOWED, msg);
475         }
476
477         // Check user presence/conflict
478         UserConfig currentAAAconf = localUserConfigList.get(user);
479         StatusCode statusCode = null;
480         String reason = null;
481         switch (command) {
482         case ADD:
483             if (currentAAAconf != null) {
484                 reason = "already present";
485                 statusCode = StatusCode.CONFLICT;
486             }
487             break;
488         case MODIFY:
489         case REMOVE:
490             if (currentAAAconf == null) {
491                 reason = "not found";
492                 statusCode = StatusCode.NOTFOUND;
493             }
494             break;
495         default:
496             break;
497
498         }
499         if (statusCode != null) {
500             String action = String.format("Failed to %s user %s: ", command.getAction(), user);
501             String msg = String.format("User %s %s in configuration database", user, reason);
502             logger.debug(action + msg);
503             return new Status(statusCode, msg);
504         }
505
506         switch (command) {
507         case ADD:
508             return addRemoveLocalUserInternal(AAAconf, false);
509         case MODIFY:
510             addRemoveLocalUserInternal(currentAAAconf, true);
511             return addRemoveLocalUserInternal(AAAconf, false);
512         case REMOVE:
513             return addRemoveLocalUserInternal(AAAconf, true);
514         default:
515             return new Status(StatusCode.INTERNALERROR, "Unknown action");
516         }
517     }
518
519     private Status addRemoveLocalUserInternal(UserConfig AAAconf, boolean delete) {
520         // Update Config database
521         if (delete) {
522             localUserConfigList.remove(AAAconf.getUser());
523             /*
524              * A user account has been removed form local database, we assume
525              * admin does not want this user to stay connected, in case he has
526              * an open session. So we clean the active list as well.
527              */
528             removeUserFromActiveList(AAAconf.getUser());
529         } else {
530             localUserConfigList.put(AAAconf.getUser(), AAAconf);
531         }
532
533         return new Status(StatusCode.SUCCESS);
534     }
535
536     private Status addRemoveAAAServer(ServerConfig AAAconf, boolean delete) {
537         // Validation check
538         if (!AAAconf.isValid()) {
539             String msg = "Invalid Server configuration";
540             logger.warn(msg);
541             return new Status(StatusCode.BADREQUEST, msg);
542         }
543
544         // Update configuration database
545         if (delete) {
546             remoteServerConfigList.remove(AAAconf.getAddress());
547         } else {
548             remoteServerConfigList.put(AAAconf.getAddress(), AAAconf);
549         }
550
551         return new Status(StatusCode.SUCCESS);
552     }
553
554     private Status addRemoveAuthInfo(AuthorizationConfig AAAconf, boolean delete) {
555         Status configCheck = AAAconf.validate();
556         if (!configCheck.isSuccess()) {
557             String msg = "Invalid Authorization configuration: "
558                     + configCheck.getDescription();
559             logger.warn(msg);
560             return new Status(StatusCode.BADREQUEST, msg);
561         }
562
563         // Update configuration database
564         if (delete) {
565             authorizationConfList.remove(AAAconf.getUser());
566         } else {
567             authorizationConfList.put(AAAconf.getUser(), AAAconf);
568         }
569
570         return new Status(StatusCode.SUCCESS);
571     }
572
573     @Override
574     public Status addLocalUser(UserConfig AAAconf) {
575         return changeLocalUser(AAAconf, Command.ADD);
576     }
577
578     @Override
579     public Status modifyLocalUser(UserConfig AAAconf) {
580         return changeLocalUser(AAAconf, Command.MODIFY);
581     }
582
583     @Override
584     public Status removeLocalUser(UserConfig AAAconf) {
585         return changeLocalUser(AAAconf, Command.REMOVE);
586     }
587
588     @Override
589     public Status removeLocalUser(String userName) {
590         if (userName == null || userName.trim().isEmpty()) {
591             return new Status(StatusCode.BADREQUEST, "Invalid user name");
592         }
593
594         if (!localUserConfigList.containsKey(userName)) {
595             return new Status(StatusCode.NOTFOUND, "User does not exist");
596         }
597
598         return changeLocalUser(localUserConfigList.get(userName), Command.REMOVE);
599     }
600
601     @Override
602     public Status addAAAServer(ServerConfig AAAconf) {
603         return addRemoveAAAServer(AAAconf, false);
604     }
605
606     @Override
607     public Status removeAAAServer(ServerConfig AAAconf) {
608         return addRemoveAAAServer(AAAconf, true);
609     }
610
611     @Override
612     public Status addAuthInfo(AuthorizationConfig AAAconf) {
613         return addRemoveAuthInfo(AAAconf, false);
614     }
615
616     @Override
617     public Status removeAuthInfo(AuthorizationConfig AAAconf) {
618         return addRemoveAuthInfo(AAAconf, true);
619     }
620
621     @Override
622     public List<UserConfig> getLocalUserList() {
623         return new ArrayList<UserConfig>(localUserConfigList.values());
624     }
625
626     @Override
627     public List<ServerConfig> getAAAServerList() {
628         return new ArrayList<ServerConfig>(remoteServerConfigList.values());
629     }
630
631     @Override
632     public List<AuthorizationConfig> getAuthorizationList() {
633         return new ArrayList<AuthorizationConfig>(
634                 authorizationConfList.values());
635     }
636
637     @Override
638     public Status changeLocalUserPassword(String user, String curPassword, String newPassword) {
639         UserConfig targetConfigEntry = null;
640
641         // update configuration entry
642         targetConfigEntry = localUserConfigList.get(user);
643         if (targetConfigEntry == null) {
644             return new Status(StatusCode.NOTFOUND, "User not found");
645         }
646         Status status = targetConfigEntry.update(curPassword, newPassword, null);
647         if (!status.isSuccess()) {
648             return status;
649         }
650         // Trigger cluster update
651         localUserConfigList.put(user, targetConfigEntry);
652
653         logger.trace("Password changed for User \"{}\"", user);
654
655         return status;
656     }
657
658     @Override
659     public void userLogout(String userName) {
660         // TODO: if user was authenticated through AAA server, send
661         // Acct-Status-Type=stop message to server with logout as reason
662         removeUserFromActiveList(userName);
663         logger.trace("User \"{}\" logged out", userName);
664     }
665
666     /*
667      * This function will get called by http session mgr when session times out
668      */
669     @Override
670     public void userTimedOut(String userName) {
671         // TODO: if user was authenticated through AAA server, send
672         // Acct-Status-Type=stop message to server with timeout as reason
673         removeUserFromActiveList(userName);
674         logger.trace("User \"{}\" timed out", userName);
675     }
676
677     @Override
678     public String getAccessDate(String user) {
679         return this.activeUsers.get(user).getAccessDate();
680     }
681
682     @Override
683     public synchronized Map<String, List<String>> getUserLoggedIn() {
684         Map<String, List<String>> loggedInList = new HashMap<String, List<String>>();
685         for (Map.Entry<String, AuthenticatedUser> user : activeUsers.entrySet()) {
686             String userNameShow = user.getKey();
687             loggedInList.put(userNameShow, user.getValue().getUserRoles());
688         }
689         return loggedInList;
690     }
691
692     public void _umAddUser(CommandInterpreter ci) {
693         String userName = ci.nextArgument();
694         String password = ci.nextArgument();
695         String role = ci.nextArgument();
696
697         List<String> roles = new ArrayList<String>();
698         while (role != null) {
699             if (!role.trim().isEmpty()) {
700                 roles.add(role);
701             }
702             role = ci.nextArgument();
703         }
704
705         if (userName == null || userName.trim().isEmpty() || password == null || password.trim().isEmpty()
706                 || roles.isEmpty()) {
707             ci.println("Invalid Arguments");
708             ci.println("umAddUser <user_name> <password> <user_role>");
709             return;
710         }
711         ci.print(this.addLocalUser(new UserConfig(userName, password, roles)));
712     }
713
714     public void _umRemUser(CommandInterpreter ci) {
715         String userName = ci.nextArgument();
716
717         if (userName == null || userName.trim().isEmpty()) {
718             ci.println("Invalid Arguments");
719             ci.println("umRemUser <user_name>");
720             return;
721         }
722         UserConfig target = localUserConfigList.get(userName);
723         if (target == null) {
724             ci.println("User not found");
725             return;
726         }
727         ci.println(this.removeLocalUser(target));
728     }
729
730     public void _umGetUsers(CommandInterpreter ci) {
731         for (UserConfig conf : this.getLocalUserList()) {
732             ci.println(conf.getUser() + " " + conf.getRoles());
733         }
734     }
735
736     public void _addAAAServer(CommandInterpreter ci) {
737         String server = ci.nextArgument();
738         String secret = ci.nextArgument();
739         String protocol = ci.nextArgument();
740
741         if (server == null || secret == null || protocol == null) {
742             ci.println("Usage : addAAAServer <server> <secret> <protocol>");
743             return;
744         }
745         ServerConfig s = new ServerConfig(server, secret, protocol);
746         addAAAServer(s);
747     }
748
749     public void _removeAAAServer(CommandInterpreter ci) {
750         String server = ci.nextArgument();
751         String secret = ci.nextArgument();
752         String protocol = ci.nextArgument();
753
754         if (server == null || secret == null || protocol == null) {
755             ci.println("Usage : addAAAServer <server> <secret> <protocol>");
756             return;
757         }
758         ServerConfig s = new ServerConfig(server, secret, protocol);
759         removeAAAServer(s);
760     }
761
762     public void _printAAAServers(CommandInterpreter ci) {
763         for (ServerConfig aaaServer : remoteServerConfigList.values()) {
764             ci.println(aaaServer.getAddress() + "-" + aaaServer.getProtocol());
765         }
766     }
767
768     @Override
769     public String getHelp() {
770         StringBuffer help = new StringBuffer();
771         return help.toString();
772     }
773
774     void setClusterGlobalService(IClusterGlobalServices s) {
775         logger.debug("Cluster Service Global set");
776         this.clusterGlobalService = s;
777     }
778
779     void unsetClusterGlobalService(IClusterGlobalServices s) {
780         if (this.clusterGlobalService == s) {
781             logger.debug("Cluster Service Global removed!");
782             this.clusterGlobalService = null;
783         }
784     }
785
786     public void setConfigurationService(IConfigurationService service) {
787         logger.trace("Got configuration service set request {}", service);
788         this.configurationService = service;
789     }
790
791     public void unsetConfigurationService(IConfigurationService service) {
792         logger.trace("Got configuration service UNset request");
793         this.configurationService = null;
794     }
795
796     void unsetContainerAuthClient(IContainerAuthorization s) {
797         if (this.containerAuthorizationClient == s) {
798             this.containerAuthorizationClient = null;
799         }
800     }
801
802     void setContainerAuthClient(IContainerAuthorization s) {
803         this.containerAuthorizationClient = s;
804     }
805
806     void setAppAuthClient(IResourceAuthorization s) {
807         this.applicationAuthorizationClients.add(s);
808     }
809
810     void unsetAppAuthClient(IResourceAuthorization s) {
811         this.applicationAuthorizationClients.remove(s);
812     }
813
814     /**
815      * Function called by the dependency manager when all the required
816      * dependencies are satisfied
817      *
818      */
819     void init() {
820     }
821
822     /**
823      * Function called by the dependency manager when at least one dependency
824      * become unsatisfied or when the component is shutting down because for
825      * example bundle is being stopped.
826      *
827      */
828     void destroy() {
829     }
830
831     /**
832      * Function called by dependency manager after "init ()" is called and after
833      * the services provided by the class are registered in the service registry
834      *
835      */
836     void start() {
837         authProviders = new ConcurrentHashMap<String, IAAAProvider>();
838         // Instantiate cluster synced variables
839         allocateCaches();
840         retrieveCaches();
841
842         // Read startup configuration and populate databases
843         loadConfigurations();
844
845         // Check if a password recovery was triggered for default network admin user
846         checkPasswordRecovery();
847
848         // Make sure default Network Admin account is there
849         checkDefaultNetworkAdmin();
850
851         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
852         bundleContext.registerService(CommandProvider.class.getName(), this, null);
853     }
854
855     /**
856      * Function called by the dependency manager before the services exported by
857      * the component are unregistered, this will be followed by a "destroy ()"
858      * calls
859      *
860      */
861     void stop() {
862     }
863
864     @Override
865     public List<String> getUserRoles(String userName) {
866         List<String> roles = null;
867         if (userName != null) {
868             /*
869              * First look in active users then in local configured users,
870              * finally in local authorized users
871              */
872             if (activeUsers.containsKey(userName)) {
873                 roles = activeUsers.get(userName).getUserRoles();
874             } else if (localUserConfigList.containsKey(userName)) {
875                 roles = localUserConfigList.get(userName).getRoles();
876             } else if (authorizationConfList.containsKey(userName)) {
877                 roles = authorizationConfList.get(userName).getRoles();
878             }
879         }
880         return (roles == null) ? new ArrayList<String>(0) : roles;
881     }
882
883     @Override
884     public UserLevel getUserLevel(String username) {
885         // Returns the highest controller user level for the passed user
886         List<String> rolesNames = getUserRoles(username);
887
888         if (rolesNames.isEmpty()) {
889             return UserLevel.NOUSER;
890         }
891
892         // Check against the well known controller roles first
893         if (rolesNames.contains(UserLevel.SYSTEMADMIN.toString())) {
894             return UserLevel.SYSTEMADMIN;
895         }
896         if (rolesNames.contains(UserLevel.NETWORKADMIN.toString())) {
897             return UserLevel.NETWORKADMIN;
898         }
899         if (rolesNames.contains(UserLevel.NETWORKOPERATOR.toString())) {
900             return UserLevel.NETWORKOPERATOR;
901         }
902         // Check if container user now
903         if (containerAuthorizationClient != null) {
904             for (String roleName : rolesNames) {
905                 if (containerAuthorizationClient.isApplicationRole(roleName)) {
906                     return UserLevel.CONTAINERUSER;
907                 }
908             }
909         }
910         // Finally check if application user
911         if (applicationAuthorizationClients != null) {
912             for (String roleName : rolesNames) {
913                 for (IResourceAuthorization client : this.applicationAuthorizationClients) {
914                     if (client.isApplicationRole(roleName)) {
915                         return UserLevel.APPUSER;
916                     }
917                 }
918             }
919         }
920         return UserLevel.NOUSER;
921     }
922
923
924     @Override
925     public List<UserLevel> getUserLevels(String username) {
926         // Returns the controller user levels for the passed user
927         List<String> rolesNames =  getUserRoles(username);
928         List<UserLevel> levels = new ArrayList<UserLevel>();
929
930         if (rolesNames.isEmpty()) {
931             return levels;
932         }
933
934         // Check against the well known controller roles first
935         if (rolesNames.contains(UserLevel.SYSTEMADMIN.toString())) {
936             levels.add(UserLevel.SYSTEMADMIN);
937         }
938         if (rolesNames.contains(UserLevel.NETWORKADMIN.toString())) {
939             levels.add(UserLevel.NETWORKADMIN);
940         }
941         if (rolesNames.contains(UserLevel.NETWORKOPERATOR.toString())) {
942             levels.add(UserLevel.NETWORKOPERATOR);
943         }
944         // Check if container user now
945         if (containerAuthorizationClient != null) {
946             for (String roleName : rolesNames) {
947                 if (containerAuthorizationClient.isApplicationRole(roleName)) {
948                     levels.add(UserLevel.CONTAINERUSER);
949                     break;
950                 }
951             }
952         }
953         // Finally check if application user
954         if (applicationAuthorizationClients != null) {
955             for (String roleName : rolesNames) {
956                 for (IResourceAuthorization client : this.applicationAuthorizationClients) {
957                     if (client.isApplicationRole(roleName)) {
958                         levels.add(UserLevel.APPUSER);
959                         break;
960                     }
961                 }
962             }
963         }
964         return levels;
965     }
966
967     @Override
968     public Status saveConfiguration() {
969         boolean success = true;
970         Status ret = saveLocalUserList();
971         if (!ret.isSuccess()) {
972             success = false;
973         }
974         ret = saveAAAServerList();
975         if (!ret.isSuccess()) {
976             success = false;
977         }
978         ret = saveAuthorizationList();
979         if (!ret.isSuccess()) {
980             success = false;
981         }
982
983         if (success) {
984             return new Status(StatusCode.SUCCESS);
985         }
986
987         return new Status(StatusCode.INTERNALERROR, "Failed to save user configurations");
988     }
989
990     @Override
991     public UserDetails loadUserByUsername(String username)
992             throws UsernameNotFoundException {
993         AuthenticatedUser user = activeUsers.get(username);
994
995         if (user != null) {
996             boolean enabled = true;
997             boolean accountNonExpired = true;
998             boolean credentialsNonExpired = true;
999             boolean accountNonLocked = true;
1000
1001             return new User(username, localUserConfigList.get(username)
1002                     .getPassword(), enabled, accountNonExpired,
1003                     credentialsNonExpired, accountNonLocked,
1004                     user.getGrantedAuthorities(getUserLevel(username)));
1005         } else {
1006             throw new UsernameNotFoundException("User not found " + username);
1007         }
1008     }
1009
1010     @Override
1011     public boolean supports(Class<?> authentication) {
1012         return UsernamePasswordAuthenticationToken.class
1013                 .isAssignableFrom(authentication);
1014
1015     }
1016
1017     @Override
1018     public SecurityContextRepository getSecurityContextRepo() {
1019         return securityContextRepo;
1020     }
1021
1022     public void setSecurityContextRepo(
1023             SecurityContextRepository securityContextRepo) {
1024         this.securityContextRepo = securityContextRepo;
1025     }
1026
1027     @Override
1028     public Authentication authenticate(Authentication authentication)
1029             throws AuthenticationException {
1030
1031         if (StringUtils.isBlank((String) authentication.getCredentials())
1032                 || StringUtils.isBlank((String) authentication.getPrincipal())) {
1033             throw new BadCredentialsException(
1034                     "Username or credentials did not match");
1035         }
1036
1037         AuthResultEnum result = authenticate(
1038                 (String) authentication.getPrincipal(),
1039                 (String) authentication.getCredentials());
1040         if (result.equals(AuthResultEnum.AUTHOR_PASS)
1041                 || result.equals(AuthResultEnum.AUTH_ACCEPT_LOC)
1042                 || result.equals(AuthResultEnum.AUTH_ACCEPT)) {
1043
1044             AuthenticatedUser user = activeUsers.get(authentication
1045                     .getPrincipal().toString());
1046
1047             if (user == null) {
1048                 throw new AuthenticationServiceException(
1049                         "Authentication Failure");
1050             }
1051
1052             authentication = new UsernamePasswordAuthenticationToken(
1053                     authentication.getPrincipal(),
1054                     authentication.getCredentials(),
1055                     user.getGrantedAuthorities(getUserLevel(authentication
1056                             .getName())));
1057             return authentication;
1058
1059         } else {
1060             throw new BadCredentialsException(
1061                     "Username or credentials did not match");
1062         }
1063
1064     }
1065
1066     // Following are setters for use in unit testing
1067     void setLocalUserConfigList(ConcurrentMap<String, UserConfig> ucl) {
1068         if (ucl != null) {
1069             this.localUserConfigList = ucl;
1070         }
1071     }
1072
1073     void setRemoteServerConfigList(ConcurrentMap<String, ServerConfig> scl) {
1074         if (scl != null) {
1075             this.remoteServerConfigList = scl;
1076         }
1077     }
1078
1079     void setAuthorizationConfList(ConcurrentMap<String, AuthorizationConfig> acl) {
1080         if (acl != null) {
1081             this.authorizationConfList = acl;
1082         }
1083     }
1084
1085     void setActiveUsers(ConcurrentMap<String, AuthenticatedUser> au) {
1086         if (au != null) {
1087             this.activeUsers = au;
1088         }
1089     }
1090
1091     void setAuthProviders(ConcurrentMap<String, IAAAProvider> ap) {
1092         if (ap != null) {
1093             this.authProviders = ap;
1094         }
1095     }
1096
1097     @Override
1098     public ISessionManager getSessionManager() {
1099         return this.sessionMgr;
1100     }
1101
1102     public void setSessionMgr(ISessionManager sessionMgr) {
1103         this.sessionMgr = sessionMgr;
1104     }
1105
1106     @Override
1107     public String getPassword(String username) {
1108         return localUserConfigList.get(username).getPassword();
1109     }
1110
1111     @Override
1112     public boolean isRoleInUse(String role) {
1113         if (role == null || role.isEmpty()) {
1114             return false;
1115         }
1116         // Check against controller roles
1117         if (role.equals(UserLevel.SYSTEMADMIN.toString())
1118                 || role.equals(UserLevel.NETWORKADMIN.toString())
1119                 || role.equals(UserLevel.NETWORKOPERATOR.toString())) {
1120             return true;
1121         }
1122         // Check if container roles
1123         if (containerAuthorizationClient != null) {
1124             if (containerAuthorizationClient.isApplicationRole(role)) {
1125                 return true;
1126             }
1127         }
1128         // Finally if application role
1129         if (applicationAuthorizationClients != null) {
1130             for (IResourceAuthorization client : this.applicationAuthorizationClients) {
1131                 if (client.isApplicationRole(role)) {
1132                     return true;
1133                 }
1134             }
1135         }
1136         return false;
1137     }
1138 }