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