From a1ea877042b93949ef244b96e8affd65cc3f89c1 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Wed, 01 Jun 2011 20:19:51 -0400 Subject: [PATCH] Readme markdown on summary page per-repository. --- src/com/gitblit/JettyLoginService.java | 465 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 452 insertions(+), 13 deletions(-) diff --git a/src/com/gitblit/JettyLoginService.java b/src/com/gitblit/JettyLoginService.java index 93e9a19..63a9861 100644 --- a/src/com/gitblit/JettyLoginService.java +++ b/src/com/gitblit/JettyLoginService.java @@ -1,32 +1,471 @@ +/* + * 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 org.eclipse.jetty.security.HashLoginService; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.security.Principal; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.security.auth.Subject; + +import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.security.IdentityService; +import org.eclipse.jetty.security.MappedLoginService; import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.log.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.gitblit.wicket.User; +import com.gitblit.models.UserModel; -public class JettyLoginService extends HashLoginService implements ILoginService { +public class JettyLoginService extends MappedLoginService implements ILoginService { - public JettyLoginService(String realmFile) { - super(Constants.NAME, realmFile); + private final Logger logger = LoggerFactory.getLogger(JettyLoginService.class); + + private final File realmFile; + + public JettyLoginService(File realmFile) { + super(); + setName(Constants.NAME); + this.realmFile = realmFile; } - + @Override - public User authenticate(String username, char[] password) { + public UserModel authenticate(String username, char[] password) { UserIdentity identity = login(username, new String(password)); if (identity == null || identity.equals(UserIdentity.UNAUTHENTICATED_IDENTITY)) { return null; } - User user = new User(username, password); - user.canAdmin(identity.isUserInRole(Constants.ADMIN_ROLE, null)); - user.canClone(identity.isUserInRole(Constants.PULL_ROLE, null)); - user.canPush(identity.isUserInRole(Constants.PUSH_ROLE, null)); + UserModel user = new UserModel(username); + user.canAdmin = identity.isUserInRole(Constants.ADMIN_ROLE, null); + + // Add repositories + for (Principal principal : identity.getSubject().getPrincipals()) { + if (principal instanceof RolePrincipal) { + RolePrincipal role = (RolePrincipal) principal; + String roleName = role.getName(); + if (roleName.charAt(0) != '#') { + user.addRepository(roleName); + } + } + } return user; } @Override - public User authenticate(char [] cookie) { - // TODO cookie login + public UserModel getUserModel(String username) { + UserIdentity identity = _users.get(username); + if (identity == null) { + return null; + } + UserModel model = new UserModel(username); + Subject subject = identity.getSubject(); + for (Principal principal : subject.getPrincipals()) { + if (principal instanceof RolePrincipal) { + RolePrincipal role = (RolePrincipal) principal; + String name = role.getName(); + switch (name.charAt(0)) { + case '#': + // Permissions + if (name.equalsIgnoreCase(Constants.ADMIN_ROLE)) { + model.canAdmin = true; + } + break; + default: + model.addRepository(name); + } + } + } + // Retrieve the password from the realm file. + // Stupid, I know, but the password is buried within protected inner + // classes in private variables. Too much work to reflectively retrieve. + try { + Properties allUsers = readRealmFile(); + String value = allUsers.getProperty(username); + String password = value.split(",")[0]; + model.password = password; + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to read password for user {0}!", username), t); + } + return model; + } + + @Override + public boolean updateUserModel(UserModel model) { + return updateUserModel(model.username, model); + } + + @Override + public boolean updateUserModel(String username, UserModel model) { + try { + Properties allUsers = readRealmFile(); + ArrayList<String> roles = new ArrayList<String>(model.repositories); + + // Permissions + if (model.canAdmin) { + roles.add(Constants.ADMIN_ROLE); + } + + StringBuilder sb = new StringBuilder(); + 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); + allUsers.put(model.username, sb.toString()); + + writeRealmFile(allUsers); + + // Update login service + removeUser(username); + putUser(model.username, Credential.getCredential(model.password), + roles.toArray(new String[0])); + return true; + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to update user model {0}!", model.username), + t); + } + return false; + } + + @Override + public boolean deleteUserModel(UserModel model) { + return deleteUser(model.username); + } + + @Override + public boolean deleteUser(String username) { + try { + // Read realm file + Properties allUsers = readRealmFile(); + allUsers.remove(username); + writeRealmFile(allUsers); + + // Drop user from map + removeUser(username); + return true; + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to delete user {0}!", username), t); + } + return false; + } + + @Override + public List<String> getAllUsernames() { + List<String> list = new ArrayList<String>(); + list.addAll(_users.keySet()); + return list; + } + + @Override + public List<String> getUsernamesForRole(String role) { + List<String> list = new ArrayList<String>(); + try { + Properties allUsers = readRealmFile(); + 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)) { + list.add(username); + break; + } + } + } + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t); + } + return list; + } + + @Override + public boolean setUsernamesForRole(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 = readRealmFile(); + 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); + String[] values = userValues.split(","); + String password = values[0]; + String[] roles = new String[values.length - 1]; + System.arraycopy(values, 1, roles, 0, values.length - 1); + putUser(user, Credential.getCredential(password), roles); + } + + // 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(','); + List<String> revisedRoles = new ArrayList<String>(); + // skip first value (password) + for (int i = 1; i < values.length; i++) { + String value = values[i]; + if (!value.equalsIgnoreCase(role)) { + revisedRoles.add(value); + sb.append(value); + sb.append(','); + } + } + sb.setLength(sb.length() - 1); + + // update properties + allUsers.put(user, sb.toString()); + + // update memory + putUser(user, Credential.getCredential(password), + revisedRoles.toArray(new String[0])); + } + + // persist changes + writeRealmFile(allUsers); + return true; + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t); + } + return false; + } + + @Override + public boolean renameRole(String oldRole, String newRole) { + try { + Properties allUsers = readRealmFile(); + 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 r = roles[i]; + if (r.equalsIgnoreCase(oldRole)) { + needsRenameRole.remove(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(','); + List<String> revisedRoles = new ArrayList<String>(); + revisedRoles.add(newRole); + // skip first value (password) + for (int i = 1; i < values.length; i++) { + String value = values[i]; + if (!value.equalsIgnoreCase(oldRole)) { + revisedRoles.add(value); + sb.append(value); + sb.append(','); + } + } + sb.setLength(sb.length() - 1); + + // update properties + allUsers.put(user, sb.toString()); + + // update memory + putUser(user, Credential.getCredential(password), + revisedRoles.toArray(new String[0])); + } + + // persist changes + writeRealmFile(allUsers); + return true; + } catch (Throwable t) { + logger.error( + MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t); + } + return false; + } + + @Override + public boolean deleteRole(String role) { + try { + Properties allUsers = readRealmFile(); + 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 r = roles[i]; + if (r.equalsIgnoreCase(role)) { + needsDeleteRole.remove(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(','); + List<String> revisedRoles = new ArrayList<String>(); + // skip first value (password) + for (int i = 1; i < values.length; i++) { + String value = values[i]; + if (!value.equalsIgnoreCase(role)) { + revisedRoles.add(value); + sb.append(value); + sb.append(','); + } + } + sb.setLength(sb.length() - 1); + + // update properties + allUsers.put(user, sb.toString()); + + // update memory + putUser(user, Credential.getCredential(password), + revisedRoles.toArray(new String[0])); + } + + // persist changes + writeRealmFile(allUsers); + return true; + } catch (Throwable t) { + logger.error(MessageFormat.format("Failed to delete role {0}!", role), t); + } + return false; + } + + private Properties readRealmFile() throws IOException { + Properties allUsers = new Properties(); + FileReader reader = new FileReader(realmFile); + allUsers.load(reader); + reader.close(); + return allUsers; + } + + private void writeRealmFile(Properties properties) throws IOException { + // Update realm file + File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp"); + FileWriter writer = new FileWriter(realmFileCopy); + properties + .store(writer, + "# Git:Blit realm file format: username=password,\\#permission,repository1,repository2..."); + writer.close(); + if (realmFileCopy.exists() && realmFileCopy.length() > 0) { + if (realmFile.delete()) { + if (!realmFileCopy.renameTo(realmFile)) { + throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!", + realmFileCopy.getAbsolutePath(), realmFile.getAbsolutePath())); + } + } else { + throw new IOException(MessageFormat.format("Failed to delete (0)!", + realmFile.getAbsolutePath())); + } + } else { + throw new IOException(MessageFormat.format("Failed to save {0}!", + realmFileCopy.getAbsolutePath())); + } + } + + /* ------------------------------------------------------------ */ + @Override + public void loadUsers() throws IOException { + if (realmFile == null) { + return; + } + + if (Log.isDebugEnabled()) { + Log.debug("Load " + this + " from " + realmFile); + } + Properties allUsers = readRealmFile(); + + // Map Users + for (Map.Entry<Object, Object> entry : allUsers.entrySet()) { + String username = ((String) entry.getKey()).trim(); + String credentials = ((String) entry.getValue()).trim(); + String roles = null; + int c = credentials.indexOf(','); + if (c > 0) { + roles = credentials.substring(c + 1).trim(); + credentials = credentials.substring(0, c).trim(); + } + + if (username != null && username.length() > 0 && credentials != null + && credentials.length() > 0) { + String[] roleArray = IdentityService.NO_ROLES; + if (roles != null && roles.length() > 0) { + roleArray = roles.split(","); + } + putUser(username, Credential.getCredential(credentials), roleArray); + } + } + } + + @Override + protected UserIdentity loadUser(String username) { return null; } } -- Gitblit v1.9.1