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