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