James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
commit | author | age
8f1c9f 1 /*
JM 2  * Copyright 2013 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit.manager;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.text.MessageFormat;
21 import java.util.ArrayList;
22 import java.util.Collection;
04a985 23 import java.util.HashMap;
8f1c9f 24 import java.util.List;
04a985 25 import java.util.Map;
8f1c9f 26
JM 27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.gitblit.ConfigUserService;
23e08c 31 import com.gitblit.Constants;
8f1c9f 32 import com.gitblit.IStoredSettings;
JM 33 import com.gitblit.IUserService;
34 import com.gitblit.Keys;
ca4d98 35 import com.gitblit.extensions.UserTeamLifeCycleListener;
8f1c9f 36 import com.gitblit.models.TeamModel;
JM 37 import com.gitblit.models.UserModel;
38 import com.gitblit.utils.StringUtils;
f9980e 39 import com.google.inject.Inject;
JM 40 import com.google.inject.Singleton;
8f1c9f 41
JM 42 /**
43  * The user manager manages persistence and retrieval of users and teams.
44  *
45  * @author James Moger
46  *
47  */
f9980e 48 @Singleton
8f1c9f 49 public class UserManager implements IUserManager {
JM 50
51     private final Logger logger = LoggerFactory.getLogger(getClass());
52
53     private final IStoredSettings settings;
54
55     private final IRuntimeManager runtimeManager;
56
ca4d98 57     private final IPluginManager pluginManager;
JM 58
04a985 59     private final Map<String, String> legacyBackingServices;
JM 60
8f1c9f 61     private IUserService userService;
JM 62
1b34b0 63     @Inject
ca4d98 64     public UserManager(IRuntimeManager runtimeManager, IPluginManager pluginManager) {
8f1c9f 65         this.settings = runtimeManager.getSettings();
JM 66         this.runtimeManager = runtimeManager;
ca4d98 67         this.pluginManager = pluginManager;
04a985 68
JM 69         // map of legacy realm backing user services
70         legacyBackingServices = new HashMap<String, String>();
71         legacyBackingServices.put("com.gitblit.HtpasswdUserService", "realm.htpasswd.backingUserService");
72         legacyBackingServices.put("com.gitblit.LdapUserService", "realm.ldap.backingUserService");
73         legacyBackingServices.put("com.gitblit.PAMUserService", "realm.pam.backingUserService");
74         legacyBackingServices.put("com.gitblit.RedmineUserService", "realm.redmine.backingUserService");
75         legacyBackingServices.put("com.gitblit.SalesforceUserService", "realm.salesforce.backingUserService");
76         legacyBackingServices.put("com.gitblit.WindowsUserService", "realm.windows.backingUserService");
8f1c9f 77     }
JM 78
79     /**
04a985 80      * Set the user service. The user service authenticates *local* users and is
JM 81      * responsible for persisting and retrieving all users and all teams.
8f1c9f 82      *
JM 83      * @param userService
84      */
85     public void setUserService(IUserService userService) {
86         this.userService = userService;
87         this.userService.setup(runtimeManager);
328692 88         logger.info(userService.toString());
8f1c9f 89     }
JM 90
91     @Override
04a985 92     public void setup(IRuntimeManager runtimeManager) {
JM 93         // NOOP
94     }
95
96     @Override
269c50 97     public UserManager start() {
8f1c9f 98         if (this.userService == null) {
04a985 99             String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.conf");
8f1c9f 100             IUserService service = null;
04a985 101             if (legacyBackingServices.containsKey(realm)) {
JM 102                 // create the user service from the legacy config
103                 String realmKey = legacyBackingServices.get(realm);
104                 logger.warn("");
088b6f 105                 logger.warn(Constants.BORDER2);
04a985 106                 logger.warn(" Key '{}' is obsolete!", realmKey);
JM 107                 logger.warn(" Please set '{}={}'", Keys.realm.userService, settings.getString(realmKey, "${baseFolder}/users.conf"));
088b6f 108                 logger.warn(Constants.BORDER2);
04a985 109                 logger.warn("");
JM 110                 File realmFile = runtimeManager.getFileOrFolder(realmKey, "${baseFolder}/users.conf");
8f1c9f 111                 service = createUserService(realmFile);
04a985 112             } else {
JM 113                 // either a file path OR a custom user service
114                 try {
115                     // check to see if this "file" is a custom user service class
116                     Class<?> realmClass = Class.forName(realm);
117                     service = (IUserService) realmClass.newInstance();
328692 118                 } catch (ClassNotFoundException t) {
04a985 119                     // typical file path configuration
JM 120                     File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
121                     service = createUserService(realmFile);
328692 122                 } catch (InstantiationException | IllegalAccessException  e) {
a3e480 123                     logger.error("failed to instantiate user service {}: {}", realm, e.getMessage());
04a985 124                 }
8f1c9f 125             }
JM 126             setUserService(service);
127         }
128         return this;
129     }
130
131     protected IUserService createUserService(File realmFile) {
132         IUserService service = null;
133         if (realmFile.getName().toLowerCase().endsWith(".conf")) {
04a985 134             // config-based realm file
8f1c9f 135             service = new ConfigUserService(realmFile);
JM 136         }
137
138         assert service != null;
139
140         if (!realmFile.exists()) {
141             // Create the Administrator account for a new realm file
142             try {
143                 realmFile.createNewFile();
144             } catch (IOException x) {
145                 logger.error(MessageFormat.format("COULD NOT CREATE REALM FILE {0}!", realmFile), x);
146             }
147             UserModel admin = new UserModel("admin");
148             admin.password = "admin";
149             admin.canAdmin = true;
150             admin.excludeFromFederation = true;
151             service.updateUserModel(admin);
152         }
153
154         return service;
155     }
156
157     @Override
269c50 158     public UserManager stop() {
8f1c9f 159         return this;
JM 160     }
161
162     /**
23e08c 163      * Returns true if the username represents an internal account
JM 164      *
165      * @param username
166      * @return true if the specified username represents an internal account
167      */
168     @Override
169     public boolean isInternalAccount(String username) {
170         return !StringUtils.isEmpty(username)
171                 && (username.equalsIgnoreCase(Constants.FEDERATION_USER)
172                         || username.equalsIgnoreCase(UserModel.ANONYMOUS.username));
173     }
174
175     /**
8f1c9f 176      * Returns the cookie value for the specified user.
JM 177      *
178      * @param model
179      * @return cookie value
180      */
181     @Override
182     public String getCookie(UserModel model) {
183         return userService.getCookie(model);
184     }
185
186     /**
04a985 187      * Retrieve the user object for the specified cookie.
8f1c9f 188      *
JM 189      * @param cookie
190      * @return a user object or null
191      */
192     @Override
04a985 193     public UserModel getUserModel(char[] cookie) {
JM 194         UserModel user = userService.getUserModel(cookie);
8f1c9f 195         return user;
JM 196     }
197
198     /**
199      * Retrieve the user object for the specified username.
200      *
201      * @param username
202      * @return a user object or null
203      */
204     @Override
205     public UserModel getUserModel(String username) {
206         if (StringUtils.isEmpty(username)) {
207             return null;
208         }
209         String usernameDecoded = StringUtils.decodeUsername(username);
210         UserModel user = userService.getUserModel(usernameDecoded);
211         return user;
212     }
213
214     /**
215      * Updates/writes a complete user object.
216      *
217      * @param model
218      * @return true if update is successful
219      */
220     @Override
221     public boolean updateUserModel(UserModel model) {
ca4d98 222         final boolean isCreate = null == userService.getUserModel(model.username);
JM 223         if (userService.updateUserModel(model)) {
224             if (isCreate) {
225                 callCreateUserListeners(model);
226             }
227             return true;
228         }
229         return false;
8f1c9f 230     }
JM 231
232     /**
233      * Updates/writes all specified user objects.
234      *
235      * @param models a list of user models
236      * @return true if update is successful
237      * @since 1.2.0
238      */
239     @Override
240     public boolean updateUserModels(Collection<UserModel> models) {
241         return userService.updateUserModels(models);
242     }
243
244     /**
245      * Adds/updates a user object keyed by username. This method allows for
246      * renaming a user.
247      *
248      * @param username
249      *            the old username
250      * @param model
251      *            the user object to use for username
252      * @return true if update is successful
253      */
254     @Override
255     public boolean updateUserModel(String username, UserModel model) {
ca4d98 256         final boolean isCreate = null == userService.getUserModel(username);
JM 257         if (userService.updateUserModel(username, model)) {
258             if (isCreate) {
259                 callCreateUserListeners(model);
260             }
261             return true;
262         }
263         return false;
8f1c9f 264     }
JM 265
266     /**
267      * Deletes the user object from the user service.
268      *
269      * @param model
270      * @return true if successful
271      */
272     @Override
273     public boolean deleteUserModel(UserModel model) {
ca4d98 274         if (userService.deleteUserModel(model)) {
JM 275             callDeleteUserListeners(model);
276             return true;
277         }
278         return false;
8f1c9f 279     }
JM 280
281     /**
282      * Delete the user object with the specified username
283      *
284      * @param username
285      * @return true if successful
286      */
287     @Override
288     public boolean deleteUser(String username) {
289         if (StringUtils.isEmpty(username)) {
290             return false;
291         }
292         String usernameDecoded = StringUtils.decodeUsername(username);
ca4d98 293         UserModel user = getUserModel(usernameDecoded);
JM 294         if (userService.deleteUser(usernameDecoded)) {
295             callDeleteUserListeners(user);
296             return true;
297         }
298         return false;
8f1c9f 299     }
JM 300
301     /**
302      * Returns the list of all users available to the login service.
303      *
304      * @return list of all usernames
305      */
306     @Override
307     public List<String> getAllUsernames() {
308         List<String> names = new ArrayList<String>(userService.getAllUsernames());
309         return names;
310     }
311
312     /**
313      * Returns the list of all users available to the login service.
314      *
315      * @return list of all users
316      * @since 0.8.0
317      */
318     @Override
319     public List<UserModel> getAllUsers() {
320         List<UserModel> users = userService.getAllUsers();
321         return users;
322     }
323
324     /**
325      * Returns the list of all teams available to the login service.
326      *
327      * @return list of all teams
328      * @since 0.8.0
329      */
330     @Override
331     public List<String> getAllTeamNames() {
04a985 332         List<String> teams = userService.getAllTeamNames();
JM 333         return teams;
8f1c9f 334     }
JM 335
336     /**
337      * Returns the list of all teams available to the login service.
338      *
339      * @return list of all teams
340      * @since 0.8.0
341      */
342     @Override
343     public List<TeamModel> getAllTeams() {
344         List<TeamModel> teams = userService.getAllTeams();
345         return teams;
346     }
347
348     /**
349      * Returns the list of all teams who are allowed to bypass the access
350      * restriction placed on the specified repository.
351      *
352      * @param role
353      *            the repository name
354      * @return list of all teams that can bypass the access restriction
355      * @since 0.8.0
356      */
357     @Override
358     public List<String> getTeamNamesForRepositoryRole(String role) {
04a985 359         List<String> teams = userService.getTeamNamesForRepositoryRole(role);
JM 360         return teams;
8f1c9f 361     }
JM 362
363     /**
364      * Retrieve the team object for the specified team name.
365      *
366      * @param teamname
367      * @return a team object or null
368      * @since 0.8.0
369      */
370     @Override
371     public TeamModel getTeamModel(String teamname) {
04a985 372         TeamModel team = userService.getTeamModel(teamname);
JM 373         return team;
8f1c9f 374     }
JM 375
376     /**
377      * Updates/writes a complete team object.
378      *
379      * @param model
380      * @return true if update is successful
381      * @since 0.8.0
382      */
383     @Override
384     public boolean updateTeamModel(TeamModel model) {
ca4d98 385         final boolean isCreate = null == userService.getTeamModel(model.name);
JM 386         if (userService.updateTeamModel(model)) {
387             if (isCreate) {
388                 callCreateTeamListeners(model);
389             }
390             return true;
391         }
392         return false;
8f1c9f 393     }
JM 394
395     /**
396      * Updates/writes all specified team objects.
397      *
398      * @param models a list of team models
399      * @return true if update is successful
400      * @since 1.2.0
401      */
402     @Override
403     public boolean updateTeamModels(Collection<TeamModel> models) {
404         return userService.updateTeamModels(models);
405     }
406
407     /**
408      * Updates/writes and replaces a complete team object keyed by teamname.
409      * This method allows for renaming a team.
410      *
411      * @param teamname
412      *            the old teamname
413      * @param model
414      *            the team object to use for teamname
415      * @return true if update is successful
416      * @since 0.8.0
417      */
418     @Override
419     public boolean updateTeamModel(String teamname, TeamModel model) {
ca4d98 420         final boolean isCreate = null == userService.getTeamModel(teamname);
JM 421         if (userService.updateTeamModel(teamname, model)) {
422             if (isCreate) {
423                 callCreateTeamListeners(model);
424             }
425             return true;
426         }
427         return false;
8f1c9f 428     }
JM 429
430     /**
431      * Deletes the team object from the user service.
432      *
433      * @param model
434      * @return true if successful
435      * @since 0.8.0
436      */
437     @Override
438     public boolean deleteTeamModel(TeamModel model) {
ca4d98 439         if (userService.deleteTeamModel(model)) {
JM 440             callDeleteTeamListeners(model);
441             return true;
442         }
443         return false;
8f1c9f 444     }
JM 445
446     /**
447      * Delete the team object with the specified teamname
448      *
449      * @param teamname
450      * @return true if successful
451      * @since 0.8.0
452      */
453     @Override
454     public boolean deleteTeam(String teamname) {
ca4d98 455         TeamModel team = userService.getTeamModel(teamname);
JM 456         if (userService.deleteTeam(teamname)) {
457             callDeleteTeamListeners(team);
458             return true;
459         }
460         return false;
8f1c9f 461     }
JM 462
463     /**
464      * Returns the list of all users who are allowed to bypass the access
465      * restriction placed on the specified repository.
466      *
467      * @param role
468      *            the repository name
469      * @return list of all usernames that can bypass the access restriction
470      * @since 0.8.0
471      */
472     @Override
473     public List<String> getUsernamesForRepositoryRole(String role) {
474         return userService.getUsernamesForRepositoryRole(role);
475     }
476
477     /**
478      * Renames a repository role.
479      *
480      * @param oldRole
481      * @param newRole
482      * @return true if successful
483      */
484     @Override
485     public boolean renameRepositoryRole(String oldRole, String newRole) {
486         return userService.renameRepositoryRole(oldRole, newRole);
487     }
488
489     /**
490      * Removes a repository role from all users.
491      *
492      * @param role
493      * @return true if successful
494      */
495     @Override
496     public boolean deleteRepositoryRole(String role) {
497         return userService.deleteRepositoryRole(role);
498     }
ca4d98 499
JM 500     protected void callCreateUserListeners(UserModel user) {
501         if (pluginManager == null || user == null) {
502             return;
503         }
504
505         for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
506             try {
507                 listener.onCreation(user);
508             } catch (Throwable t) {
509                 logger.error(String.format("failed to call plugin.onCreation%s", user.username), t);
510             }
511         }
512     }
513
514     protected void callCreateTeamListeners(TeamModel team) {
515         if (pluginManager == null || team == null) {
516             return;
517         }
518
519         for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
520             try {
521                 listener.onCreation(team);
522             } catch (Throwable t) {
523                 logger.error(String.format("failed to call plugin.onCreation %s", team.name), t);
524             }
525         }
526     }
527
528     protected void callDeleteUserListeners(UserModel user) {
529         if (pluginManager == null || user == null) {
530             return;
531         }
532
533         for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
534             try {
535                 listener.onDeletion(user);
536             } catch (Throwable t) {
537                 logger.error(String.format("failed to call plugin.onDeletion %s", user.username), t);
538             }
539         }
540     }
541
542     protected void callDeleteTeamListeners(TeamModel team) {
543         if (pluginManager == null || team == null) {
544             return;
545         }
546
547         for (UserTeamLifeCycleListener listener : pluginManager.getExtensions(UserTeamLifeCycleListener.class)) {
548             try {
549                 listener.onDeletion(team);
550             } catch (Throwable t) {
551                 logger.error(String.format("failed to call plugin.onDeletion %s", team.name), t);
552             }
553         }
554     }
8f1c9f 555 }