| /* | |
| * Copyright 2011 gitblit.com. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package com.gitblit; | |
| import java.io.File; | |
| import java.io.FileWriter; | |
| import java.io.IOException; | |
| import java.text.MessageFormat; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.Properties; | |
| import java.util.Set; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| import com.gitblit.Constants.AccessPermission; | |
| import com.gitblit.models.TeamModel; | |
| import com.gitblit.models.UserModel; | |
| import com.gitblit.utils.ArrayUtils; | |
| import com.gitblit.utils.DeepCopier; | |
| import com.gitblit.utils.StringUtils; | |
| /** | |
| * FileUserService is Gitblit's original default user service implementation. | |
| * | |
| * Users and their repository memberships are stored in a simple properties file | |
| * which is cached and dynamically reloaded when modified. | |
| * | |
| * This class was deprecated in Gitblit 0.8.0 in favor of ConfigUserService | |
| * which is still a human-readable, editable, plain-text file but it is more | |
| * flexible for storing additional fields. | |
| * | |
| * @author James Moger | |
| * | |
| */ | |
| @Deprecated | |
| public class FileUserService extends FileSettings implements IUserService { | |
| private final Logger logger = LoggerFactory.getLogger(FileUserService.class); | |
| private final Map<String, String> cookies = new ConcurrentHashMap<String, String>(); | |
| private final Map<String, TeamModel> teams = new ConcurrentHashMap<String, TeamModel>(); | |
| public FileUserService(File realmFile) { | |
| super(realmFile.getAbsolutePath()); | |
| } | |
| /** | |
| * Setup the user service. | |
| * | |
| * @param settings | |
| * @since 0.7.0 | |
| */ | |
| @Override | |
| public void setup(IStoredSettings settings) { | |
| } | |
| /** | |
| * Does the user service support changes to credentials? | |
| * | |
| * @return true or false | |
| * @since 1.0.0 | |
| */ | |
| @Override | |
| public boolean supportsCredentialChanges() { | |
| return true; | |
| } | |
| /** | |
| * Does the user service support changes to user display name? | |
| * | |
| * @return true or false | |
| * @since 1.0.0 | |
| */ | |
| @Override | |
| public boolean supportsDisplayNameChanges() { | |
| return false; | |
| } | |
| /** | |
| * Does the user service support changes to user email address? | |
| * | |
| * @return true or false | |
| * @since 1.0.0 | |
| */ | |
| @Override | |
| public boolean supportsEmailAddressChanges() { | |
| return false; | |
| } | |
| /** | |
| * Does the user service support changes to team memberships? | |
| * | |
| * @return true or false | |
| * @since 1.0.0 | |
| */ | |
| public boolean supportsTeamMembershipChanges() { | |
| return true; | |
| } | |
| /** | |
| * Does the user service support cookie authentication? | |
| * | |
| * @return true or false | |
| */ | |
| @Override | |
| public boolean supportsCookies() { | |
| return true; | |
| } | |
| /** | |
| * Returns the cookie value for the specified user. | |
| * | |
| * @param model | |
| * @return cookie value | |
| */ | |
| @Override | |
| public String getCookie(UserModel model) { | |
| if (!StringUtils.isEmpty(model.cookie)) { | |
| return model.cookie; | |
| } | |
| Properties allUsers = super.read(); | |
| String value = allUsers.getProperty(model.username); | |
| String[] roles = value.split(","); | |
| String password = roles[0]; | |
| String cookie = StringUtils.getSHA1(model.username + password); | |
| return cookie; | |
| } | |
| /** | |
| * Authenticate a user based on their cookie. | |
| * | |
| * @param cookie | |
| * @return a user object or null | |
| */ | |
| @Override | |
| public UserModel authenticate(char[] cookie) { | |
| String hash = new String(cookie); | |
| if (StringUtils.isEmpty(hash)) { | |
| return null; | |
| } | |
| read(); | |
| UserModel model = null; | |
| if (cookies.containsKey(hash)) { | |
| String username = cookies.get(hash); | |
| model = getUserModel(username); | |
| } | |
| return model; | |
| } | |
| /** | |
| * Authenticate a user based on a username and password. | |
| * | |
| * @param username | |
| * @param password | |
| * @return a user object or null | |
| */ | |
| @Override | |
| public UserModel authenticate(String username, char[] password) { | |
| Properties allUsers = read(); | |
| String userInfo = allUsers.getProperty(username); | |
| if (StringUtils.isEmpty(userInfo)) { | |
| return null; | |
| } | |
| UserModel returnedUser = null; | |
| UserModel user = getUserModel(username); | |
| if (user.password.startsWith(StringUtils.MD5_TYPE)) { | |
| // password digest | |
| String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password)); | |
| if (user.password.equalsIgnoreCase(md5)) { | |
| returnedUser = user; | |
| } | |
| } else if (user.password.startsWith(StringUtils.COMBINED_MD5_TYPE)) { | |
| // username+password digest | |
| String md5 = StringUtils.COMBINED_MD5_TYPE | |
| + StringUtils.getMD5(username.toLowerCase() + new String(password)); | |
| if (user.password.equalsIgnoreCase(md5)) { | |
| returnedUser = user; | |
| } | |
| } else if (user.password.equals(new String(password))) { | |
| // plain-text password | |
| returnedUser = user; | |
| } | |
| return returnedUser; | |
| } | |
| /** | |
| * Logout a user. | |
| * | |
| * @param user | |
| */ | |
| @Override | |
| public void logout(UserModel user) { | |
| } | |
| /** | |
| * Retrieve the user object for the specified username. | |
| * | |
| * @param username | |
| * @return a user object or null | |
| */ | |
| @Override | |
| public UserModel getUserModel(String username) { | |
| Properties allUsers = read(); | |
| String userInfo = allUsers.getProperty(username.toLowerCase()); | |
| if (userInfo == null) { | |
| return null; | |
| } | |
| UserModel model = new UserModel(username.toLowerCase()); | |
| String[] userValues = userInfo.split(","); | |
| model.password = userValues[0]; | |
| for (int i = 1; i < userValues.length; i++) { | |
| String role = userValues[i]; | |
| switch (role.charAt(0)) { | |
| case '#': | |
| // Permissions | |
| if (role.equalsIgnoreCase(Constants.ADMIN_ROLE)) { | |
| model.canAdmin = true; | |
| } else if (role.equalsIgnoreCase(Constants.FORK_ROLE)) { | |
| model.canFork = true; | |
| } else if (role.equalsIgnoreCase(Constants.CREATE_ROLE)) { | |
| model.canCreate = true; | |
| } else if (role.equalsIgnoreCase(Constants.NOT_FEDERATED_ROLE)) { | |
| model.excludeFromFederation = true; | |
| } | |
| break; | |
| default: | |
| model.addRepositoryPermission(role); | |
| } | |
| } | |
| // set the teams for the user | |
| for (TeamModel team : teams.values()) { | |
| if (team.hasUser(username)) { | |
| model.teams.add(DeepCopier.copy(team)); | |
| } | |
| } | |
| return model; | |
| } | |
| /** | |
| * Updates/writes a complete user object. | |
| * | |
| * @param model | |
| * @return true if update is successful | |
| */ | |
| @Override | |
| public boolean updateUserModel(UserModel model) { | |
| return updateUserModel(model.username, model); | |
| } | |
| /** | |
| * Updates/writes all specified user objects. | |
| * | |
| * @param model a list of user models | |
| * @return true if update is successful | |
| * @since 1.2.0 | |
| */ | |
| @Override | |
| public boolean updateUserModels(List<UserModel> models) { | |
| try { | |
| Properties allUsers = read(); | |
| for (UserModel model : models) { | |
| updateUserCache(allUsers, model.username, model); | |
| } | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to update {0} user models!", models.size()), | |
| t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Updates/writes and replaces a complete user object keyed by username. | |
| * This method allows for renaming a user. | |
| * | |
| * @param username | |
| * the old username | |
| * @param model | |
| * the user object to use for username | |
| * @return true if update is successful | |
| */ | |
| @Override | |
| public boolean updateUserModel(String username, UserModel model) { | |
| try { | |
| Properties allUsers = read(); | |
| updateUserCache(allUsers, username, model); | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to update user model {0}!", model.username), | |
| t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Updates/writes and replaces a complete user object keyed by username. | |
| * This method allows for renaming a user. | |
| * | |
| * @param username | |
| * the old username | |
| * @param model | |
| * the user object to use for username | |
| * @return true if update is successful | |
| */ | |
| private boolean updateUserCache(Properties allUsers, String username, UserModel model) { | |
| try { | |
| UserModel oldUser = getUserModel(username); | |
| List<String> roles; | |
| if (model.permissions == null) { | |
| roles = new ArrayList<String>(); | |
| } else { | |
| // discrete repository permissions | |
| roles = new ArrayList<String>(); | |
| for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) { | |
| if (entry.getValue().exceeds(AccessPermission.NONE)) { | |
| // code:repository (e.g. RW+:~james/myrepo.git | |
| roles.add(entry.getValue().asRole(entry.getKey())); | |
| } | |
| } | |
| } | |
| // Permissions | |
| if (model.canAdmin) { | |
| roles.add(Constants.ADMIN_ROLE); | |
| } | |
| if (model.canFork) { | |
| roles.add(Constants.FORK_ROLE); | |
| } | |
| if (model.canCreate) { | |
| roles.add(Constants.CREATE_ROLE); | |
| } | |
| if (model.excludeFromFederation) { | |
| roles.add(Constants.NOT_FEDERATED_ROLE); | |
| } | |
| StringBuilder sb = new StringBuilder(); | |
| if (!StringUtils.isEmpty(model.password)) { | |
| sb.append(model.password); | |
| } | |
| sb.append(','); | |
| for (String role : roles) { | |
| sb.append(role); | |
| sb.append(','); | |
| } | |
| // trim trailing comma | |
| sb.setLength(sb.length() - 1); | |
| allUsers.remove(username.toLowerCase()); | |
| allUsers.put(model.username.toLowerCase(), sb.toString()); | |
| // null check on "final" teams because JSON-sourced UserModel | |
| // can have a null teams object | |
| if (model.teams != null) { | |
| // update team cache | |
| for (TeamModel team : model.teams) { | |
| TeamModel t = getTeamModel(team.name); | |
| if (t == null) { | |
| // new team | |
| t = team; | |
| } | |
| t.removeUser(username); | |
| t.addUser(model.username); | |
| updateTeamCache(allUsers, t.name, t); | |
| } | |
| // check for implicit team removal | |
| if (oldUser != null) { | |
| for (TeamModel team : oldUser.teams) { | |
| if (!model.isTeamMember(team.name)) { | |
| team.removeUser(username); | |
| updateTeamCache(allUsers, team.name, team); | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to update user model {0}!", model.username), | |
| t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Deletes the user object from the user service. | |
| * | |
| * @param model | |
| * @return true if successful | |
| */ | |
| @Override | |
| public boolean deleteUserModel(UserModel model) { | |
| return deleteUser(model.username); | |
| } | |
| /** | |
| * Delete the user object with the specified username | |
| * | |
| * @param username | |
| * @return true if successful | |
| */ | |
| @Override | |
| public boolean deleteUser(String username) { | |
| try { | |
| // Read realm file | |
| Properties allUsers = read(); | |
| UserModel user = getUserModel(username); | |
| allUsers.remove(username); | |
| for (TeamModel team : user.teams) { | |
| TeamModel t = getTeamModel(team.name); | |
| if (t == null) { | |
| // new team | |
| t = team; | |
| } | |
| t.removeUser(username); | |
| updateTeamCache(allUsers, t.name, t); | |
| } | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to delete user {0}!", username), t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Returns the list of all users available to the login service. | |
| * | |
| * @return list of all usernames | |
| */ | |
| @Override | |
| public List<String> getAllUsernames() { | |
| Properties allUsers = read(); | |
| List<String> list = new ArrayList<String>(); | |
| for (String user : allUsers.stringPropertyNames()) { | |
| if (user.charAt(0) == '@') { | |
| // skip team user definitions | |
| continue; | |
| } | |
| list.add(user); | |
| } | |
| Collections.sort(list); | |
| return list; | |
| } | |
| /** | |
| * Returns the list of all users available to the login service. | |
| * | |
| * @return list of all usernames | |
| */ | |
| @Override | |
| public List<UserModel> getAllUsers() { | |
| read(); | |
| List<UserModel> list = new ArrayList<UserModel>(); | |
| for (String username : getAllUsernames()) { | |
| list.add(getUserModel(username)); | |
| } | |
| Collections.sort(list); | |
| return list; | |
| } | |
| /** | |
| * Returns the list of all users who are allowed to bypass the access | |
| * restriction placed on the specified repository. | |
| * | |
| * @param role | |
| * the repository name | |
| * @return list of all usernames that can bypass the access restriction | |
| */ | |
| @Override | |
| public List<String> getUsernamesForRepositoryRole(String role) { | |
| List<String> list = new ArrayList<String>(); | |
| try { | |
| Properties allUsers = read(); | |
| for (String username : allUsers.stringPropertyNames()) { | |
| if (username.charAt(0) == '@') { | |
| continue; | |
| } | |
| String value = allUsers.getProperty(username); | |
| String[] values = value.split(","); | |
| // skip first value (password) | |
| for (int i = 1; i < values.length; i++) { | |
| String r = values[i]; | |
| if (r.equalsIgnoreCase(role)) { | |
| list.add(username); | |
| break; | |
| } | |
| } | |
| } | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t); | |
| } | |
| Collections.sort(list); | |
| return list; | |
| } | |
| /** | |
| * Sets the list of all users who are allowed to bypass the access | |
| * restriction placed on the specified repository. | |
| * | |
| * @param role | |
| * the repository name | |
| * @param usernames | |
| * @return true if successful | |
| */ | |
| @Override | |
| public boolean setUsernamesForRepositoryRole(String role, List<String> usernames) { | |
| try { | |
| Set<String> specifiedUsers = new HashSet<String>(usernames); | |
| Set<String> needsAddRole = new HashSet<String>(specifiedUsers); | |
| Set<String> needsRemoveRole = new HashSet<String>(); | |
| // identify users which require add and remove role | |
| Properties allUsers = read(); | |
| for (String username : allUsers.stringPropertyNames()) { | |
| String value = allUsers.getProperty(username); | |
| String[] values = value.split(","); | |
| // skip first value (password) | |
| for (int i = 1; i < values.length; i++) { | |
| String r = values[i]; | |
| if (r.equalsIgnoreCase(role)) { | |
| // user has role, check against revised user list | |
| if (specifiedUsers.contains(username)) { | |
| needsAddRole.remove(username); | |
| } else { | |
| // remove role from user | |
| needsRemoveRole.add(username); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| // add roles to users | |
| for (String user : needsAddRole) { | |
| String userValues = allUsers.getProperty(user); | |
| userValues += "," + role; | |
| allUsers.put(user, userValues); | |
| } | |
| // remove role from user | |
| for (String user : needsRemoveRole) { | |
| String[] values = allUsers.getProperty(user).split(","); | |
| String password = values[0]; | |
| StringBuilder sb = new StringBuilder(); | |
| sb.append(password); | |
| sb.append(','); | |
| // skip first value (password) | |
| for (int i = 1; i < values.length; i++) { | |
| String value = values[i]; | |
| if (!value.equalsIgnoreCase(role)) { | |
| sb.append(value); | |
| sb.append(','); | |
| } | |
| } | |
| sb.setLength(sb.length() - 1); | |
| // update properties | |
| allUsers.put(user, sb.toString()); | |
| } | |
| // persist changes | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Renames a repository role. | |
| * | |
| * @param oldRole | |
| * @param newRole | |
| * @return true if successful | |
| */ | |
| @Override | |
| public boolean renameRepositoryRole(String oldRole, String newRole) { | |
| try { | |
| Properties allUsers = read(); | |
| Set<String> needsRenameRole = new HashSet<String>(); | |
| // identify users which require role rename | |
| for (String username : allUsers.stringPropertyNames()) { | |
| String value = allUsers.getProperty(username); | |
| String[] roles = value.split(","); | |
| // skip first value (password) | |
| for (int i = 1; i < roles.length; i++) { | |
| String repository = AccessPermission.repositoryFromRole(roles[i]); | |
| if (repository.equalsIgnoreCase(oldRole)) { | |
| needsRenameRole.add(username); | |
| break; | |
| } | |
| } | |
| } | |
| // rename role for identified users | |
| for (String user : needsRenameRole) { | |
| String userValues = allUsers.getProperty(user); | |
| String[] values = userValues.split(","); | |
| String password = values[0]; | |
| StringBuilder sb = new StringBuilder(); | |
| sb.append(password); | |
| sb.append(','); | |
| sb.append(newRole); | |
| sb.append(','); | |
| // skip first value (password) | |
| for (int i = 1; i < values.length; i++) { | |
| String repository = AccessPermission.repositoryFromRole(values[i]); | |
| if (repository.equalsIgnoreCase(oldRole)) { | |
| AccessPermission permission = AccessPermission.permissionFromRole(values[i]); | |
| sb.append(permission.asRole(newRole)); | |
| sb.append(','); | |
| } else { | |
| sb.append(values[i]); | |
| sb.append(','); | |
| } | |
| } | |
| sb.setLength(sb.length() - 1); | |
| // update properties | |
| allUsers.put(user, sb.toString()); | |
| } | |
| // persist changes | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error( | |
| MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Removes a repository role from all users. | |
| * | |
| * @param role | |
| * @return true if successful | |
| */ | |
| @Override | |
| public boolean deleteRepositoryRole(String role) { | |
| try { | |
| Properties allUsers = read(); | |
| Set<String> needsDeleteRole = new HashSet<String>(); | |
| // identify users which require role rename | |
| for (String username : allUsers.stringPropertyNames()) { | |
| String value = allUsers.getProperty(username); | |
| String[] roles = value.split(","); | |
| // skip first value (password) | |
| for (int i = 1; i < roles.length; i++) { | |
| String repository = AccessPermission.repositoryFromRole(roles[i]); | |
| if (repository.equalsIgnoreCase(role)) { | |
| needsDeleteRole.add(username); | |
| break; | |
| } | |
| } | |
| } | |
| // delete role for identified users | |
| for (String user : needsDeleteRole) { | |
| String userValues = allUsers.getProperty(user); | |
| String[] values = userValues.split(","); | |
| String password = values[0]; | |
| StringBuilder sb = new StringBuilder(); | |
| sb.append(password); | |
| sb.append(','); | |
| // skip first value (password) | |
| for (int i = 1; i < values.length; i++) { | |
| String repository = AccessPermission.repositoryFromRole(values[i]); | |
| if (!repository.equalsIgnoreCase(role)) { | |
| sb.append(values[i]); | |
| sb.append(','); | |
| } | |
| } | |
| sb.setLength(sb.length() - 1); | |
| // update properties | |
| allUsers.put(user, sb.toString()); | |
| } | |
| // persist changes | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to delete role {0}!", role), t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Writes the properties file. | |
| * | |
| * @param properties | |
| * @throws IOException | |
| */ | |
| private void write(Properties properties) throws IOException { | |
| // Write a temporary copy of the users file | |
| File realmFileCopy = new File(propertiesFile.getAbsolutePath() + ".tmp"); | |
| FileWriter writer = new FileWriter(realmFileCopy); | |
| properties | |
| .store(writer, | |
| " Gitblit realm file format:\n username=password,\\#permission,repository1,repository2...\n @teamname=!username1,!username2,!username3,repository1,repository2..."); | |
| writer.close(); | |
| // If the write is successful, delete the current file and rename | |
| // the temporary copy to the original filename. | |
| if (realmFileCopy.exists() && realmFileCopy.length() > 0) { | |
| if (propertiesFile.exists()) { | |
| if (!propertiesFile.delete()) { | |
| throw new IOException(MessageFormat.format("Failed to delete {0}!", | |
| propertiesFile.getAbsolutePath())); | |
| } | |
| } | |
| if (!realmFileCopy.renameTo(propertiesFile)) { | |
| throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!", | |
| realmFileCopy.getAbsolutePath(), propertiesFile.getAbsolutePath())); | |
| } | |
| } else { | |
| throw new IOException(MessageFormat.format("Failed to save {0}!", | |
| realmFileCopy.getAbsolutePath())); | |
| } | |
| } | |
| /** | |
| * Reads the properties file and rebuilds the in-memory cookie lookup table. | |
| */ | |
| @Override | |
| protected synchronized Properties read() { | |
| long lastRead = lastModified(); | |
| boolean reload = forceReload(); | |
| Properties allUsers = super.read(); | |
| if (reload || (lastRead != lastModified())) { | |
| // reload hash cache | |
| cookies.clear(); | |
| teams.clear(); | |
| for (String username : allUsers.stringPropertyNames()) { | |
| String value = allUsers.getProperty(username); | |
| String[] roles = value.split(","); | |
| if (username.charAt(0) == '@') { | |
| // team definition | |
| TeamModel team = new TeamModel(username.substring(1)); | |
| List<String> repositories = new ArrayList<String>(); | |
| List<String> users = new ArrayList<String>(); | |
| List<String> mailingLists = new ArrayList<String>(); | |
| List<String> preReceive = new ArrayList<String>(); | |
| List<String> postReceive = new ArrayList<String>(); | |
| for (String role : roles) { | |
| if (role.charAt(0) == '!') { | |
| users.add(role.substring(1)); | |
| } else if (role.charAt(0) == '&') { | |
| mailingLists.add(role.substring(1)); | |
| } else if (role.charAt(0) == '^') { | |
| preReceive.add(role.substring(1)); | |
| } else if (role.charAt(0) == '%') { | |
| postReceive.add(role.substring(1)); | |
| } else { | |
| switch (role.charAt(0)) { | |
| case '#': | |
| // Permissions | |
| if (role.equalsIgnoreCase(Constants.ADMIN_ROLE)) { | |
| team.canAdmin = true; | |
| } else if (role.equalsIgnoreCase(Constants.FORK_ROLE)) { | |
| team.canFork = true; | |
| } else if (role.equalsIgnoreCase(Constants.CREATE_ROLE)) { | |
| team.canCreate = true; | |
| } | |
| break; | |
| default: | |
| repositories.add(role); | |
| } | |
| repositories.add(role); | |
| } | |
| } | |
| if (!team.canAdmin) { | |
| // only read permissions for non-admin teams | |
| team.addRepositoryPermissions(repositories); | |
| } | |
| team.addUsers(users); | |
| team.addMailingLists(mailingLists); | |
| team.preReceiveScripts.addAll(preReceive); | |
| team.postReceiveScripts.addAll(postReceive); | |
| teams.put(team.name.toLowerCase(), team); | |
| } else { | |
| // user definition | |
| String password = roles[0]; | |
| cookies.put(StringUtils.getSHA1(username.toLowerCase() + password), username.toLowerCase()); | |
| } | |
| } | |
| } | |
| return allUsers; | |
| } | |
| @Override | |
| public String toString() { | |
| return getClass().getSimpleName() + "(" + propertiesFile.getAbsolutePath() + ")"; | |
| } | |
| /** | |
| * Returns the list of all teams available to the login service. | |
| * | |
| * @return list of all teams | |
| * @since 0.8.0 | |
| */ | |
| @Override | |
| public List<String> getAllTeamNames() { | |
| List<String> list = new ArrayList<String>(teams.keySet()); | |
| Collections.sort(list); | |
| return list; | |
| } | |
| /** | |
| * Returns the list of all teams available to the login service. | |
| * | |
| * @return list of all teams | |
| * @since 0.8.0 | |
| */ | |
| @Override | |
| public List<TeamModel> getAllTeams() { | |
| List<TeamModel> list = new ArrayList<TeamModel>(teams.values()); | |
| list = DeepCopier.copy(list); | |
| Collections.sort(list); | |
| return list; | |
| } | |
| /** | |
| * Returns the list of all teams who are allowed to bypass the access | |
| * restriction placed on the specified repository. | |
| * | |
| * @param role | |
| * the repository name | |
| * @return list of all teamnames that can bypass the access restriction | |
| */ | |
| @Override | |
| public List<String> getTeamnamesForRepositoryRole(String role) { | |
| List<String> list = new ArrayList<String>(); | |
| try { | |
| Properties allUsers = read(); | |
| for (String team : allUsers.stringPropertyNames()) { | |
| if (team.charAt(0) != '@') { | |
| // skip users | |
| continue; | |
| } | |
| String value = allUsers.getProperty(team); | |
| String[] values = value.split(","); | |
| for (int i = 0; i < values.length; i++) { | |
| String r = values[i]; | |
| if (r.equalsIgnoreCase(role)) { | |
| // strip leading @ | |
| list.add(team.substring(1)); | |
| break; | |
| } | |
| } | |
| } | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to get teamnames for role {0}!", role), t); | |
| } | |
| Collections.sort(list); | |
| return list; | |
| } | |
| /** | |
| * Sets the list of all teams who are allowed to bypass the access | |
| * restriction placed on the specified repository. | |
| * | |
| * @param role | |
| * the repository name | |
| * @param teamnames | |
| * @return true if successful | |
| */ | |
| @Override | |
| public boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames) { | |
| try { | |
| Set<String> specifiedTeams = new HashSet<String>(teamnames); | |
| Set<String> needsAddRole = new HashSet<String>(specifiedTeams); | |
| Set<String> needsRemoveRole = new HashSet<String>(); | |
| // identify teams which require add and remove role | |
| Properties allUsers = read(); | |
| for (String team : allUsers.stringPropertyNames()) { | |
| if (team.charAt(0) != '@') { | |
| // skip users | |
| continue; | |
| } | |
| String name = team.substring(1); | |
| String value = allUsers.getProperty(team); | |
| String[] values = value.split(","); | |
| for (int i = 0; i < values.length; i++) { | |
| String r = values[i]; | |
| if (r.equalsIgnoreCase(role)) { | |
| // team has role, check against revised team list | |
| if (specifiedTeams.contains(name)) { | |
| needsAddRole.remove(name); | |
| } else { | |
| // remove role from team | |
| needsRemoveRole.add(name); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| // add roles to teams | |
| for (String name : needsAddRole) { | |
| String team = "@" + name; | |
| String teamValues = allUsers.getProperty(team); | |
| teamValues += "," + role; | |
| allUsers.put(team, teamValues); | |
| } | |
| // remove role from team | |
| for (String name : needsRemoveRole) { | |
| String team = "@" + name; | |
| String[] values = allUsers.getProperty(team).split(","); | |
| StringBuilder sb = new StringBuilder(); | |
| for (int i = 0; i < values.length; i++) { | |
| String value = values[i]; | |
| if (!value.equalsIgnoreCase(role)) { | |
| sb.append(value); | |
| sb.append(','); | |
| } | |
| } | |
| sb.setLength(sb.length() - 1); | |
| // update properties | |
| allUsers.put(team, sb.toString()); | |
| } | |
| // persist changes | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to set teamnames for role {0}!", role), t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Retrieve the team object for the specified team name. | |
| * | |
| * @param teamname | |
| * @return a team object or null | |
| * @since 0.8.0 | |
| */ | |
| @Override | |
| public TeamModel getTeamModel(String teamname) { | |
| read(); | |
| TeamModel team = teams.get(teamname.toLowerCase()); | |
| if (team != null) { | |
| // clone the model, otherwise all changes to this object are | |
| // live and unpersisted | |
| team = DeepCopier.copy(team); | |
| } | |
| return team; | |
| } | |
| /** | |
| * Updates/writes a complete team object. | |
| * | |
| * @param model | |
| * @return true if update is successful | |
| * @since 0.8.0 | |
| */ | |
| @Override | |
| public boolean updateTeamModel(TeamModel model) { | |
| return updateTeamModel(model.name, model); | |
| } | |
| /** | |
| * Updates/writes all specified team objects. | |
| * | |
| * @param models a list of team models | |
| * @return true if update is successful | |
| * @since 1.2.0 | |
| */ | |
| public boolean updateTeamModels(List<TeamModel> models) { | |
| try { | |
| Properties allUsers = read(); | |
| for (TeamModel model : models) { | |
| updateTeamCache(allUsers, model.name, model); | |
| } | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to update {0} team models!", models.size()), t); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Updates/writes and replaces a complete team object keyed by teamname. | |
| * This method allows for renaming a team. | |
| * | |
| * @param teamname | |
| * the old teamname | |
| * @param model | |
| * the team object to use for teamname | |
| * @return true if update is successful | |
| * @since 0.8.0 | |
| */ | |
| @Override | |
| public boolean updateTeamModel(String teamname, TeamModel model) { | |
| try { | |
| Properties allUsers = read(); | |
| updateTeamCache(allUsers, teamname, model); | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to update team model {0}!", model.name), t); | |
| } | |
| return false; | |
| } | |
| private void updateTeamCache(Properties allUsers, String teamname, TeamModel model) { | |
| StringBuilder sb = new StringBuilder(); | |
| List<String> roles; | |
| if (model.permissions == null) { | |
| // legacy, use repository list | |
| if (model.repositories != null) { | |
| roles = new ArrayList<String>(model.repositories); | |
| } else { | |
| roles = new ArrayList<String>(); | |
| } | |
| } else { | |
| // discrete repository permissions | |
| roles = new ArrayList<String>(); | |
| for (Map.Entry<String, AccessPermission> entry : model.permissions.entrySet()) { | |
| if (entry.getValue().exceeds(AccessPermission.NONE)) { | |
| // code:repository (e.g. RW+:~james/myrepo.git | |
| roles.add(entry.getValue().asRole(entry.getKey())); | |
| } | |
| } | |
| } | |
| // Permissions | |
| if (model.canAdmin) { | |
| roles.add(Constants.ADMIN_ROLE); | |
| } | |
| if (model.canFork) { | |
| roles.add(Constants.FORK_ROLE); | |
| } | |
| if (model.canCreate) { | |
| roles.add(Constants.CREATE_ROLE); | |
| } | |
| for (String role : roles) { | |
| sb.append(role); | |
| sb.append(','); | |
| } | |
| if (!ArrayUtils.isEmpty(model.users)) { | |
| for (String user : model.users) { | |
| sb.append('!'); | |
| sb.append(user); | |
| sb.append(','); | |
| } | |
| } | |
| if (!ArrayUtils.isEmpty(model.mailingLists)) { | |
| for (String address : model.mailingLists) { | |
| sb.append('&'); | |
| sb.append(address); | |
| sb.append(','); | |
| } | |
| } | |
| if (!ArrayUtils.isEmpty(model.preReceiveScripts)) { | |
| for (String script : model.preReceiveScripts) { | |
| sb.append('^'); | |
| sb.append(script); | |
| sb.append(','); | |
| } | |
| } | |
| if (!ArrayUtils.isEmpty(model.postReceiveScripts)) { | |
| for (String script : model.postReceiveScripts) { | |
| sb.append('%'); | |
| sb.append(script); | |
| sb.append(','); | |
| } | |
| } | |
| // trim trailing comma | |
| sb.setLength(sb.length() - 1); | |
| allUsers.remove("@" + teamname); | |
| allUsers.put("@" + model.name, sb.toString()); | |
| // update team cache | |
| teams.remove(teamname.toLowerCase()); | |
| teams.put(model.name.toLowerCase(), model); | |
| } | |
| /** | |
| * Deletes the team object from the user service. | |
| * | |
| * @param model | |
| * @return true if successful | |
| * @since 0.8.0 | |
| */ | |
| @Override | |
| public boolean deleteTeamModel(TeamModel model) { | |
| return deleteTeam(model.name); | |
| } | |
| /** | |
| * Delete the team object with the specified teamname | |
| * | |
| * @param teamname | |
| * @return true if successful | |
| * @since 0.8.0 | |
| */ | |
| @Override | |
| public boolean deleteTeam(String teamname) { | |
| Properties allUsers = read(); | |
| teams.remove(teamname.toLowerCase()); | |
| allUsers.remove("@" + teamname); | |
| try { | |
| write(allUsers); | |
| return true; | |
| } catch (Throwable t) { | |
| logger.error(MessageFormat.format("Failed to delete team {0}!", teamname), t); | |
| } | |
| return false; | |
| } | |
| } |