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