Trim trailing whitespace and organize imports
Change-Id: I9f91138b20219be6e3c4b28251487df262bff6cc
| | |
| | | /**
|
| | | * The AccessRestrictionFilter is an AuthenticationFilter that confirms that the
|
| | | * requested repository can be accessed by the anonymous or named user.
|
| | | * |
| | | *
|
| | | * The filter extracts the name of the repository from the url and determines if
|
| | | * the requested action for the repository requires a Basic authentication
|
| | | * prompt. If authentication is required and no credentials are stored in the
|
| | | * "Authorization" header, then a basic authentication challenge is issued.
|
| | | * |
| | | *
|
| | | * http://en.wikipedia.org/wiki/Basic_access_authentication
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class AccessRestrictionFilter extends AuthenticationFilter {
|
| | |
|
| | | /**
|
| | | * Extract the repository name from the url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return repository name
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Analyze the url and returns the action of the request.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return action of the request
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Determine if a non-existing repository can be created using this filter.
|
| | | * |
| | | *
|
| | | * @return true if the filter allows repository creation
|
| | | */
|
| | | protected abstract boolean isCreationAllowed();
|
| | | |
| | |
|
| | | /**
|
| | | * Determine if the action may be executed on the repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if the action may be performed
|
| | |
| | |
|
| | | /**
|
| | | * Determine if the repository requires authentication.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if authentication required
|
| | |
| | | /**
|
| | | * Determine if the user can access the repository and perform the specified
|
| | | * action.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param user
|
| | | * @param action
|
| | |
| | |
|
| | | /**
|
| | | * Allows a filter to create a repository, if one does not exist.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | * @param repository
|
| | | * @param action
|
| | |
| | | protected RepositoryModel createRepository(UserModel user, String repository, String action) {
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * doFilter does the actual work of preprocessing the request to ensure that
|
| | | * the user may proceed.
|
| | | * |
| | | *
|
| | | * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
|
| | | * javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
| | | */
|
| | |
| | |
|
| | | String fullUrl = getFullUrl(httpRequest);
|
| | | String repository = extractRepositoryName(fullUrl);
|
| | | |
| | |
|
| | | if (GitBlit.self().isCollectingGarbage(repository)) {
|
| | | logger.info(MessageFormat.format("ARF: Rejecting request for {0}, busy collecting garbage!", repository));
|
| | | httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
|
| | |
| | | model = createRepository(user, repository, urlRequestType);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | if (model == null) {
|
| | | // repository not found. send 404.
|
| | | logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,
|
| | |
| | | return;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // Confirm that the action may be executed on the repository
|
| | | if (!isActionAllowed(model, urlRequestType)) {
|
| | | logger.info(MessageFormat.format("ARF: action {0} on {1} forbidden ({2})",
|
| | |
| | |
|
| | | /**
|
| | | * Utility class to add an indexBranch setting to matching repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class AddIndexedBranch {
|
| | |
|
| | |
| | | jc.usage();
|
| | | return;
|
| | | }
|
| | | |
| | |
|
| | | // create a lowercase set of excluded repositories
|
| | | Set<String> exclusions = new TreeSet<String>();
|
| | | for (String exclude : params.exclusions) {
|
| | | exclusions.add(exclude.toLowerCase());
|
| | | }
|
| | | |
| | |
|
| | | // determine available repositories
|
| | | File folder = new File(params.folder);
|
| | | List<String> repoList = JGitUtils.getRepositoryList(folder, false, true, -1, null);
|
| | | |
| | |
|
| | | int modCount = 0;
|
| | | int skipCount = 0;
|
| | | for (String repo : repoList) {
|
| | |
| | | break;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | if (skip) {
|
| | | System.out.println("skipping " + repo);
|
| | | skipCount++;
|
| | | continue;
|
| | | }
|
| | |
|
| | | |
| | |
|
| | | try {
|
| | | // load repository config
|
| | | File gitDir = FileKey.resolve(new File(folder, repo), FS.DETECTED);
|
| | | Repository repository = new FileRepositoryBuilder().setGitDir(gitDir).build();
|
| | | StoredConfig config = repository.getConfig();
|
| | | config.load();
|
| | | |
| | |
|
| | | Set<String> indexedBranches = new LinkedHashSet<String>();
|
| | | |
| | |
|
| | | // add all local branches to index
|
| | | if(params.addAllLocalBranches) {
|
| | | List<RefModel> list = JGitUtils.getLocalBranches(repository, true, -1);
|
| | |
| | | System.out.println(MessageFormat.format("adding [gitblit] indexBranch={0} for {1}", params.branch, repo));
|
| | | indexedBranches.add(params.branch);
|
| | | }
|
| | | |
| | |
|
| | | String [] branches = config.getStringList("gitblit", null, "indexBranch");
|
| | | if (!ArrayUtils.isEmpty(branches)) {
|
| | | for (String branch : branches) {
|
| | |
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | System.out.println(MessageFormat.format("updated {0} repository configurations, skipped {1}", modCount, skipCount));
|
| | | }
|
| | |
|
| | | |
| | |
|
| | |
|
| | | /**
|
| | | * JCommander Parameters class for AddIndexedBranch.
|
| | |
| | |
|
| | | @Parameter(names = { "--skip" }, description = "Skip the named repository (simple fizzy matching is supported)", required = false)
|
| | | public List<String> exclusions = new ArrayList<String>();
|
| | | |
| | |
|
| | | @Parameter(names = { "--all-local-branches" }, description = "Add all local branches to index. If specified, the --branch parameter is not considered.", required = false)
|
| | | public boolean addAllLocalBranches = false;
|
| | | }
|
| | |
| | | /**
|
| | | * The AuthenticationFilter is a servlet filter that preprocesses requests that
|
| | | * match its url pattern definition in the web.xml file.
|
| | | * |
| | | *
|
| | | * http://en.wikipedia.org/wiki/Basic_access_authentication
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class AuthenticationFilter implements Filter {
|
| | |
|
| | |
| | | /**
|
| | | * doFilter does the actual work of preprocessing the request to ensure that
|
| | | * the user may proceed.
|
| | | * |
| | | *
|
| | | * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
|
| | | * javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
| | | */
|
| | | @Override
|
| | | public abstract void doFilter(final ServletRequest request, final ServletResponse response,
|
| | | final FilterChain chain) throws IOException, ServletException;
|
| | | |
| | |
|
| | | /**
|
| | | * Allow the filter to require a client certificate to continue processing.
|
| | | * |
| | | *
|
| | | * @return true, if a client certificate is required
|
| | | */
|
| | | protected boolean requiresClientCertificate() {
|
| | |
| | |
|
| | | /**
|
| | | * Returns the full relative url of the request.
|
| | | * |
| | | *
|
| | | * @param httpRequest
|
| | | * @return url
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the user making the request, if the user has authenticated.
|
| | | * |
| | | *
|
| | | * @param httpRequest
|
| | | * @return user
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Handles requests for branch graphs
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class BranchGraphServlet extends HttpServlet {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns an url to this servlet for the specified parameters.
|
| | | * |
| | | *
|
| | | * @param baseURL
|
| | | * @param repository
|
| | | * @param objectId
|
| | |
| | | }
|
| | |
|
| | | // fetch the requested commits plus some extra so that the last
|
| | | // commit displayed *likely* has correct lane assignments |
| | | // commit displayed *likely* has correct lane assignments
|
| | | CommitList commitList = new CommitList();
|
| | | commitList.source(rw);
|
| | | commitList.fillTo(2*Math.max(requestedCommits, maxCommits));
|
| | |
| | |
|
| | | // create an image buffer and render the lanes
|
| | | BufferedImage image = new BufferedImage(graphWidth, rowHeight*numCommits, BufferedImage.TYPE_INT_ARGB);
|
| | | |
| | |
|
| | | Graphics2D g = null;
|
| | | try {
|
| | | g = image.createGraphics();
|
| | |
| | | /**
|
| | | * ConfigUserService is Gitblit's default user service implementation since
|
| | | * version 0.8.0.
|
| | | * |
| | | *
|
| | | * Users and their repository memberships are stored in a git-style config file
|
| | | * which is cached and dynamically reloaded when modified. This file is
|
| | | * plain-text, human-readable, and may be edited with a text editor.
|
| | | * |
| | | *
|
| | | * Additionally, this format allows for expansion of the user model without
|
| | | * bringing in the complexity of a database.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class ConfigUserService implements IUserService {
|
| | |
|
| | |
| | | private static final String USER = "user";
|
| | |
|
| | | private static final String PASSWORD = "password";
|
| | | |
| | |
|
| | | private static final String DISPLAYNAME = "displayName";
|
| | | |
| | |
|
| | | private static final String EMAILADDRESS = "emailAddress";
|
| | | |
| | |
|
| | | private static final String ORGANIZATIONALUNIT = "organizationalUnit";
|
| | | |
| | |
|
| | | private static final String ORGANIZATION = "organization";
|
| | | |
| | |
|
| | | private static final String LOCALITY = "locality";
|
| | | |
| | |
|
| | | private static final String STATEPROVINCE = "stateProvince";
|
| | | |
| | |
|
| | | private static final String COUNTRYCODE = "countryCode";
|
| | | |
| | |
|
| | | private static final String COOKIE = "cookie";
|
| | |
|
| | | private static final String REPOSITORY = "repository";
|
| | |
| | | private static final String PRERECEIVE = "preReceiveScript";
|
| | |
|
| | | private static final String POSTRECEIVE = "postReceiveScript";
|
| | | |
| | |
|
| | | private static final String STARRED = "starred";
|
| | | |
| | |
|
| | | private static final String LOCALE = "locale";
|
| | |
|
| | | private final File realmFile;
|
| | |
| | | private final Map<String, TeamModel> teams = new ConcurrentHashMap<String, TeamModel>();
|
| | |
|
| | | private volatile long lastModified;
|
| | | |
| | |
|
| | | private volatile boolean forceReload;
|
| | |
|
| | | public ConfigUserService(File realmFile) {
|
| | |
| | |
|
| | | /**
|
| | | * Setup the user service.
|
| | | * |
| | | *
|
| | | * @param settings
|
| | | * @since 0.7.0
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Does the user service support changes to credentials?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Does the user service support changes to user display name?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Does the user service support changes to user email address?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Does the user service support changes to team memberships?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */ |
| | | */
|
| | | @Override
|
| | | public boolean supportsTeamMembershipChanges() {
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Does the user service support cookie authentication?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | */
|
| | | @Override
|
| | |
| | |
|
| | | /**
|
| | | * Returns the cookie value for the specified user.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return cookie value
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Authenticate a user based on their cookie.
|
| | | * |
| | | *
|
| | | * @param cookie
|
| | | * @return a user object or null
|
| | | */
|
| | |
| | | if (cookies.containsKey(hash)) {
|
| | | model = cookies.get(hash);
|
| | | }
|
| | | |
| | |
|
| | | if (model != null) {
|
| | | // clone the model, otherwise all changes to this object are
|
| | | // live and unpersisted
|
| | |
| | |
|
| | | /**
|
| | | * Authenticate a user based on a username and password.
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @param password
|
| | | * @return a user object or null
|
| | |
| | |
|
| | | /**
|
| | | * Logout a user.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | */
|
| | | @Override
|
| | | public void logout(UserModel user) { |
| | | public void logout(UserModel user) {
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieve the user object for the specified username.
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @return a user object or null
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Updates/writes a complete user object.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if update is successful
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Updates/writes all specified user objects.
|
| | | * |
| | | *
|
| | | * @param models a list of user models
|
| | | * @return true if update is successful
|
| | | * @since 1.2.0
|
| | |
| | | } else {
|
| | | // do not clobber existing team definition
|
| | | // maybe because this is a federated user
|
| | | t.addUser(model.username); |
| | | t.addUser(model.username);
|
| | | }
|
| | | }
|
| | |
|
| | |
| | | /**
|
| | | * 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
|
| | |
| | |
|
| | | /**
|
| | | * Deletes the user object from the user service.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if successful
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Delete the user object with the specified username
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @return true if successful
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the list of all teams available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all teams
|
| | | * @since 0.8.0
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the list of all teams available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all teams
|
| | | * @since 0.8.0
|
| | | */
|
| | |
| | | /**
|
| | | * 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
|
| | |
| | | /**
|
| | | * 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
|
| | |
| | |
|
| | | /**
|
| | | * Retrieve the team object for the specified team name.
|
| | | * |
| | | *
|
| | | * @param teamname
|
| | | * @return a team object or null
|
| | | * @since 0.8.0
|
| | |
| | |
|
| | | /**
|
| | | * Updates/writes a complete team object.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | |
| | |
|
| | | /**
|
| | | * Updates/writes all specified team objects.
|
| | | * |
| | | *
|
| | | * @param models a list of team models
|
| | | * @return true if update is successful
|
| | | * @since 1.2.0
|
| | |
| | | /**
|
| | | * 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
|
| | |
| | |
|
| | | /**
|
| | | * Deletes the team object from the user service.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | |
| | |
|
| | | /**
|
| | | * Delete the team object with the specified teamname
|
| | | * |
| | | *
|
| | | * @param teamname
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | |
| | |
|
| | | /**
|
| | | * Returns the list of all users available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all usernames
|
| | | */
|
| | | @Override
|
| | |
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of all users available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all usernames
|
| | | */
|
| | | @Override
|
| | |
| | | list = DeepCopier.copy(list);
|
| | | 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
|
| | |
| | | /**
|
| | | * Sets the list of all uses who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | *
|
| | | * @param role
|
| | | * the repository name
|
| | | * @param usernames
|
| | |
| | |
|
| | | /**
|
| | | * Renames a repository role.
|
| | | * |
| | | *
|
| | | * @param oldRole
|
| | | * @param newRole
|
| | | * @return true if successful
|
| | |
| | |
|
| | | /**
|
| | | * Removes a repository role from all users.
|
| | | * |
| | | *
|
| | | * @param role
|
| | | * @return true if successful
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Writes the properties file.
|
| | | * |
| | | *
|
| | | * @throws IOException
|
| | | */
|
| | | private synchronized void write() throws IOException {
|
| | |
| | | }
|
| | | config.setStringList(USER, model.username, REPOSITORY, permissions);
|
| | | }
|
| | | |
| | |
|
| | | // user preferences
|
| | | if (model.getPreferences() != null) {
|
| | | List<String> starred = model.getPreferences().getStarredRepositories();
|
| | |
| | | roles.add(Constants.NO_ROLE);
|
| | | }
|
| | | config.setStringList(TEAM, model.name, ROLE, roles);
|
| | | |
| | |
|
| | | if (!model.canAdmin) {
|
| | | // write team permission for non-admin teams
|
| | | if (model.permissions == null) {
|
| | |
| | | Set<String> usernames = config.getSubsections(USER);
|
| | | for (String username : usernames) {
|
| | | UserModel user = new UserModel(username.toLowerCase());
|
| | | user.password = config.getString(USER, username, PASSWORD); |
| | | user.password = config.getString(USER, username, PASSWORD);
|
| | | user.displayName = config.getString(USER, username, DISPLAYNAME);
|
| | | user.emailAddress = config.getString(USER, username, EMAILADDRESS);
|
| | | user.organizationalUnit = config.getString(USER, username, ORGANIZATIONALUNIT);
|
| | |
| | | user.stateProvince = config.getString(USER, username, STATEPROVINCE);
|
| | | user.countryCode = config.getString(USER, username, COUNTRYCODE);
|
| | | user.cookie = config.getString(USER, username, COOKIE);
|
| | | user.getPreferences().locale = config.getString(USER, username, LOCALE); |
| | | user.getPreferences().locale = config.getString(USER, username, LOCALE);
|
| | | if (StringUtils.isEmpty(user.cookie) && !StringUtils.isEmpty(user.password)) {
|
| | | user.cookie = StringUtils.getSHA1(user.username + user.password);
|
| | | }
|
| | |
| | | team.canAdmin = roles.contains(Constants.ADMIN_ROLE);
|
| | | team.canFork = roles.contains(Constants.FORK_ROLE);
|
| | | team.canCreate = roles.contains(Constants.CREATE_ROLE);
|
| | | |
| | |
|
| | | if (!team.canAdmin) {
|
| | | // non-admin team, read permissions
|
| | | team.addRepositoryPermissions(Arrays.asList(config.getStringList(TEAM, teamname,
|
| | |
| | |
|
| | | /**
|
| | | * Constant values used by Gitblit.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class Constants {
|
| | |
|
| | |
| | | public static final String FULL_NAME = "Gitblit - a pure Java Git solution";
|
| | |
|
| | | public static final String ADMIN_ROLE = "#admin";
|
| | | |
| | |
|
| | | public static final String FORK_ROLE = "#fork";
|
| | | |
| | |
|
| | | public static final String CREATE_ROLE = "#create";
|
| | |
|
| | | public static final String NOT_FEDERATED_ROLE = "#notfederated";
|
| | | |
| | |
|
| | | public static final String NO_ROLE = "#none";
|
| | | |
| | |
|
| | | public static final String EXTERNAL_ACCOUNT = "#externalAccount";
|
| | |
|
| | | public static final String PROPERTIES_FILE = "gitblit.properties";
|
| | | |
| | |
|
| | | public static final String DEFAULT_USER_REPOSITORY_PREFIX = "~";
|
| | |
|
| | | public static final String GIT_PATH = "/git/";
|
| | |
| | | public static final String FEDERATION_PATH = "/federation/";
|
| | |
|
| | | public static final String RPC_PATH = "/rpc/";
|
| | | |
| | |
|
| | | public static final String PAGES = "/pages/";
|
| | | |
| | |
|
| | | public static final String SPARKLESHARE_INVITE_PATH = "/sparkleshare/";
|
| | | |
| | |
|
| | | public static final String BRANCH_GRAPH_PATH = "/graph/";
|
| | |
|
| | | public static final String BORDER = "***********************************************************";
|
| | |
| | | public static final String FEDERATION_USER = "$gitblit";
|
| | |
|
| | | public static final String PROPOSAL_EXT = ".json";
|
| | | |
| | |
|
| | | public static final String ENCODING = "UTF-8";
|
| | | |
| | |
|
| | | public static final int LEN_SHORTLOG = 78;
|
| | | |
| | |
|
| | | public static final int LEN_SHORTLOG_REFS = 60;
|
| | | |
| | |
|
| | | public static final String DEFAULT_BRANCH = "default";
|
| | | |
| | |
|
| | | public static final String CONFIG_GITBLIT = "gitblit";
|
| | | |
| | |
|
| | | public static final String CONFIG_CUSTOM_FIELDS = "customFields";
|
| | | |
| | |
|
| | | public static final String ISO8601 = "yyyy-MM-dd'T'HH:mm:ssZ";
|
| | | |
| | |
|
| | | public static final String baseFolder = "baseFolder";
|
| | | |
| | |
|
| | | public static final String baseFolder$ = "${" + baseFolder + "}";
|
| | | |
| | |
|
| | | public static final String contextFolder$ = "${contextFolder}";
|
| | | |
| | |
|
| | | public static final String HEAD = "HEAD";
|
| | |
|
| | | public static final String R_GITBLIT = "refs/gitblit/";
|
| | | |
| | |
|
| | | public static final String R_HEADS = "refs/heads/";
|
| | | |
| | |
|
| | | public static final String R_NOTES = "refs/notes/";
|
| | | |
| | |
|
| | | public static final String R_CHANGES = "refs/changes/";
|
| | | |
| | |
|
| | | public static final String R_PULL= "refs/pull/";
|
| | |
|
| | | public static final String R_TAGS = "refs/tags/";
|
| | | |
| | |
|
| | | public static final String R_REMOTES = "refs/remotes/";
|
| | |
|
| | | public static String getVersion() {
|
| | |
| | | public static String getGitBlitVersion() {
|
| | | return NAME + " v" + getVersion();
|
| | | }
|
| | | |
| | |
|
| | | public static String getBuildDate() {
|
| | | return getManifestValue("build-date", "PENDING");
|
| | | }
|
| | | |
| | |
|
| | | private static String getManifestValue(String attrib, String defaultValue) {
|
| | | Class<?> clazz = Constants.class;
|
| | | String className = clazz.getSimpleName() + ".class";
|
| | |
| | | }
|
| | | return defaultValue;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Enumeration representing the four access restriction levels.
|
| | | */
|
| | | public static enum AccessRestrictionType {
|
| | | NONE, PUSH, CLONE, VIEW;
|
| | | |
| | |
|
| | | private static final AccessRestrictionType [] AUTH_TYPES = { PUSH, CLONE, VIEW };
|
| | |
|
| | | public static AccessRestrictionType fromName(String name) {
|
| | |
| | | }
|
| | | return NONE;
|
| | | }
|
| | | |
| | |
|
| | | public static List<AccessRestrictionType> choices(boolean allowAnonymousPush) {
|
| | | if (allowAnonymousPush) {
|
| | | return Arrays.asList(values());
|
| | |
| | | return this.ordinal() >= type.ordinal();
|
| | | }
|
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name();
|
| | | }
|
| | | |
| | |
|
| | | public boolean isValidPermission(AccessPermission permission) {
|
| | | switch (this) {
|
| | | case VIEW:
|
| | |
| | | return permission.atLeast(AccessPermission.CLONE);
|
| | | case PUSH:
|
| | | // PUSH restriction
|
| | | // only PUSH or greater access permissions are valid |
| | | // only PUSH or greater access permissions are valid
|
| | | return permission.atLeast(AccessPermission.PUSH);
|
| | | case NONE:
|
| | | // NO access restriction
|
| | |
| | | return false;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Enumeration representing the types of authorization control for an
|
| | | * access restricted resource.
|
| | | */
|
| | | public static enum AuthorizationControl {
|
| | | AUTHENTICATED, NAMED;
|
| | | |
| | |
|
| | | public static AuthorizationControl fromName(String name) {
|
| | | for (AuthorizationControl type : values()) {
|
| | | if (type.name().equalsIgnoreCase(name)) {
|
| | |
| | | }
|
| | | return NAMED;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name();
|
| | | }
|
| | |
| | | return REPOSITORIES;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name();
|
| | | }
|
| | |
| | | return PULL_REPOSITORIES;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name();
|
| | | }
|
| | |
| | | // Order is important here. anything above LIST_SETTINGS requires
|
| | | // administrator privileges and web.allowRpcManagement.
|
| | | CLEAR_REPOSITORY_CACHE, GET_PROTOCOL, LIST_REPOSITORIES, LIST_BRANCHES, GET_USER, LIST_SETTINGS,
|
| | | CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY, |
| | | LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER, |
| | | CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY,
|
| | | LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER,
|
| | | LIST_TEAMS, CREATE_TEAM, EDIT_TEAM, DELETE_TEAM,
|
| | | LIST_REPOSITORY_MEMBERS, SET_REPOSITORY_MEMBERS, LIST_REPOSITORY_TEAMS, SET_REPOSITORY_TEAMS, |
| | | LIST_REPOSITORY_MEMBER_PERMISSIONS, SET_REPOSITORY_MEMBER_PERMISSIONS, LIST_REPOSITORY_TEAM_PERMISSIONS, SET_REPOSITORY_TEAM_PERMISSIONS, |
| | | LIST_REPOSITORY_MEMBERS, SET_REPOSITORY_MEMBERS, LIST_REPOSITORY_TEAMS, SET_REPOSITORY_TEAMS,
|
| | | LIST_REPOSITORY_MEMBER_PERMISSIONS, SET_REPOSITORY_MEMBER_PERMISSIONS, LIST_REPOSITORY_TEAM_PERMISSIONS, SET_REPOSITORY_TEAM_PERMISSIONS,
|
| | | LIST_FEDERATION_REGISTRATIONS, LIST_FEDERATION_RESULTS, LIST_FEDERATION_PROPOSALS, LIST_FEDERATION_SETS,
|
| | | EDIT_SETTINGS, LIST_STATUS;
|
| | |
|
| | |
| | | }
|
| | | }
|
| | | return null;
|
| | | } |
| | | }
|
| | |
|
| | | public boolean exceeds(RpcRequest type) {
|
| | | return this.ordinal() > type.ordinal();
|
| | |
| | | */
|
| | | public static enum SearchType {
|
| | | AUTHOR, COMMITTER, COMMIT;
|
| | | |
| | |
|
| | | public static SearchType forName(String name) {
|
| | | for (SearchType type : values()) {
|
| | | if (type.name().equalsIgnoreCase(name)) {
|
| | |
| | | }
|
| | | return COMMIT;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name().toLowerCase();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * The types of objects that can be indexed and queried.
|
| | | */
|
| | |
| | | return null;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * The access permissions available for a repository. |
| | | * The access permissions available for a repository.
|
| | | */
|
| | | public static enum AccessPermission {
|
| | | NONE("N"), EXCLUDE("X"), VIEW("V"), CLONE("R"), PUSH("RW"), CREATE("RWC"), DELETE("RWD"), REWIND("RW+"), OWNER("RW+");
|
| | | |
| | |
|
| | | public static final AccessPermission [] NEWPERMISSIONS = { EXCLUDE, VIEW, CLONE, PUSH, CREATE, DELETE, REWIND };
|
| | | |
| | |
|
| | | public static AccessPermission LEGACY = REWIND;
|
| | | |
| | |
|
| | | public final String code;
|
| | | |
| | |
|
| | | private AccessPermission(String code) {
|
| | | this.code = code;
|
| | | }
|
| | |
| | | public boolean exceeds(AccessPermission perm) {
|
| | | return ordinal() > perm.ordinal();
|
| | | }
|
| | | |
| | |
|
| | | public String asRole(String repository) {
|
| | | return code + ":" + repository;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return code;
|
| | | }
|
| | | |
| | |
|
| | | public static AccessPermission permissionFromRole(String role) {
|
| | | String [] fields = role.split(":", 2);
|
| | | if (fields.length == 1) {
|
| | |
| | | return AccessPermission.fromCode(fields[0]);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static String repositoryFromRole(String role) {
|
| | | String [] fields = role.split(":", 2);
|
| | | if (fields.length == 1) {
|
| | |
| | | return fields[1];
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static AccessPermission fromCode(String code) {
|
| | | for (AccessPermission perm : values()) {
|
| | | if (perm.code.equalsIgnoreCase(code)) {
|
| | |
| | | return AccessPermission.NONE;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static enum RegistrantType {
|
| | | REPOSITORY, USER, TEAM;
|
| | | }
|
| | | |
| | |
|
| | | public static enum PermissionType {
|
| | | MISSING, ANONYMOUS, EXPLICIT, TEAM, REGEX, OWNER, ADMINISTRATOR;
|
| | | }
|
| | | |
| | |
|
| | | public static enum GCStatus {
|
| | | READY, COLLECTING;
|
| | | |
| | |
|
| | | public boolean exceeds(GCStatus s) {
|
| | | return ordinal() > s.ordinal();
|
| | | }
|
| | |
| | |
|
| | | public static enum AuthenticationType {
|
| | | CREDENTIALS, COOKIE, CERTIFICATE, CONTAINER;
|
| | | |
| | |
|
| | | public boolean isStandard() {
|
| | | return ordinal() <= COOKIE.ordinal();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static enum AccountType {
|
| | | LOCAL, EXTERNAL, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM, HTPASSWD;
|
| | | |
| | |
|
| | | public boolean isLocal() {
|
| | | return this == LOCAL;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static enum CommitMessageRenderer {
|
| | | PLAIN, MARKDOWN;
|
| | | |
| | |
|
| | | public static CommitMessageRenderer fromName(String name) {
|
| | | for (CommitMessageRenderer renderer : values()) {
|
| | | if (renderer.name().equalsIgnoreCase(name)) {
|
| | |
| | | * The DownloadZipFilter is an AccessRestrictionFilter which ensures that zip
|
| | | * requests for view-restricted repositories have proper authentication
|
| | | * credentials and are authorized.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class DownloadZipFilter extends AccessRestrictionFilter {
|
| | |
|
| | | /**
|
| | | * Extract the repository name from the url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return repository name
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Analyze the url and returns the action of the request.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return action of the request
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Determine if a non-existing repository can be created using this filter.
|
| | | * |
| | | *
|
| | | * @return true if the filter allows repository creation
|
| | | */
|
| | | @Override
|
| | |
| | |
|
| | | /**
|
| | | * Determine if the action may be executed on the repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if the action may be performed
|
| | |
| | |
|
| | | /**
|
| | | * Determine if the repository requires authentication.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if authentication required
|
| | |
| | | /**
|
| | | * Determine if the user can access the repository and perform the specified
|
| | | * action.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param user
|
| | | * @param action
|
| | |
| | | /**
|
| | | * Streams out a zip file from the specified repository for any tree path at any
|
| | | * revision.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class DownloadZipServlet extends HttpServlet {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | private transient Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);
|
| | | |
| | |
|
| | | public static enum Format {
|
| | | zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2");
|
| | | |
| | |
|
| | | public final String extension;
|
| | | |
| | |
|
| | | Format(String ext) {
|
| | | this.extension = ext;
|
| | | }
|
| | | |
| | |
|
| | | public static Format fromName(String name) {
|
| | | for (Format format : values()) {
|
| | | if (format.name().equalsIgnoreCase(name)) {
|
| | |
| | |
|
| | | /**
|
| | | * Returns an url to this servlet for the specified parameters.
|
| | | * |
| | | *
|
| | | * @param baseURL
|
| | | * @param repository
|
| | | * @param objectId
|
| | |
| | |
|
| | | /**
|
| | | * Creates a zip stream from the repository of the requested data.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @param response
|
| | | * @throws javax.servlet.ServletException
|
| | |
| | | response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
| | | return;
|
| | | }
|
| | | |
| | |
|
| | | Format format = Format.zip;
|
| | | String repository = request.getParameter("r");
|
| | | String basePath = request.getParameter("p");
|
| | |
| | | if (!StringUtils.isEmpty(f)) {
|
| | | format = Format.fromName(f);
|
| | | }
|
| | | |
| | |
|
| | | try {
|
| | | String name = repository;
|
| | | if (name.indexOf('/') > -1) {
|
| | |
| | | if (!StringUtils.isEmpty(objectId)) {
|
| | | name += "-" + objectId;
|
| | | }
|
| | | |
| | |
|
| | | Repository r = GitBlit.self().getRepository(repository);
|
| | | if (r == null) {
|
| | | if (GitBlit.self().isCollectingGarbage(repository)) {
|
| | |
| | | CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream());
|
| | | break;
|
| | | }
|
| | | |
| | |
|
| | | response.flushBuffer();
|
| | | } catch (IOException t) {
|
| | | String message = t.getMessage() == null ? "" : t.getMessage().toLowerCase();
|
| | | if (message.contains("reset") || message.contains("broken pipe")) {
|
| | | logger.error("Client aborted zip download: " + message);
|
| | | } else {
|
| | | logger.error("Failed to write attachment to client", t); |
| | | logger.error("Failed to write attachment to client", t);
|
| | | }
|
| | | } catch (Throwable t) {
|
| | | logger.error("Failed to write attachment to client", t);
|
| | |
| | | * |
| | | */ |
| | | public class EnforceAuthenticationFilter implements Filter { |
| | | |
| | | |
| | | protected transient Logger logger = LoggerFactory.getLogger(getClass()); |
| | | |
| | | /* |
| | | /* |
| | | * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) |
| | | */ |
| | | @Override |
| | |
| | | // nothing to be done |
| | | |
| | | } //init |
| | | |
| | | |
| | | /* |
| | | |
| | | /* |
| | | * This does the actual filtering: is the user authenticated? If not, enforce HTTP authentication (401) |
| | | * |
| | | * |
| | | * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) |
| | | */ |
| | | @Override |
| | | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { |
| | | |
| | | |
| | | /* |
| | | * Determine whether to enforce the BASIC authentication: |
| | | */ |
| | | @SuppressWarnings("static-access") |
| | | Boolean mustForceAuth = GitBlit.self().getBoolean(Keys.web.authenticateViewPages, false) |
| | | && GitBlit.self().getBoolean(Keys.web.enforceHttpBasicAuthentication, false); |
| | | |
| | | |
| | | HttpServletRequest HttpRequest = (HttpServletRequest)request; |
| | | HttpServletResponse HttpResponse = (HttpServletResponse)response; |
| | | HttpServletResponse HttpResponse = (HttpServletResponse)response; |
| | | UserModel user = GitBlit.self().authenticate(HttpRequest); |
| | | |
| | | |
| | | if (mustForceAuth && (user == null)) { |
| | | // not authenticated, enforce now: |
| | | logger.debug(MessageFormat.format("EnforceAuthFilter: user not authenticated for URL {0}!", request.toString())); |
| | |
| | | } else { |
| | | // user is authenticated, or don't care, continue handling |
| | | chain.doFilter( request, response ); |
| | | |
| | | |
| | | } // authenticated |
| | | } // doFilter |
| | | |
| | | |
| | | /* |
| | | |
| | | /* |
| | | * @see javax.servlet.Filter#destroy() |
| | | */ |
| | | @Override |
| | |
| | |
|
| | | /**
|
| | | * Command-line client to pull federated Gitblit repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class FederationClient {
|
| | |
|
| | |
| | | System.out.println("No Federation Registrations! Nothing to do.");
|
| | | System.exit(0);
|
| | | }
|
| | | |
| | |
|
| | | // command-line specified repositories folder
|
| | | if (!StringUtils.isEmpty(params.repositoriesFolder)) {
|
| | | settings.overrideSetting(Keys.git.repositoriesFolder, new File(
|
| | |
| | | /**
|
| | | * Constructor for specifying a single federation registration. This
|
| | | * constructor is used to schedule the next pull execution.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | */
|
| | | private FederationPullExecutor(FederationModel registration) {
|
| | |
| | | * Constructor to specify a group of federation registrations. This is
|
| | | * normally used at startup to pull and then schedule the next update based
|
| | | * on each registrations frequency setting.
|
| | | * |
| | | *
|
| | | * @param registrations
|
| | | * @param isDaemon
|
| | | * if true, registrations are rescheduled in perpetuity. if
|
| | |
| | | /**
|
| | | * Mirrors a repository and, optionally, the server's users, and/or
|
| | | * configuration settings from a origin Gitblit instance.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | * @throws Exception
|
| | | */
|
| | |
| | | repositoryName.indexOf(DOT_GIT_EXT));
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // confirm that the origin of any pre-existing repository matches
|
| | | // the clone url
|
| | | String fetchHead = null;
|
| | | Repository existingRepository = GitBlit.self().getRepository(repositoryName);
|
| | | |
| | |
|
| | | if (existingRepository == null && GitBlit.self().isCollectingGarbage(repositoryName)) {
|
| | | logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName));
|
| | | continue;
|
| | |
| | | String branch = org.eclipse.jgit.lib.Constants.R_HEADS
|
| | | + ref.displayName.substring(ref.displayName.indexOf('/') + 1);
|
| | | String hash = ref.getReferencedObjectId().getName();
|
| | | |
| | |
|
| | | JGitUtils.setBranchRef(r, branch, hash);
|
| | | logger.info(MessageFormat.format(" resetting {0} of {1} to {2}", branch,
|
| | | repository.name, hash));
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | String newHead;
|
| | | if (StringUtils.isEmpty(repository.HEAD)) {
|
| | | newHead = newFetchHead;
|
| | |
| | | federationSets.addAll(repository.federationSets);
|
| | | }
|
| | | repository.federationSets = new ArrayList<String>(federationSets);
|
| | | |
| | |
|
| | | // merge indexed branches
|
| | | Set<String> indexedBranches = new HashSet<String>();
|
| | | if (rm.indexedBranches != null) {
|
| | |
| | | /**
|
| | | * Sends a status acknowledgment to the origin Gitblit instance. This
|
| | | * includes the results of the federated pull.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | * @throws Exception
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Schedules the next check of the federated Gitblit instance.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | */
|
| | | private void schedule(FederationModel registration) {
|
| | |
| | |
|
| | | /**
|
| | | * Handles federation requests.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class FederationServlet extends JsonServlet {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Processes a federation request.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @param response
|
| | | * @throws javax.servlet.ServletException
|
| | |
| | | return;
|
| | | }
|
| | | Map<String, String> scripts = new HashMap<String, String>();
|
| | | |
| | |
|
| | | Set<String> names = new HashSet<String>();
|
| | | names.addAll(GitBlit.getStrings(Keys.groovy.preReceiveScripts));
|
| | | names.addAll(GitBlit.getStrings(Keys.groovy.postReceiveScripts));
|
| | |
| | | /**
|
| | | * Dynamically loads and reloads a properties file by keeping track of the last
|
| | | * modification date.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class FileSettings extends IStoredSettings {
|
| | |
|
| | |
| | | private final Properties properties = new Properties();
|
| | |
|
| | | private volatile long lastModified;
|
| | | |
| | |
|
| | | private volatile boolean forceReload;
|
| | |
|
| | | public FileSettings(String file) {
|
| | |
| | | /**
|
| | | * Updates the specified settings in the settings file.
|
| | | */
|
| | | @Override
|
| | | public synchronized boolean saveSettings(Map<String, String> settings) {
|
| | | String content = FileUtils.readContent(propertiesFile, "\n");
|
| | | for (Map.Entry<String, String> setting:settings.entrySet()) {
|
| | |
| | | }
|
| | | FileUtils.writeContent(propertiesFile, content);
|
| | | // manually set the forceReload flag because not all JVMs support real
|
| | | // millisecond resolution of lastModified. (issue-55) |
| | | // millisecond resolution of lastModified. (issue-55)
|
| | | forceReload = true;
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | private String regExEscape(String input) {
|
| | | return input.replace(".", "\\.").replace("$", "\\$").replace("{", "\\{");
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * The GC executor handles periodic garbage collection in repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GCExecutor implements Runnable {
|
| | |
|
| | | public static enum GCStatus {
|
| | | READY, COLLECTING;
|
| | | |
| | |
|
| | | public boolean exceeds(GCStatus s) {
|
| | | return ordinal() > s.ordinal();
|
| | | }
|
| | |
| | | private final Logger logger = LoggerFactory.getLogger(GCExecutor.class);
|
| | |
|
| | | private final IStoredSettings settings;
|
| | | |
| | |
|
| | | private AtomicBoolean running = new AtomicBoolean(false);
|
| | | |
| | |
|
| | | private AtomicBoolean forceClose = new AtomicBoolean(false);
|
| | | |
| | |
|
| | | private final Map<String, GCStatus> gcCache = new ConcurrentHashMap<String, GCStatus>();
|
| | |
|
| | | public GCExecutor(IStoredSettings settings) {
|
| | |
| | |
|
| | | /**
|
| | | * Indicates if the GC executor is ready to process repositories.
|
| | | * |
| | | *
|
| | | * @return true if the GC executor is ready to process repositories
|
| | | */
|
| | | public boolean isReady() {
|
| | | return settings.getBoolean(Keys.git.enableGarbageCollection, false);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isRunning() {
|
| | | return running.get();
|
| | | }
|
| | | |
| | |
|
| | | public boolean lock(String repositoryName) {
|
| | | return setGCStatus(repositoryName, GCStatus.COLLECTING);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Tries to set a GCStatus for the specified repository.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @return true if the status has been set
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns true if Gitblit is actively collecting garbage in this repository.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @return true if actively collecting garbage
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Resets the GC status to ready.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | */
|
| | | public void releaseLock(String repositoryName) {
|
| | | gcCache.put(repositoryName.toLowerCase(), GCStatus.READY);
|
| | | }
|
| | | |
| | |
|
| | | public void close() {
|
| | | forceClose.set(true);
|
| | | }
|
| | |
| | | if (!isReady()) {
|
| | | return;
|
| | | }
|
| | | |
| | | running.set(true); |
| | |
|
| | | running.set(true);
|
| | | Date now = new Date();
|
| | |
|
| | | for (String repositoryName : GitBlit.self().getRepositoryList()) {
|
| | |
| | | logger.warn(MessageFormat.format("GCExecutor is missing repository {0}?!?", repositoryName));
|
| | | continue;
|
| | | }
|
| | | |
| | |
|
| | | if (!isRepositoryIdle(repository)) {
|
| | | logger.debug(MessageFormat.format("GCExecutor is skipping {0} because it is not idle", repositoryName));
|
| | | continue;
|
| | |
| | | logger.warn(MessageFormat.format("Can not acquire GC lock for {0}, skipping", repositoryName));
|
| | | continue;
|
| | | }
|
| | | |
| | |
|
| | | logger.debug(MessageFormat.format("GCExecutor locked idle repository {0}", repositoryName));
|
| | | |
| | |
|
| | | Git git = new Git(repository);
|
| | | GarbageCollectCommand gc = git.gc();
|
| | | Properties stats = gc.getStatistics();
|
| | | |
| | |
|
| | | // determine if this is a scheduled GC
|
| | | Calendar cal = Calendar.getInstance();
|
| | | cal.setTime(model.lastGC);
|
| | |
| | | if (hasGarbage && (hasEnoughGarbage || shouldCollectGarbage)) {
|
| | | long looseKB = sizeOfLooseObjects/1024L;
|
| | | logger.info(MessageFormat.format("Collecting {1} KB of loose objects from {0}", repositoryName, looseKB));
|
| | | |
| | |
|
| | | // do the deed
|
| | | gc.call();
|
| | | |
| | |
|
| | | garbageCollected = true;
|
| | | }
|
| | | } catch (Exception e) {
|
| | |
| | | model.lastGC = new Date();
|
| | | GitBlit.self().updateConfiguration(repository, model);
|
| | | }
|
| | | |
| | |
|
| | | repository.close();
|
| | | }
|
| | | |
| | | // reset the GC lock |
| | |
|
| | | // reset the GC lock
|
| | | releaseLock(repositoryName);
|
| | | logger.debug(MessageFormat.format("GCExecutor released GC lock for {0}", repositoryName));
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | running.set(false);
|
| | | }
|
| | | |
| | |
|
| | | private boolean isRepositoryIdle(Repository repository) {
|
| | | try {
|
| | | // Read the use count.
|
| | |
| | | * the web ui and the servlets. This class is either directly instantiated by |
| | | * the GitBlitServer class (Gitblit GO) or is reflectively instantiated from the |
| | | * definition in the web.xml file (Gitblit WAR). |
| | | * |
| | | * |
| | | * This class is the central logic processor for Gitblit. All settings, user |
| | | * object, and repository object operations pass through this class. |
| | | * |
| | | * |
| | | * Repository Resolution. There are two pathways for finding repositories. One |
| | | * pathway, for web ui display and repository authentication & authorization, is |
| | | * within this class. The other pathway is through the standard GitServlet. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | * |
| | | */ |
| | | public class GitBlit implements ServletContextListener { |
| | | |
| | | private static GitBlit gitblit; |
| | | |
| | | |
| | | private final Logger logger = LoggerFactory.getLogger(GitBlit.class); |
| | | |
| | | private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5); |
| | | |
| | | private final List<FederationModel> federationRegistrations = Collections |
| | | .synchronizedList(new ArrayList<FederationModel>()); |
| | | |
| | | |
| | | private final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>(); |
| | | |
| | | private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>(); |
| | |
| | | private final ObjectCache<Long> repositorySizeCache = new ObjectCache<Long>(); |
| | | |
| | | private final ObjectCache<List<Metric>> repositoryMetricsCache = new ObjectCache<List<Metric>>(); |
| | | |
| | | |
| | | private final Map<String, RepositoryModel> repositoryListCache = new ConcurrentHashMap<String, RepositoryModel>(); |
| | | |
| | | |
| | | private final Map<String, ProjectModel> projectCache = new ConcurrentHashMap<String, ProjectModel>(); |
| | | |
| | | |
| | | private final AtomicReference<String> repositoryListSettingsChecksum = new AtomicReference<String>(""); |
| | | |
| | | |
| | | private final ObjectCache<String> projectMarkdownCache = new ObjectCache<String>(); |
| | | |
| | | |
| | | private final ObjectCache<String> projectRepositoriesMarkdownCache = new ObjectCache<String>(); |
| | | |
| | | private ServletContext servletContext; |
| | | |
| | | |
| | | private File baseFolder; |
| | | |
| | | private File repositoriesFolder; |
| | |
| | | private ServerStatus serverStatus; |
| | | |
| | | private MailExecutor mailExecutor; |
| | | |
| | | |
| | | private LuceneExecutor luceneExecutor; |
| | | |
| | | |
| | | private GCExecutor gcExecutor; |
| | | |
| | | |
| | | private TimeZone timezone; |
| | | |
| | | |
| | | private FileBasedConfig projectConfigs; |
| | | |
| | | |
| | | private FanoutService fanoutService; |
| | | |
| | | private GitDaemon gitDaemon; |
| | |
| | | |
| | | /** |
| | | * Returns the Gitblit singleton. |
| | | * |
| | | * |
| | | * @return gitblit singleton |
| | | */ |
| | | public static GitBlit self() { |
| | |
| | | } |
| | | return gitblit; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the boot date of the Gitblit server. |
| | | * |
| | | * |
| | | * @return the boot date of Gitblit |
| | | */ |
| | | public static Date getBootDate() { |
| | | return self().serverStatus.bootDate; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the most recent change date of any repository served by Gitblit. |
| | | * |
| | | * |
| | | * @return a date |
| | | */ |
| | | public static Date getLastActivityDate() { |
| | |
| | | |
| | | /** |
| | | * Determine if this is the GO variant of Gitblit. |
| | | * |
| | | * |
| | | * @return true if this is the GO variant of Gitblit. |
| | | */ |
| | | public static boolean isGO() { |
| | | return self().settings instanceof FileSettings; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Determine if this Gitblit instance is actively serving git repositories |
| | | * or if it is merely a repository viewer. |
| | | * |
| | | * |
| | | * @return true if Gitblit is serving repositories |
| | | */ |
| | | public static boolean isServingRepositories() { |
| | |
| | | /** |
| | | * Determine if this Gitblit instance is actively serving git repositories |
| | | * or if it is merely a repository viewer. |
| | | * |
| | | * |
| | | * @return true if Gitblit is serving repositories |
| | | */ |
| | | public static boolean isSendingMail() { |
| | |
| | | |
| | | /** |
| | | * Returns the preferred timezone for the Gitblit instance. |
| | | * |
| | | * |
| | | * @return a timezone |
| | | */ |
| | | public static TimeZone getTimezone() { |
| | |
| | | } |
| | | return self().timezone; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the active settings. |
| | | * |
| | | * |
| | | * @return the active settings |
| | | */ |
| | | public static IStoredSettings getSettings() { |
| | | return self().settings; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the user-defined blob encodings. |
| | | * |
| | | * |
| | | * @return an array of encodings, may be empty |
| | | */ |
| | | public static String [] getEncodings() { |
| | | return getStrings(Keys.web.blobEncodings).toArray(new String[0]); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the boolean value for the specified key. If the key does not |
| | | * exist or the value for the key can not be interpreted as a boolean, the |
| | | * defaultValue is returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getBoolean(String, boolean) |
| | | * @param key |
| | | * @param defaultValue |
| | |
| | | * Returns the integer value for the specified key. If the key does not |
| | | * exist or the value for the key can not be interpreted as an integer, the |
| | | * defaultValue is returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getInteger(String key, int defaultValue) |
| | | * @param key |
| | | * @param defaultValue |
| | |
| | | * Returns the integer list for the specified key. If the key does not |
| | | * exist or the value for the key can not be interpreted as an integer, an |
| | | * empty list is returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getIntegers(String key) |
| | | * @param key |
| | | * @return key value or defaultValue |
| | |
| | | public static List<Integer> getIntegers(String key) { |
| | | return self().settings.getIntegers(key); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the value in bytes for the specified key. If the key does not |
| | | * exist or the value for the key can not be interpreted as an integer, the |
| | | * defaultValue is returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getFilesize(String key, int defaultValue) |
| | | * @param key |
| | | * @param defaultValue |
| | |
| | | * Returns the value in bytes for the specified key. If the key does not |
| | | * exist or the value for the key can not be interpreted as a long, the |
| | | * defaultValue is returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getFilesize(String key, long defaultValue) |
| | | * @param key |
| | | * @param defaultValue |
| | |
| | | * Returns the char value for the specified key. If the key does not exist |
| | | * or the value for the key can not be interpreted as a character, the |
| | | * defaultValue is returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getChar(String key, char defaultValue) |
| | | * @param key |
| | | * @param defaultValue |
| | |
| | | * Returns the string value for the specified key. If the key does not exist |
| | | * or the value for the key can not be interpreted as a string, the |
| | | * defaultValue is returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getString(String key, String defaultValue) |
| | | * @param key |
| | | * @param defaultValue |
| | |
| | | |
| | | /** |
| | | * Returns a list of space-separated strings from the specified key. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getStrings(String key) |
| | | * @param n |
| | | * @return list of strings |
| | |
| | | |
| | | /** |
| | | * Returns a map of space-separated key-value pairs from the specified key. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getStrings(String key) |
| | | * @param n |
| | | * @return map of string, string |
| | |
| | | /** |
| | | * Returns the list of keys whose name starts with the specified prefix. If |
| | | * the prefix is null or empty, all key names are returned. |
| | | * |
| | | * |
| | | * @see IStoredSettings.getAllKeys(String key) |
| | | * @param startingWith |
| | | * @return list of keys |
| | |
| | | |
| | | /** |
| | | * Is Gitblit running in debug mode? |
| | | * |
| | | * |
| | | * @return true if Gitblit is running in debug mode |
| | | */ |
| | | public static boolean isDebugMode() { |
| | |
| | | |
| | | /** |
| | | * Returns the file object for the specified configuration key. |
| | | * |
| | | * |
| | | * @return the file |
| | | */ |
| | | public static File getFileOrFolder(String key, String defaultFileOrFolder) { |
| | |
| | | * file or folder retrievals are (at least initially) funneled through this |
| | | * method so it is the correct point to globally override/alter filesystem |
| | | * access based on environment or some other indicator. |
| | | * |
| | | * |
| | | * @return the file |
| | | */ |
| | | public static File getFileOrFolder(String fileOrFolder) { |
| | |
| | | /** |
| | | * Returns the path of the repositories folder. This method checks to see if |
| | | * Gitblit is running on a cloud service and may return an adjusted path. |
| | | * |
| | | * |
| | | * @return the repositories folder path |
| | | */ |
| | | public static File getRepositoriesFolder() { |
| | |
| | | /** |
| | | * Returns the path of the proposals folder. This method checks to see if |
| | | * Gitblit is running on a cloud service and may return an adjusted path. |
| | | * |
| | | * |
| | | * @return the proposals folder path |
| | | */ |
| | | public static File getProposalsFolder() { |
| | |
| | | /** |
| | | * Returns the path of the Groovy folder. This method checks to see if |
| | | * Gitblit is running on a cloud service and may return an adjusted path. |
| | | * |
| | | * |
| | | * @return the Groovy scripts folder path |
| | | */ |
| | | public static File getGroovyScriptsFolder() { |
| | | return getFileOrFolder(Keys.groovy.scriptsFolder, "${baseFolder}/groovy"); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Updates the list of server settings. |
| | | * |
| | | * |
| | | * @param settings |
| | | * @return true if the update succeeded |
| | | */ |
| | |
| | | serverStatus.heapFree = Runtime.getRuntime().freeMemory(); |
| | | return serverStatus; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns a list of repository URLs and the user access permission. |
| | | * |
| | | * |
| | | * @param request |
| | | * @param user |
| | | * @param repository |
| | |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | |
| | | protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | sb.append(HttpUtils.getGitblitURL(request)); |
| | | sb.append(Constants.GIT_PATH); |
| | | sb.append(repository.name); |
| | | |
| | | |
| | | // inject username into repository url if authentication is required |
| | | if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE) |
| | | && !StringUtils.isEmpty(username)) { |
| | |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | |
| | | protected String getGitDaemonUrl(HttpServletRequest request, UserModel user, RepositoryModel repository) { |
| | | if (gitDaemon != null) { |
| | | String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); |
| | |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | protected AccessPermission getGitDaemonAccessPermission(UserModel user, RepositoryModel repository) { |
| | | if (gitDaemon != null && user.canClone(repository)) { |
| | | AccessPermission gitDaemonPermission = user.getRepositoryPermission(repository).permission; |
| | |
| | | /** |
| | | * Returns the list of custom client applications to be used for the |
| | | * repository url panel; |
| | | * |
| | | * |
| | | * @return a collection of client applications |
| | | */ |
| | | public Collection<GitClientApplication> getClientApplications() { |
| | |
| | | if (clients != null) { |
| | | clientApplications.updateObject("user", lastModified, clients); |
| | | return clients; |
| | | } |
| | | } |
| | | } catch (IOException e) { |
| | | logger.error("Failed to deserialize " + userDefs.getAbsolutePath(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // no user definitions, use system definitions |
| | | if (!clientApplications.hasCurrent("system", new Date(0))) { |
| | | try { |
| | |
| | | logger.error("Failed to deserialize clientapps.json resource!", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | return clientApplications.getObject("system"); |
| | | } |
| | | |
| | | |
| | | private Collection<GitClientApplication> readClientApplications(InputStream is) { |
| | | try { |
| | | Type type = new TypeToken<Collection<GitClientApplication>>() { |
| | |
| | | /** |
| | | * Set the user service. The user service authenticates all users and is |
| | | * responsible for managing user permissions. |
| | | * |
| | | * |
| | | * @param userService |
| | | */ |
| | | public void setUserService(IUserService userService) { |
| | |
| | | this.userService = userService; |
| | | this.userService.setup(settings); |
| | | } |
| | | |
| | | |
| | | public boolean supportsAddUser() { |
| | | return supportsCredentialChanges(new UserModel("")); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns true if the user's credentials can be changed. |
| | | * |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports credential changes |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Returns true if the user's display name can be changed. |
| | | * |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports display name changes |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Returns true if the user's email address can be changed. |
| | | * |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports email address changes |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Returns true if the user's team memberships can be changed. |
| | | * |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports team membership changes |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Returns true if the username represents an internal account |
| | | * |
| | | * |
| | | * @param username |
| | | * @return true if the specified username represents an internal account |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Authenticate a user based on a username and password. |
| | | * |
| | | * |
| | | * @see IUserService.authenticate(String, char[]) |
| | | * @param username |
| | | * @param password |
| | |
| | | |
| | | /** |
| | | * Authenticate a user based on their cookie. |
| | | * |
| | | * |
| | | * @param cookies |
| | | * @return a user object or null |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Authenticate a user based on HTTP request parameters. |
| | | * |
| | | * |
| | | * Authentication by X509Certificate is tried first and then by cookie. |
| | | * |
| | | * |
| | | * @param httpRequest |
| | | * @return a user object or null |
| | | */ |
| | | public UserModel authenticate(HttpServletRequest httpRequest) { |
| | | return authenticate(httpRequest, false); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Authenticate a user based on HTTP request parameters. |
| | | * |
| | | * |
| | | * Authentication by X509Certificate, servlet container principal, cookie, |
| | | * and BASIC header. |
| | | * |
| | | * |
| | | * @param httpRequest |
| | | * @param requiresCertificate |
| | | * @return a user object or null |
| | |
| | | model.username, metadata.serialNumber, httpRequest.getRemoteAddr())); |
| | | } |
| | | } |
| | | |
| | | |
| | | if (requiresCertificate) { |
| | | // caller requires client certificate authentication (e.g. git servlet) |
| | | return null; |
| | | } |
| | | |
| | | |
| | | // try to authenticate by servlet container principal |
| | | Principal principal = httpRequest.getUserPrincipal(); |
| | | if (principal != null) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // try to authenticate by cookie |
| | | if (allowCookieAuthentication()) { |
| | | UserModel user = authenticate(httpRequest.getCookies()); |
| | |
| | | return user; |
| | | } |
| | | } |
| | | |
| | | |
| | | // try to authenticate by BASIC |
| | | final String authorization = httpRequest.getHeader("Authorization"); |
| | | if (authorization != null && authorization.startsWith("Basic")) { |
| | |
| | | user.username, httpRequest.getRemoteAddr())); |
| | | return user; |
| | | } else { |
| | | logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}", |
| | | logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}", |
| | | username, httpRequest.getRemoteAddr())); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | protected void flagWicketSession(AuthenticationType authenticationType) { |
| | | RequestCycle requestCycle = RequestCycle.get(); |
| | | if (requestCycle != null) { |
| | |
| | | |
| | | /** |
| | | * Sets a cookie for the specified user. |
| | | * |
| | | * |
| | | * @param response |
| | | * @param user |
| | | */ |
| | |
| | | response.addCookie(userCookie); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Logout a user. |
| | | * |
| | | * |
| | | * @param user |
| | | */ |
| | | public void logout(UserModel user) { |
| | |
| | | |
| | | /** |
| | | * Encode the username for user in an url. |
| | | * |
| | | * |
| | | * @param name |
| | | * @return the encoded name |
| | | */ |
| | | protected String encodeUsername(String name) { |
| | | return name.replace("@", "%40").replace(" ", "%20").replace("\\", "%5C"); |
| | | return name.replace("@", "%40").replace(" ", "%20").replace("\\", "%5C"); |
| | | } |
| | | |
| | | /** |
| | | * Decode a username from an encoded url. |
| | | * |
| | | * |
| | | * @param name |
| | | * @return the decoded name |
| | | */ |
| | | protected String decodeUsername(String name) { |
| | | return name.replace("%40", "@").replace("%20", " ").replace("%5C", "\\"); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of all users available to the login service. |
| | | * |
| | | * |
| | | * @see IUserService.getAllUsernames() |
| | | * @return list of all usernames |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Returns the list of all users available to the login service. |
| | | * |
| | | * |
| | | * @see IUserService.getAllUsernames() |
| | | * @return list of all usernames |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Delete the user object with the specified username |
| | | * |
| | | * |
| | | * @see IUserService.deleteUser(String) |
| | | * @param username |
| | | * @return true if successful |
| | |
| | | String usernameDecoded = decodeUsername(username); |
| | | return userService.deleteUser(usernameDecoded); |
| | | } |
| | | |
| | | |
| | | protected UserModel getFederationUser() { |
| | | // the federation user is an administrator |
| | | UserModel federationUser = new UserModel(Constants.FEDERATION_USER); |
| | |
| | | |
| | | /** |
| | | * Retrieve the user object for the specified username. |
| | | * |
| | | * |
| | | * @see IUserService.getUserModel(String) |
| | | * @param username |
| | | * @return a user object or null |
| | |
| | | return null; |
| | | } |
| | | String usernameDecoded = decodeUsername(username); |
| | | UserModel user = userService.getUserModel(usernameDecoded); |
| | | UserModel user = userService.getUserModel(usernameDecoded); |
| | | return user; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the effective list of permissions for this user, taking into account |
| | | * team memberships, ownerships. |
| | | * |
| | | * |
| | | * @param user |
| | | * @return the effective list of permissions for the user |
| | | */ |
| | |
| | | set.add(rp); |
| | | } |
| | | } |
| | | |
| | | |
| | | List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(set); |
| | | Collections.sort(list); |
| | | return list; |
| | |
| | | * Returns the list of users and their access permissions for the specified |
| | | * repository including permission source information such as the team or |
| | | * regular expression which sets the permission. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @return a list of RegistrantAccessPermissions |
| | | */ |
| | |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Sets the access permissions to the specified repository for the specified users. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @param permissions |
| | | * @return true if the user models have been updated |
| | |
| | | } |
| | | return userService.updateUserModels(users); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of all users who have an explicit access permission |
| | | * for the specified repository. |
| | | * |
| | | * |
| | | * @see IUserService.getUsernamesForRepositoryRole(String) |
| | | * @param repository |
| | | * @return list of all usernames that have an access permission for the repository |
| | |
| | | /** |
| | | * Sets the list of all uses who are allowed to bypass the access |
| | | * restriction placed on the specified repository. |
| | | * |
| | | * |
| | | * @see IUserService.setUsernamesForRepositoryRole(String, List<String>) |
| | | * @param repository |
| | | * @param usernames |
| | |
| | | /** |
| | | * Adds/updates a complete user object keyed by username. This method allows |
| | | * for renaming a user. |
| | | * |
| | | * |
| | | * @see IUserService.updateUserModel(String, UserModel) |
| | | * @param username |
| | | * @param user |
| | |
| | | "Failed to rename ''{0}'' because ''{1}'' already exists.", username, |
| | | user.username)); |
| | | } |
| | | |
| | | |
| | | // rename repositories and owner fields for all repositories |
| | | for (RepositoryModel model : getRepositoryModels(user)) { |
| | | if (model.isUsersPersonalRepository(username)) { |
| | |
| | | /** |
| | | * Returns the list of available teams that a user or repository may be |
| | | * assigned to. |
| | | * |
| | | * |
| | | * @return the list of teams |
| | | */ |
| | | public List<String> getAllTeamnames() { |
| | |
| | | /** |
| | | * Returns the list of available teams that a user or repository may be |
| | | * assigned to. |
| | | * |
| | | * |
| | | * @return the list of teams |
| | | */ |
| | | public List<TeamModel> getAllTeams() { |
| | |
| | | |
| | | /** |
| | | * Returns the TeamModel object for the specified name. |
| | | * |
| | | * |
| | | * @param teamname |
| | | * @return a TeamModel object or null |
| | | */ |
| | | public TeamModel getTeamModel(String teamname) { |
| | | return userService.getTeamModel(teamname); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of teams and their access permissions for the specified |
| | | * repository including the source of the permission such as the admin flag |
| | | * or a regular expression. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @return a list of RegistrantAccessPermissions |
| | | */ |
| | |
| | | Collections.sort(list); |
| | | return list; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Sets the access permissions to the specified repository for the specified teams. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @param permissions |
| | | * @return true if the team models have been updated |
| | |
| | | } |
| | | return userService.updateTeamModels(teams); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of all teams who have an explicit access permission for |
| | | * the specified repository. |
| | | * |
| | | * |
| | | * @see IUserService.getTeamnamesForRepositoryRole(String) |
| | | * @param repository |
| | | * @return list of all teamnames with explicit access permissions to the repository |
| | |
| | | /** |
| | | * Sets the list of all uses who are allowed to bypass the access |
| | | * restriction placed on the specified repository. |
| | | * |
| | | * |
| | | * @see IUserService.setTeamnamesForRepositoryRole(String, List<String>) |
| | | * @param repository |
| | | * @param teamnames |
| | |
| | | |
| | | /** |
| | | * Updates the TeamModel object for the specified name. |
| | | * |
| | | * |
| | | * @param teamname |
| | | * @param team |
| | | * @param isCreate |
| | |
| | | |
| | | /** |
| | | * Delete the team object with the specified teamname |
| | | * |
| | | * |
| | | * @see IUserService.deleteTeam(String) |
| | | * @param teamname |
| | | * @return true if successful |
| | |
| | | public boolean deleteTeam(String teamname) { |
| | | return userService.deleteTeam(teamname); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Adds the repository to the list of cached repositories if Gitblit is |
| | | * configured to cache the repository list. |
| | | * |
| | | * |
| | | * @param model |
| | | */ |
| | | private void addToCachedRepositoryList(RepositoryModel model) { |
| | | if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { |
| | | repositoryListCache.put(model.name.toLowerCase(), model); |
| | | |
| | | |
| | | // update the fork origin repository with this repository clone |
| | | if (!StringUtils.isEmpty(model.originRepository)) { |
| | | if (repositoryListCache.containsKey(model.originRepository)) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Removes the repository from the list of cached repositories. |
| | | * |
| | | * |
| | | * @param name |
| | | * @return the model being removed |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Clears all the cached metadata for the specified repository. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | */ |
| | | private void clearRepositoryMetadataCache(String repositoryName) { |
| | | repositorySizeCache.remove(repositoryName); |
| | | repositoryMetricsCache.remove(repositoryName); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Resets the repository list cache. |
| | | * |
| | | * |
| | | */ |
| | | public void resetRepositoryListCache() { |
| | | logger.info("Repository cache manually reset"); |
| | | repositoryListCache.clear(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Calculate the checksum of settings that affect the repository list cache. |
| | | * @return a checksum |
| | |
| | | String checksum = StringUtils.getSHA1(ns.toString()); |
| | | return checksum; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Compare the last repository list setting checksum to the current checksum. |
| | | * If different then clear the cache so that it may be rebuilt. |
| | | * |
| | | * |
| | | * @return true if the cached repository list is valid since the last check |
| | | */ |
| | | private boolean isValidRepositoryList() { |
| | |
| | | /** |
| | | * Returns the list of all repositories available to Gitblit. This method |
| | | * does not consider user access permissions. |
| | | * |
| | | * |
| | | * @return list of all repositories |
| | | */ |
| | | public List<String> getRepositoryList() { |
| | | if (repositoryListCache.size() == 0 || !isValidRepositoryList()) { |
| | | // we are not caching OR we have not yet cached OR the cached list is invalid |
| | | long startTime = System.currentTimeMillis(); |
| | | List<String> repositories = JGitUtils.getRepositoryList(repositoriesFolder, |
| | | List<String> repositories = JGitUtils.getRepositoryList(repositoriesFolder, |
| | | settings.getBoolean(Keys.git.onlyAccessBareRepositories, false), |
| | | settings.getBoolean(Keys.git.searchRepositoriesSubfolders, true), |
| | | settings.getInteger(Keys.git.searchRecursionDepth, -1), |
| | |
| | | // optionally (re)calculate repository sizes |
| | | msg = "{0} repositories identified with calculated folder sizes in {1} msecs"; |
| | | } |
| | | |
| | | |
| | | for (String repository : repositories) { |
| | | getRepositoryModel(repository); |
| | | } |
| | | |
| | | |
| | | // rebuild fork networks |
| | | for (RepositoryModel model : repositoryListCache.values()) { |
| | | if (!StringUtils.isEmpty(model.originRepository)) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | long duration = System.currentTimeMillis() - startTime; |
| | | logger.info(MessageFormat.format(msg, repositoryListCache.size(), duration)); |
| | | } |
| | | } |
| | | |
| | | |
| | | // return sorted copy of cached list |
| | | List<String> list = new ArrayList<String>(); |
| | | for (RepositoryModel model : repositoryListCache.values()) { |
| | |
| | | |
| | | /** |
| | | * Returns the JGit repository for the specified name. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @return repository or null |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Returns the JGit repository for the specified name. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param logError |
| | | * @return repository or null |
| | |
| | | // Decode url-encoded repository name (issue-278) |
| | | // http://stackoverflow.com/questions/17183110 |
| | | repositoryName = repositoryName.replace("%7E", "~").replace("%7e", "~"); |
| | | |
| | | |
| | | if (isCollectingGarbage(repositoryName)) { |
| | | logger.warn(MessageFormat.format("Rejecting request for {0}, busy collecting garbage!", repositoryName)); |
| | | return null; |
| | |
| | | File dir = FileKey.resolve(new File(repositoriesFolder, repositoryName), FS.DETECTED); |
| | | if (dir == null) |
| | | return null; |
| | | |
| | | |
| | | Repository r = null; |
| | | try { |
| | | FileKey key = FileKey.exact(dir, FS.DETECTED); |
| | |
| | | |
| | | /** |
| | | * Returns the list of repository models that are accessible to the user. |
| | | * |
| | | * |
| | | * @param user |
| | | * @return list of repository models accessible to user |
| | | */ |
| | |
| | | /** |
| | | * Returns a repository model if the repository exists and the user may |
| | | * access the repository. |
| | | * |
| | | * |
| | | * @param user |
| | | * @param repositoryName |
| | | * @return repository model or null |
| | |
| | | /** |
| | | * Returns the repository model for the specified repository. This method |
| | | * does not consider user access permissions. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @return repository model or null |
| | | */ |
| | |
| | | addToCachedRepositoryList(model); |
| | | return DeepCopier.copy(model); |
| | | } |
| | | |
| | | |
| | | // cached model |
| | | RepositoryModel model = repositoryListCache.get(repositoryName.toLowerCase()); |
| | | |
| | |
| | | logger.error(MessageFormat.format("Repository \"{0}\" is missing! Removing from cache.", repositoryName)); |
| | | return null; |
| | | } |
| | | |
| | | |
| | | FileBasedConfig config = (FileBasedConfig) getRepositoryConfig(r); |
| | | if (config.isOutdated()) { |
| | | // reload model |
| | |
| | | removeFromCachedRepositoryList(model.name); |
| | | addToCachedRepositoryList(model); |
| | | } else { |
| | | // update a few repository parameters |
| | | // update a few repository parameters |
| | | if (!model.hasCommits) { |
| | | // update hasCommits, assume a repository only gains commits :) |
| | | model.hasCommits = JGitUtils.hasCommits(r); |
| | |
| | | updateLastChangeFields(r, model); |
| | | } |
| | | r.close(); |
| | | |
| | | |
| | | // return a copy of the cached model |
| | | return DeepCopier.copy(model); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the star count of the repository. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @return the star count |
| | | */ |
| | |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | |
| | | private void reloadProjectMarkdown(ProjectModel project) { |
| | | // project markdown |
| | | File pmkd = new File(getRepositoriesFolder(), (project.isRoot ? "" : project.name) + "/project.mkd"); |
| | |
| | | } |
| | | project.projectMarkdown = projectMarkdownCache.getObject(project.name); |
| | | } |
| | | |
| | | |
| | | // project repositories markdown |
| | | File rmkd = new File(getRepositoriesFolder(), (project.isRoot ? "" : project.name) + "/repositories.mkd"); |
| | | if (rmkd.exists()) { |
| | |
| | | project.repositoriesMarkdown = projectRepositoriesMarkdownCache.getObject(project.name); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the map of project config. This map is cached and reloaded if |
| | | * the underlying projects.conf file changes. |
| | | * |
| | | * |
| | | * @return project config map |
| | | */ |
| | | private Map<String, ProjectModel> getProjectConfigs() { |
| | | if (projectCache.isEmpty() || projectConfigs.isOutdated()) { |
| | | |
| | | |
| | | try { |
| | | projectConfigs.load(); |
| | | } catch (Exception e) { |
| | |
| | | } |
| | | project.title = projectConfigs.getString("project", name, "title"); |
| | | project.description = projectConfigs.getString("project", name, "description"); |
| | | |
| | | |
| | | reloadProjectMarkdown(project); |
| | | |
| | | |
| | | configs.put(name.toLowerCase(), project); |
| | | } |
| | | projectCache.clear(); |
| | |
| | | } |
| | | return projectCache; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns a list of project models for the user. |
| | | * |
| | | * |
| | | * @param user |
| | | * @param includeUsers |
| | | * @return list of projects that are accessible to the user |
| | |
| | | Map<String, ProjectModel> map = new TreeMap<String, ProjectModel>(); |
| | | // root project |
| | | map.put("", configs.get("")); |
| | | |
| | | |
| | | for (RepositoryModel model : getRepositoryModels(user)) { |
| | | String rootPath = StringUtils.getRootPath(model.name).toLowerCase(); |
| | | String rootPath = StringUtils.getRootPath(model.name).toLowerCase(); |
| | | if (!map.containsKey(rootPath)) { |
| | | ProjectModel project; |
| | | if (configs.containsKey(rootPath)) { |
| | |
| | | } |
| | | map.get(rootPath).addRepository(model); |
| | | } |
| | | |
| | | |
| | | // sort projects, root project first |
| | | List<ProjectModel> projects; |
| | | if (includeUsers) { |
| | |
| | | } |
| | | return projects; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the project model for the specified user. |
| | | * |
| | | * |
| | | * @param name |
| | | * @param user |
| | | * @return a project model, or null if it does not exist |
| | |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns a project model for the Gitblit/system user. |
| | | * |
| | | * |
| | | * @param name a project name |
| | | * @return a project model or null if the project does not exist |
| | | */ |
| | |
| | | // no repositories == no project |
| | | return null; |
| | | } |
| | | |
| | | |
| | | reloadProjectMarkdown(project); |
| | | return project; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of project models that are referenced by the supplied |
| | | * repository model list. This is an alternative method exists to ensure |
| | | * Gitblit does not call getRepositoryModels(UserModel) twice in a request. |
| | | * |
| | | * |
| | | * @param repositoryModels |
| | | * @param includeUsers |
| | | * @return a list of project models |
| | |
| | | } |
| | | return new ArrayList<ProjectModel>(projects.values()); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Workaround JGit. I need to access the raw config object directly in order |
| | | * to see if the config is dirty so that I can reload a repository model. |
| | |
| | | * config. If the config changes are made within Gitblit this is fine as |
| | | * the returned config will still be flagged as dirty. BUT... if the config |
| | | * is manipulated outside Gitblit then it fails to recognize this as dirty. |
| | | * |
| | | * |
| | | * @param r |
| | | * @return a config |
| | | */ |
| | |
| | | } |
| | | return r.getConfig(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a repository model from the configuration and repository data. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @return a repositoryModel or null if the repository does not exist |
| | | */ |
| | |
| | | model.name = repositoryName; |
| | | } |
| | | model.projectPath = StringUtils.getFirstPathElement(repositoryName); |
| | | |
| | | |
| | | StoredConfig config = r.getConfig(); |
| | | boolean hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url")); |
| | | |
| | | |
| | | if (config != null) { |
| | | // Initialize description from description file |
| | | if (getConfig(config,"description", null) == null) { |
| | |
| | | Constants.CONFIG_GITBLIT, null, "indexBranch"))); |
| | | model.metricAuthorExclusions = new ArrayList<String>(Arrays.asList(config.getStringList( |
| | | Constants.CONFIG_GITBLIT, null, "metricAuthorExclusions"))); |
| | | |
| | | |
| | | // Custom defined properties |
| | | model.customFields = new LinkedHashMap<String, String>(); |
| | | for (String aProperty : config.getNames(Constants.CONFIG_GITBLIT, Constants.CONFIG_CUSTOM_FIELDS)) { |
| | |
| | | model.hasCommits = JGitUtils.hasCommits(r); |
| | | updateLastChangeFields(r, model); |
| | | r.close(); |
| | | |
| | | |
| | | if (StringUtils.isEmpty(model.originRepository) && model.origin != null && model.origin.startsWith("file://")) { |
| | | // repository was cloned locally... perhaps as a fork |
| | | try { |
| | |
| | | File repoFolder = new File(getRepositoriesFolder(), originRepo); |
| | | if (repoFolder.exists()) { |
| | | model.originRepository = originRepo.toLowerCase(); |
| | | |
| | | |
| | | // persist the fork origin |
| | | updateConfiguration(r, model); |
| | | } |
| | |
| | | } |
| | | return model; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Determines if this server has the requested repository. |
| | | * |
| | | * |
| | | * @param n |
| | | * @return true if the repository exists |
| | | */ |
| | | public boolean hasRepository(String repositoryName) { |
| | | return hasRepository(repositoryName, false); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Determines if this server has the requested repository. |
| | | * |
| | | * |
| | | * @param n |
| | | * @param caseInsensitive |
| | | * @return true if the repository exists |
| | |
| | | // if we are caching use the cache to determine availability |
| | | // otherwise we end up adding a phantom repository to the cache |
| | | return repositoryListCache.containsKey(repositoryName.toLowerCase()); |
| | | } |
| | | } |
| | | Repository r = getRepository(repositoryName, false); |
| | | if (r == null) { |
| | | return false; |
| | |
| | | r.close(); |
| | | return true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Determines if the specified user has a fork of the specified origin |
| | | * repository. |
| | | * |
| | | * |
| | | * @param username |
| | | * @param origin |
| | | * @return true the if the user has a fork |
| | |
| | | public boolean hasFork(String username, String origin) { |
| | | return getFork(username, origin) != null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Gets the name of a user's fork of the specified origin |
| | | * repository. |
| | | * |
| | | * |
| | | * @param username |
| | | * @param origin |
| | | * @return the name of the user's fork, null otherwise |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | if (originModel.originRepository != null) { |
| | | roots.add(originModel.originRepository); |
| | | originModel = repositoryListCache.get(originModel.originRepository); |
| | |
| | | originModel = null; |
| | | } |
| | | } |
| | | |
| | | |
| | | for (String repository : repositoryListCache.keySet()) { |
| | | if (repository.startsWith(userPath)) { |
| | | RepositoryModel model = repositoryListCache.get(repository); |
| | |
| | | // user does not have a fork |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the fork network for a repository by traversing up the fork graph |
| | | * to discover the root and then down through all children of the root node. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @return a ForkModel |
| | | */ |
| | |
| | | return root; |
| | | } |
| | | } |
| | | |
| | | |
| | | private ForkModel getForkModelFromCache(String repository) { |
| | | RepositoryModel model = repositoryListCache.get(repository.toLowerCase()); |
| | | if (model == null) { |
| | |
| | | } |
| | | return fork; |
| | | } |
| | | |
| | | |
| | | private ForkModel getForkModel(String repository) { |
| | | RepositoryModel model = getRepositoryModel(repository.toLowerCase()); |
| | | if (model == null) { |
| | |
| | | * repository. Gitblit caches the repository sizes to reduce the performance |
| | | * penalty of recursive calculation. The cache is updated if the repository |
| | | * has been changed since the last calculation. |
| | | * |
| | | * |
| | | * @param model |
| | | * @return size in bytes of the repository |
| | | */ |
| | |
| | | /** |
| | | * Ensure that a cached repository is completely closed and its resources |
| | | * are properly released. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | */ |
| | | private void closeRepository(String repositoryName) { |
| | |
| | | repository.close(); |
| | | } |
| | | } |
| | | |
| | | |
| | | // close any open index writer/searcher in the Lucene executor |
| | | luceneExecutor.close(repositoryName); |
| | | } |
| | |
| | | * This method builds a metrics cache. The cache is updated if the |
| | | * repository is updated. A new copy of the metrics list is returned on each |
| | | * call so that modifications to the list are non-destructive. |
| | | * |
| | | * |
| | | * @param model |
| | | * @param repository |
| | | * @return a new array list of metrics |
| | |
| | | /** |
| | | * Returns the gitblit string value for the specified key. If key is not |
| | | * set, returns defaultValue. |
| | | * |
| | | * |
| | | * @param config |
| | | * @param field |
| | | * @param defaultValue |
| | |
| | | /** |
| | | * Returns the gitblit boolean value for the specified key. If key is not |
| | | * set, returns defaultValue. |
| | | * |
| | | * |
| | | * @param config |
| | | * @param field |
| | | * @param defaultValue |
| | |
| | | private boolean getConfig(StoredConfig config, String field, boolean defaultValue) { |
| | | return config.getBoolean(Constants.CONFIG_GITBLIT, field, defaultValue); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the gitblit string value for the specified key. If key is not |
| | | * set, returns defaultValue. |
| | | * |
| | | * |
| | | * @param config |
| | | * @param field |
| | | * @param defaultValue |
| | |
| | | * Creates/updates the repository model keyed by reopsitoryName. Saves all |
| | | * repository settings in .git/config. This method allows for renaming |
| | | * repositories and will update user access permissions accordingly. |
| | | * |
| | | * |
| | | * All repositories created by this method are bare and automatically have |
| | | * .git appended to their names, which is the standard convention for bare |
| | | * repositories. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param isCreate |
| | |
| | | "Failed to rename repository permissions ''{0}'' to ''{1}''.", |
| | | repositoryName, repository.name)); |
| | | } |
| | | |
| | | |
| | | // rename fork origins in their configs |
| | | if (!ArrayUtils.isEmpty(repository.forks)) { |
| | | for (String fork : repository.forks) { |
| | |
| | | rf.close(); |
| | | } |
| | | } |
| | | |
| | | |
| | | // update this repository's origin's fork list |
| | | if (!StringUtils.isEmpty(repository.originRepository)) { |
| | | RepositoryModel origin = repositoryListCache.get(repository.originRepository); |
| | |
| | | // only update symbolic head if it changes |
| | | String currentRef = JGitUtils.getHEADRef(r); |
| | | if (!StringUtils.isEmpty(repository.HEAD) && !repository.HEAD.equals(currentRef)) { |
| | | logger.info(MessageFormat.format("Relinking {0} HEAD from {1} to {2}", |
| | | logger.info(MessageFormat.format("Relinking {0} HEAD from {1} to {2}", |
| | | repository.name, currentRef, repository.HEAD)); |
| | | if (JGitUtils.setHEADtoRef(r, repository.HEAD)) { |
| | | // clear the cache |
| | |
| | | // model will actually be replaced on next load because config is stale |
| | | addToCachedRepositoryList(repository); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Updates the Gitblit configuration for the specified repository. |
| | | * |
| | | * |
| | | * @param r |
| | | * the Git repository |
| | | * @param repository |
| | |
| | | config.setString(Constants.CONFIG_GITBLIT, null, "commitMessageRenderer", |
| | | repository.commitMessageRenderer.name()); |
| | | } |
| | | |
| | | |
| | | updateList(config, "federationSets", repository.federationSets); |
| | | updateList(config, "preReceiveScript", repository.preReceiveScripts); |
| | | updateList(config, "postReceiveScript", repository.postReceiveScripts); |
| | | updateList(config, "mailingList", repository.mailingLists); |
| | | updateList(config, "indexBranch", repository.indexedBranches); |
| | | updateList(config, "metricAuthorExclusions", repository.metricAuthorExclusions); |
| | | |
| | | |
| | | // User Defined Properties |
| | | if (repository.customFields != null) { |
| | | if (repository.customFields.size() == 0) { |
| | |
| | | logger.error("Failed to save repository config!", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | private void updateList(StoredConfig config, String field, List<String> list) { |
| | | // a null list is skipped, not cleared |
| | | // this is for RPC administration where an older manager might be used |
| | |
| | | /** |
| | | * Deletes the repository from the file system and removes the repository |
| | | * permission from all repository users. |
| | | * |
| | | * |
| | | * @param model |
| | | * @return true if successful |
| | | */ |
| | |
| | | /** |
| | | * Deletes the repository from the file system and removes the repository |
| | | * permission from all repository users. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @return true if successful |
| | | */ |
| | |
| | | closeRepository(repositoryName); |
| | | // clear the repository cache |
| | | clearRepositoryMetadataCache(repositoryName); |
| | | |
| | | |
| | | RepositoryModel model = removeFromCachedRepositoryList(repositoryName); |
| | | if (model != null && !ArrayUtils.isEmpty(model.forks)) { |
| | | resetRepositoryListCache(); |
| | |
| | | /** |
| | | * Returns an html version of the commit message with any global or |
| | | * repository-specific regular expression substitution applied. |
| | | * |
| | | * |
| | | * This method uses the preferred renderer to transform the commit message. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @param text |
| | | * @return html version of the commit message |
| | |
| | | // noop |
| | | break; |
| | | } |
| | | |
| | | |
| | | return processPlainCommitMessage(repository.name, text); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns an html version of the commit message with any global or |
| | | * repository-specific regular expression substitution applied. |
| | | * |
| | | * |
| | | * This method assumes the commit message is plain text. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param text |
| | | * @return html version of the commit message |
| | |
| | | String html = StringUtils.escapeForHtml(text, false); |
| | | html = processCommitMessageRegex(repositoryName, html); |
| | | return StringUtils.breakLinesForHtml(html); |
| | | |
| | | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Apply globally or per-repository specified regex substitutions to the |
| | | * commit message. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param text |
| | | * @return the processed commit message |
| | |
| | | |
| | | /** |
| | | * Returns Gitblit's scheduled executor service for scheduling tasks. |
| | | * |
| | | * |
| | | * @return scheduledExecutor |
| | | */ |
| | | public ScheduledExecutorService executor() { |
| | |
| | | /** |
| | | * Returns the list of federated gitblit instances that this instance will |
| | | * try to pull. |
| | | * |
| | | * |
| | | * @return list of registered gitblit instances |
| | | */ |
| | | public List<FederationModel> getFederationRegistrations() { |
| | |
| | | |
| | | /** |
| | | * Retrieve the specified federation registration. |
| | | * |
| | | * |
| | | * @param name |
| | | * the name of the registration |
| | | * @return a federation registration |
| | |
| | | |
| | | /** |
| | | * Returns the list of federation sets. |
| | | * |
| | | * |
| | | * @return list of federation sets |
| | | */ |
| | | public List<FederationSet> getFederationSets(String gitblitUrl) { |
| | |
| | | |
| | | /** |
| | | * Returns the list of possible federation tokens for this Gitblit instance. |
| | | * |
| | | * |
| | | * @return list of federation tokens |
| | | */ |
| | | public List<String> getFederationTokens() { |
| | |
| | | |
| | | /** |
| | | * Returns the specified federation token for this Gitblit instance. |
| | | * |
| | | * |
| | | * @param type |
| | | * @return a federation token |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Returns the specified federation token for this Gitblit instance. |
| | | * |
| | | * |
| | | * @param value |
| | | * @return a federation token |
| | | */ |
| | |
| | | /** |
| | | * Compares the provided token with this Gitblit instance's tokens and |
| | | * determines if the requested permission may be granted to the token. |
| | | * |
| | | * |
| | | * @param req |
| | | * @param token |
| | | * @return true if the request can be executed |
| | |
| | | |
| | | /** |
| | | * Acknowledge and cache the status of a remote Gitblit instance. |
| | | * |
| | | * |
| | | * @param identification |
| | | * the identification of the pulling Gitblit instance |
| | | * @param registration |
| | |
| | | |
| | | /** |
| | | * Returns the list of registration results. |
| | | * |
| | | * |
| | | * @return the list of registration results |
| | | */ |
| | | public List<FederationModel> getFederationResultRegistrations() { |
| | |
| | | /** |
| | | * Submit a federation proposal. The proposal is cached locally and the |
| | | * Gitblit administrator(s) are notified via email. |
| | | * |
| | | * |
| | | * @param proposal |
| | | * the proposal |
| | | * @param gitblitUrl |
| | |
| | | |
| | | /** |
| | | * Returns the list of pending federation proposals |
| | | * |
| | | * |
| | | * @return list of federation proposals |
| | | */ |
| | | public List<FederationProposal> getPendingFederationProposals() { |
| | |
| | | |
| | | /** |
| | | * Get repositories for the specified token. |
| | | * |
| | | * |
| | | * @param gitblitUrl |
| | | * the base url of this gitblit instance |
| | | * @param token |
| | |
| | | |
| | | /** |
| | | * Creates a proposal from the token. |
| | | * |
| | | * |
| | | * @param gitblitUrl |
| | | * the url of this Gitblit instance |
| | | * @param token |
| | |
| | | |
| | | /** |
| | | * Returns the proposal identified by the supplied token. |
| | | * |
| | | * |
| | | * @param token |
| | | * @return the specified proposal or null |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Deletes a pending federation proposal. |
| | | * |
| | | * |
| | | * @param a |
| | | * proposal |
| | | * @return true if the proposal was deleted |
| | |
| | | /** |
| | | * Returns the list of all Groovy push hook scripts. Script files must have |
| | | * .groovy extension |
| | | * |
| | | * |
| | | * @return list of available hook scripts |
| | | */ |
| | | public List<String> getAllScripts() { |
| | |
| | | /** |
| | | * Returns the list of pre-receive scripts the repository inherited from the |
| | | * global settings and team affiliations. |
| | | * |
| | | * |
| | | * @param repository |
| | | * if null only the globally specified scripts are returned |
| | | * @return a list of scripts |
| | |
| | | * Returns the list of all available Groovy pre-receive push hook scripts |
| | | * that are not already inherited by the repository. Script files must have |
| | | * .groovy extension |
| | | * |
| | | * |
| | | * @param repository |
| | | * optional parameter |
| | | * @return list of available hook scripts |
| | |
| | | /** |
| | | * Returns the list of post-receive scripts the repository inherited from |
| | | * the global settings and team affiliations. |
| | | * |
| | | * |
| | | * @param repository |
| | | * if null only the globally specified scripts are returned |
| | | * @return a list of scripts |
| | |
| | | * Returns the list of unused Groovy post-receive push hook scripts that are |
| | | * not already inherited by the repository. Script files must have .groovy |
| | | * extension |
| | | * |
| | | * |
| | | * @param repository |
| | | * optional parameter |
| | | * @return list of available hook scripts |
| | |
| | | } |
| | | return scripts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Search the specified repositories using the Lucene query. |
| | | * |
| | | * |
| | | * @param query |
| | | * @param page |
| | | * @param pageSize |
| | | * @param repositories |
| | | * @return |
| | | */ |
| | | public List<SearchResult> search(String query, int page, int pageSize, List<String> repositories) { |
| | | public List<SearchResult> search(String query, int page, int pageSize, List<String> repositories) { |
| | | List<SearchResult> srs = luceneExecutor.search(query, page, pageSize, repositories); |
| | | return srs; |
| | | } |
| | | |
| | | /** |
| | | * Notify the administrators by email. |
| | | * |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | |
| | | Message mail = mailExecutor.createMessage(toAddresses); |
| | | if (mail != null) { |
| | | mail.setSubject(subject); |
| | | |
| | | MimeBodyPart messagePart = new MimeBodyPart(); |
| | | |
| | | MimeBodyPart messagePart = new MimeBodyPart(); |
| | | messagePart.setText(message, "utf-8"); |
| | | messagePart.setHeader("Content-Type", "text/plain; charset=\"utf-8\""); |
| | | messagePart.setHeader("Content-Transfer-Encoding", "quoted-printable"); |
| | | |
| | | |
| | | MimeMultipart multiPart = new MimeMultipart(); |
| | | multiPart.addBodyPart(messagePart); |
| | | mail.setContent(multiPart); |
| | | |
| | | |
| | | mailExecutor.queue(mail); |
| | | } |
| | | } catch (MessagingException e) { |
| | |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | |
| | | Message mail = mailExecutor.createMessage(toAddresses); |
| | | if (mail != null) { |
| | | mail.setSubject(subject); |
| | | |
| | | MimeBodyPart messagePart = new MimeBodyPart(); |
| | | |
| | | MimeBodyPart messagePart = new MimeBodyPart(); |
| | | messagePart.setText(message, "utf-8"); |
| | | messagePart.setHeader("Content-Type", "text/html; charset=\"utf-8\""); |
| | | messagePart.setHeader("Content-Transfer-Encoding", "quoted-printable"); |
| | | |
| | | |
| | | MimeMultipart multiPart = new MimeMultipart(); |
| | | multiPart.addBodyPart(messagePart); |
| | | mail.setContent(multiPart); |
| | |
| | | |
| | | /** |
| | | * Returns the descriptions/comments of the Gitblit config settings. |
| | | * |
| | | * |
| | | * @return SettingsModel |
| | | */ |
| | | public ServerSettings getSettingsModel() { |
| | |
| | | setting.name = key; |
| | | settingsModel.add(setting); |
| | | } |
| | | setting.currentValue = settings.getString(key, ""); |
| | | setting.currentValue = settings.getString(key, ""); |
| | | } |
| | | settingsModel.pushScripts = getAllScripts(); |
| | | return settingsModel; |
| | |
| | | * Parse the properties file and aggregate all the comments by the setting |
| | | * key. A setting model tracks the current value, the default value, the |
| | | * description of the setting and and directives about the setting. |
| | | * |
| | | * |
| | | * @return Map<String, SettingModel> |
| | | */ |
| | | private ServerSettings loadSettingModels() { |
| | |
| | | * Configure the Gitblit singleton with the specified settings source. This |
| | | * source may be file settings (Gitblit GO) or may be web.xml settings |
| | | * (Gitblit WAR). |
| | | * |
| | | * |
| | | * @param settings |
| | | */ |
| | | public void configureContext(IStoredSettings settings, File folder, boolean startFederation) { |
| | |
| | | mailExecutor = new MailExecutor(settings); |
| | | luceneExecutor = new LuceneExecutor(settings, repositoriesFolder); |
| | | gcExecutor = new GCExecutor(settings); |
| | | |
| | | |
| | | // initialize utilities |
| | | String prefix = settings.getString(Keys.git.userRepositoryPrefix, "~"); |
| | | ModelUtils.setUserRepoPrefix(prefix); |
| | |
| | | logger.info("Identifying available repositories..."); |
| | | getRepositoryList(); |
| | | } |
| | | |
| | | |
| | | logTimezone("JVM", TimeZone.getDefault()); |
| | | logTimezone(Constants.NAME, getTimezone()); |
| | | |
| | |
| | | } |
| | | setUserService(loginService); |
| | | } |
| | | |
| | | |
| | | // load and cache the project metadata |
| | | projectConfigs = new FileBasedConfig(getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); |
| | | getProjectConfigs(); |
| | | |
| | | configureMailExecutor(); |
| | | |
| | | configureMailExecutor(); |
| | | configureLuceneIndexing(); |
| | | configureGarbageCollector(); |
| | | if (startFederation) { |
| | |
| | | |
| | | ContainerUtils.CVE_2007_0450.test(); |
| | | } |
| | | |
| | | |
| | | protected void configureMailExecutor() { |
| | | if (mailExecutor.isReady()) { |
| | | logger.info("Mail executor is scheduled to process the message queue every 2 minutes."); |
| | |
| | | logger.warn("Mail server is not properly configured. Mail services disabled."); |
| | | } |
| | | } |
| | | |
| | | |
| | | protected void configureLuceneIndexing() { |
| | | scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2, TimeUnit.MINUTES); |
| | | logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes."); |
| | | } |
| | | |
| | | |
| | | protected void configureGarbageCollector() { |
| | | // schedule gc engine |
| | | if (gcExecutor.isReady()) { |
| | |
| | | delay = (int) ((cd.getTime() - now.getTime())/TimeUtils.MIN); |
| | | String when = delay + " mins"; |
| | | if (delay > 60) { |
| | | when = MessageFormat.format("{0,number,0.0} hours", ((float)delay)/60f); |
| | | when = MessageFormat.format("{0,number,0.0} hours", (delay)/60f); |
| | | } |
| | | logger.info(MessageFormat.format("Next scheculed GC scan is in {0}", when)); |
| | | scheduledExecutor.scheduleAtFixedRate(gcExecutor, delay, 60*24, TimeUnit.MINUTES); |
| | | } |
| | | } |
| | | |
| | | |
| | | protected void configureJGit() { |
| | | // Configure JGit |
| | | WindowCacheConfig cfg = new WindowCacheConfig(); |
| | |
| | | logger.error("Failed to configure JGit parameters!", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | protected void configureFanout() { |
| | | // startup Fanout PubSub service |
| | | if (settings.getInteger(Keys.fanout.port, 0) > 0) { |
| | |
| | | fanoutService.start(); |
| | | } |
| | | } |
| | | |
| | | |
| | | protected void configureGitDaemon() { |
| | | int port = settings.getInteger(Keys.git.daemonPort, 0); |
| | | String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | protected void configureCommitCache() { |
| | | int daysToCache = settings.getInteger(Keys.web.activityCacheDays, 14); |
| | | if (daysToCache <= 0) { |
| | |
| | | daysToCache, commitCount, repoCount, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start))); |
| | | } |
| | | } |
| | | |
| | | |
| | | protected final Logger getLogger() { |
| | | return logger; |
| | | } |
| | | |
| | | |
| | | protected final ScheduledExecutorService getScheduledExecutor() { |
| | | return scheduledExecutor; |
| | | } |
| | |
| | | protected final LuceneExecutor getLuceneExecutor() { |
| | | return luceneExecutor; |
| | | } |
| | | |
| | | |
| | | private void logTimezone(String type, TimeZone zone) { |
| | | SimpleDateFormat df = new SimpleDateFormat("z Z"); |
| | | df.setTimeZone(zone); |
| | |
| | | /** |
| | | * Configure Gitblit from the web.xml, if no configuration has already been |
| | | * specified. |
| | | * |
| | | * |
| | | * @see ServletContextListener.contextInitialize(ServletContextEvent) |
| | | */ |
| | | @Override |
| | |
| | | String contextRealPath = context.getRealPath("/"); |
| | | File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null; |
| | | String openShift = System.getenv("OPENSHIFT_DATA_DIR"); |
| | | |
| | | |
| | | if (!StringUtils.isEmpty(openShift)) { |
| | | // Gitblit is running in OpenShift/JBoss |
| | | File base = new File(openShift); |
| | |
| | | // gitblit.properties setting overrides |
| | | File overrideFile = new File(base, "gitblit.properties"); |
| | | webxmlSettings.applyOverrides(overrideFile); |
| | | |
| | | |
| | | // Copy the included scripts to the configured groovy folder |
| | | String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy"); |
| | | File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path); |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // configure context using the web.xml |
| | | configureContext(webxmlSettings, base, true); |
| | | } else { |
| | | // Gitblit is running in a standard servlet container |
| | | logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>")); |
| | | |
| | | |
| | | String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data"); |
| | | |
| | | |
| | | if (path.contains(Constants.contextFolder$) && contextFolder == null) { |
| | | // warn about null contextFolder (issue-199) |
| | | logger.error(""); |
| | |
| | | logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder)); |
| | | logger.error(""); |
| | | } |
| | | |
| | | |
| | | File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path); |
| | | base.mkdirs(); |
| | | |
| | |
| | | } |
| | | |
| | | // delegate all config to baseFolder/gitblit.properties file |
| | | FileSettings settings = new FileSettings(localSettings.getAbsolutePath()); |
| | | FileSettings settings = new FileSettings(localSettings.getAbsolutePath()); |
| | | configureContext(settings, base, true); |
| | | } |
| | | } |
| | | |
| | | |
| | | settingsModel = loadSettingModels(); |
| | | serverStatus.servletContainer = servletContext.getServerInfo(); |
| | | } |
| | | |
| | | |
| | | protected void extractResources(ServletContext context, String path, File toDir) { |
| | | for (String resource : context.getResourcePaths(path)) { |
| | | // extract the resource to the directory if it does not exist |
| | |
| | | gitDaemon.stop(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * |
| | | * |
| | | * @return true if we are running the gc executor |
| | | */ |
| | | public boolean isCollectingGarbage() { |
| | | return gcExecutor.isRunning(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns true if Gitblit is actively collecting garbage in this repository. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @return true if actively collecting garbage |
| | | */ |
| | |
| | | /** |
| | | * Creates a personal fork of the specified repository. The clone is view |
| | | * restricted by default and the owner of the source repository is given |
| | | * access to the clone. |
| | | * |
| | | * access to the clone. |
| | | * |
| | | * @param repository |
| | | * @param user |
| | | * @return the repository model of the fork, if successful |
| | |
| | | } |
| | | cloneTeams.add(cloneTeam); |
| | | } |
| | | userService.updateTeamModels(cloneTeams); |
| | | userService.updateTeamModels(cloneTeams); |
| | | |
| | | // add this clone to the cached model |
| | | addToCachedRepositoryList(cloneModel); |
| | |
| | | /** |
| | | * Allow to understand if GitBlit supports and is configured to allow |
| | | * cookie-based authentication. |
| | | * |
| | | * |
| | | * @return status of Cookie authentication enablement. |
| | | */ |
| | | public boolean allowCookieAuthentication() { |
| | |
| | |
|
| | | /**
|
| | | * GitBlitException is a marginally useful class. :)
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitBlitException extends IOException {
|
| | |
|
| | |
| | | * simplify command line parameter processing. This class also automatically
|
| | | * generates a self-signed certificate for localhost, if the keystore does not
|
| | | * already exist.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitBlitServer {
|
| | |
|
| | |
| | |
|
| | | public static void main(String... args) {
|
| | | GitBlitServer server = new GitBlitServer();
|
| | | |
| | |
|
| | | // filter out the baseFolder parameter
|
| | | List<String> filtered = new ArrayList<String>();
|
| | | String folder = "data";
|
| | |
| | | filtered.add(arg);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | Params.baseFolder = folder;
|
| | | Params params = new Params();
|
| | | JCommander jc = new JCommander(params);
|
| | |
| | |
|
| | | /**
|
| | | * Display the command line usage of Gitblit GO.
|
| | | * |
| | | *
|
| | | * @param jc
|
| | | * @param t
|
| | | */
|
| | |
| | | FileSettings settings = params.FILESETTINGS;
|
| | | if (!StringUtils.isEmpty(params.settingsfile)) {
|
| | | if (new File(params.settingsfile).exists()) {
|
| | | settings = new FileSettings(params.settingsfile); |
| | | settings = new FileSettings(params.settingsfile);
|
| | | }
|
| | | }
|
| | | logger = LoggerFactory.getLogger(GitBlitServer.class);
|
| | |
| | | String osname = System.getProperty("os.name");
|
| | | String osversion = System.getProperty("os.version");
|
| | | logger.info("Running on " + osname + " (" + osversion + ")");
|
| | | |
| | |
|
| | | List<Connector> connectors = new ArrayList<Connector>();
|
| | |
|
| | | // conditionally configure the http connector
|
| | |
| | | NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config);
|
| | | certificateConfig.update(metadata);
|
| | | }
|
| | | |
| | |
|
| | | metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR);
|
| | | X509Utils.prepareX509Infrastructure(metadata, baseFolder, new X509Log() {
|
| | | @Override
|
| | |
| | | }
|
| | | });
|
| | |
|
| | | if (serverKeyStore.exists()) { |
| | | if (serverKeyStore.exists()) {
|
| | | Connector secureConnector = createSSLConnector(params.alias, serverKeyStore, serverTrustStore, params.storePassword,
|
| | | caRevocationList, params.useNIO, params.securePort, settings.getInteger(Keys.server.threadPoolSize, 50), params.requireClientCertificates);
|
| | | String bindInterface = settings.getString(Keys.server.httpsBindInterface, null);
|
| | |
| | |
|
| | | // tempDir is where the embedded Gitblit web application is expanded and
|
| | | // where Jetty creates any necessary temporary files
|
| | | File tempDir = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.temp); |
| | | File tempDir = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.temp);
|
| | | if (tempDir.exists()) {
|
| | | try {
|
| | | FileUtils.delete(tempDir, FileUtils.RECURSIVE | FileUtils.RETRY);
|
| | |
| | | settings.overrideSetting(Keys.realm.userService, params.userService);
|
| | | settings.overrideSetting(Keys.git.repositoriesFolder, params.repositoriesFolder);
|
| | | settings.overrideSetting(Keys.git.daemonPort, params.gitPort);
|
| | | |
| | |
|
| | | // Start up an in-memory LDAP server, if configured
|
| | | try {
|
| | | if (StringUtils.isEmpty(params.ldapLdifFile) == false) {
|
| | |
| | | String rootDN = firstLine.substring(4);
|
| | | String bindUserName = settings.getString(Keys.realm.ldap.username, "");
|
| | | String bindPassword = settings.getString(Keys.realm.ldap.password, "");
|
| | | |
| | |
|
| | | // Get the port
|
| | | int port = ldapUrl.getPort();
|
| | | if (port == -1)
|
| | | port = 389;
|
| | | |
| | |
|
| | | InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(rootDN);
|
| | | config.addAdditionalBindCredentials(bindUserName, bindPassword);
|
| | | config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", port));
|
| | | config.setSchema(null);
|
| | | |
| | |
|
| | | InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
|
| | | ds.importFromLDIF(true, new LDIFReader(ldifFile));
|
| | | ds.startListening();
|
| | | |
| | |
|
| | | logger.info("LDAP Server started at ldap://localhost:" + port);
|
| | | }
|
| | | }
|
| | |
| | | System.exit(100);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | protected GitBlit getGitBlitInstance() {
|
| | | return GitBlit.self();
|
| | | }
|
| | |
|
| | | /**
|
| | | * Creates an http connector.
|
| | | * |
| | | *
|
| | | * @param useNIO
|
| | | * @param port
|
| | | * @param threadPoolSize
|
| | |
| | |
|
| | | /**
|
| | | * Creates an https connector.
|
| | | * |
| | | *
|
| | | * SSL renegotiation will be enabled if the JVM is 1.6.0_22 or later.
|
| | | * oracle.com/technetwork/java/javase/documentation/tlsreadme2-176330.html
|
| | | * |
| | | *
|
| | | * @param certAlias
|
| | | * @param keyStore
|
| | | * @param clientTrustStore
|
| | |
| | | * @return an https connector
|
| | | */
|
| | | private Connector createSSLConnector(String certAlias, File keyStore, File clientTrustStore,
|
| | | String storePassword, File caRevocationList, boolean useNIO, int port, int threadPoolSize, |
| | | String storePassword, File caRevocationList, boolean useNIO, int port, int threadPoolSize,
|
| | | boolean requireClientCertificates) {
|
| | | GitblitSslContextFactory factory = new GitblitSslContextFactory(certAlias,
|
| | | keyStore, clientTrustStore, storePassword, caRevocationList);
|
| | |
| | |
|
| | | return connector;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Creates an ajp connector.
|
| | | * |
| | | *
|
| | | * @param port
|
| | | * @return an ajp connector
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Tests to see if the operating system is Windows.
|
| | | * |
| | | *
|
| | | * @return true if this is a windows machine
|
| | | */
|
| | | private boolean isWindows() {
|
| | |
| | | * The ShutdownMonitorThread opens a socket on a specified port and waits
|
| | | * for an incoming connection. When that connection is accepted a shutdown
|
| | | * message is issued to the running Jetty server.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | private static class ShutdownMonitorThread extends Thread {
|
| | |
|
| | |
| | | */
|
| | | @Parameter(names = { "--settings" }, description = "Path to alternative settings")
|
| | | public String settingsfile;
|
| | | |
| | |
|
| | | @Parameter(names = { "--ldapLdifFile" }, description = "Path to LDIF file. This will cause an in-memory LDAP server to be started according to gitblit settings")
|
| | | public String ldapLdifFile;
|
| | |
|
| | |
| | | * The GitFilter is an AccessRestrictionFilter which ensures that Git client
|
| | | * requests for push, clone, or view restricted repositories are authenticated
|
| | | * and authorized.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitFilter extends AccessRestrictionFilter {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Extract the repository name from the url.
|
| | | * |
| | | *
|
| | | * @param cloneUrl
|
| | | * @return repository name
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Extract the repository name from the url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return repository name
|
| | | */
|
| | |
| | | /**
|
| | | * Analyze the url and returns the action of the request. Return values are
|
| | | * either "/git-receive-pack" or "/git-upload-pack".
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @return action of the request
|
| | | */
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Determine if a non-existing repository can be created using this filter.
|
| | | * |
| | | *
|
| | | * @return true if the server allows repository creation on-push
|
| | | */
|
| | | @Override
|
| | | protected boolean isCreationAllowed() {
|
| | | return GitBlit.getBoolean(Keys.git.allowCreateOnPush, true);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Determine if the repository can receive pushes.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if the action may be performed
|
| | |
| | |
|
| | | /**
|
| | | * Determine if the repository requires authentication.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if authentication required
|
| | |
| | | protected boolean requiresAuthentication(RepositoryModel repository, String action) {
|
| | | if (gitUploadPack.equals(action)) {
|
| | | // send to client
|
| | | return repository.accessRestriction.atLeast(AccessRestrictionType.CLONE); |
| | | return repository.accessRestriction.atLeast(AccessRestrictionType.CLONE);
|
| | | } else if (gitReceivePack.equals(action)) {
|
| | | // receive from client
|
| | | return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);
|
| | |
| | | /**
|
| | | * Determine if the user can access the repository and perform the specified
|
| | | * action.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param user
|
| | | * @param action
|
| | |
| | | if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
|
| | | // Git Servlet disabled
|
| | | return false;
|
| | | } |
| | | }
|
| | | if (action.equals(gitReceivePack)) {
|
| | | // Push request
|
| | | if (user.canPush(repository)) {
|
| | |
| | | }
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * An authenticated user with the CREATE role can create a repository on
|
| | | * push.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | * @param repository
|
| | | * @param action
|
| | |
| | | if (repository.contains("/../")) {
|
| | | logger.error(MessageFormat.format("Illegal relative path in repository name! {0}", repository));
|
| | | return null;
|
| | | } |
| | | }
|
| | |
|
| | | // confirm valid characters in repository name
|
| | | Character c = StringUtils.findInvalidCharacter(repository);
|
| | |
| | | logger.warn(MessageFormat.format("{0} is not permitted to create repository {1} ON-PUSH!", user.username, repository));
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // repository could not be created or action was not a push
|
| | | return null;
|
| | | }
|
| | |
| | | /**
|
| | | * Special SSL context factory that configures Gitblit GO and replaces the
|
| | | * primary trustmanager with a GitblitTrustManager.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class GitblitSslContextFactory extends SslContextFactory {
|
| | |
| | | private static final Logger logger = LoggerFactory.getLogger(GitblitSslContextFactory.class);
|
| | |
|
| | | private final File caRevocationList;
|
| | | |
| | |
|
| | | public GitblitSslContextFactory(String certAlias, File keyStore, File clientTrustStore,
|
| | | String storePassword, File caRevocationList) {
|
| | | super(keyStore.getAbsolutePath());
|
| | | |
| | |
|
| | | this.caRevocationList = caRevocationList;
|
| | |
|
| | | // disable renegotiation unless this is a patched JVM
|
| | |
| | | logger.info(" allowing SSL renegotiation on Java " + v);
|
| | | setAllowRenegotiate(allowRenegotiation);
|
| | | }
|
| | | |
| | | |
| | |
|
| | |
|
| | | if (!StringUtils.isEmpty(certAlias)) {
|
| | | logger.info(" certificate alias = " + certAlias);
|
| | | setCertAlias(certAlias);
|
| | |
| | | setKeyStorePassword(storePassword);
|
| | | setTrustStore(clientTrustStore.getAbsolutePath());
|
| | | setTrustStorePassword(storePassword);
|
| | | |
| | |
|
| | | logger.info(" keyStorePath = " + keyStore.getAbsolutePath());
|
| | | logger.info(" trustStorePath = " + clientTrustStore.getAbsolutePath());
|
| | | logger.info(" crlPath = " + caRevocationList.getAbsolutePath());
|
| | |
| | | import org.slf4j.LoggerFactory;
|
| | |
|
| | | /**
|
| | | * GitblitTrustManager is a wrapper trust manager that hot-reloads a local file |
| | | * GitblitTrustManager is a wrapper trust manager that hot-reloads a local file
|
| | | * CRL and enforces client certificate revocations. The GitblitTrustManager
|
| | | * also implements fuzzy revocation enforcement in case of issuer mismatch BUT
|
| | | * serial number match. These rejecions are specially noted in the log.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class GitblitTrustManager implements X509TrustManager {
|
| | | |
| | |
|
| | | private static final Logger logger = LoggerFactory.getLogger(GitblitTrustManager.class);
|
| | | |
| | |
|
| | | private final X509TrustManager delegate;
|
| | | private final File caRevocationList;
|
| | | |
| | |
|
| | | private final AtomicLong lastModified = new AtomicLong(0);
|
| | | private volatile X509CRL crl;
|
| | |
|
| | |
| | | public X509Certificate[] getAcceptedIssuers() {
|
| | | return delegate.getAcceptedIssuers();
|
| | | }
|
| | | |
| | |
|
| | | protected boolean isRevoked(X509Certificate cert) {
|
| | | if (!caRevocationList.exists()) {
|
| | | return false;
|
| | |
| | | // exact cert is revoked
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | X509CRLEntry entry = crl.getRevokedCertificate(cert.getSerialNumber());
|
| | | if (entry != null) {
|
| | | logger.warn("Certificate issuer does not match CRL issuer, but serial number has been revoked!");
|
| | |
| | | logger.warn(" crl issuer = " + crl.getIssuerX500Principal());
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | protected synchronized void read() {
|
| | | if (lastModified.get() == caRevocationList.lastModified()) {
|
| | | return;
|
| | |
| | | |
| | | /** |
| | | * Implementation of a user service using an Apache htpasswd file for authentication. |
| | | * |
| | | * |
| | | * This user service implement custom authentication using entries in a file created |
| | | * by the 'htpasswd' program of an Apache web server. All possible output |
| | | * options of the 'htpasswd' program version 2.2 are supported: |
| | |
| | | * glibc crypt() (not on Windows and NetWare), |
| | | * Apache MD5 (apr1), |
| | | * unsalted SHA-1. |
| | | * |
| | | * |
| | | * Configuration options: |
| | | * realm.htpasswd.backingUserService - Specify the backing user service that is used |
| | | * to keep the user data other than the password. |
| | |
| | | * realm.htpasswd.overrideLocalAuthentication - Specify if local accounts are overwritten |
| | | * when authentication matches for an |
| | | * external account. |
| | | * |
| | | * |
| | | * @author Florian Zschocke |
| | | * |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Setup the user service. |
| | | * |
| | | * |
| | | * The HtpasswdUserService extends the GitblitUserService and is thus |
| | | * backed by the available user services provided by the GitblitUserService. |
| | | * In addition the setup tries to read and parse the htpasswd file to be used |
| | | * for authentication. |
| | | * |
| | | * |
| | | * @param settings |
| | | * @since 0.7.0 |
| | | */ |
| | |
| | | |
| | | /** |
| | | * Determine if the account is to be treated as a local account. |
| | | * |
| | | * |
| | | * This influences authentication. A local account will be authenticated |
| | | * by the backing user service while an external account will be handled |
| | | * by the backing user service while an external account will be handled |
| | | * by this user service. |
| | | * <br/> |
| | | * The decision also depends on the setting of the key |
| | |
| | | * If the key is set to false, then it is determined if the account is local |
| | | * according to the logic of the GitblitUserService. |
| | | */ |
| | | protected boolean isLocalAccount(String username) |
| | | @Override |
| | | protected boolean isLocalAccount(String username) |
| | | { |
| | | if ( settings.getBoolean(KEY_OVERRIDE_LOCALAUTH, DEFAULT_OVERRIDE_LOCALAUTH) ) { |
| | | read(); |
| | |
| | | * |
| | | * @return AccountType.HTPASSWD |
| | | */ |
| | | protected AccountType getAccountType() |
| | | @Override |
| | | protected AccountType getAccountType() |
| | | { |
| | | return AccountType.HTPASSWD; |
| | | } |
| | |
| | |
|
| | | /**
|
| | | * Base class for stored settings implementations.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class IStoredSettings {
|
| | |
|
| | |
| | | /**
|
| | | * Returns the list of keys whose name starts with the specified prefix. If
|
| | | * the prefix is null or empty, all key names are returned.
|
| | | * |
| | | *
|
| | | * @param startingWith
|
| | | * @return list of keys
|
| | | */
|
| | |
| | | * Returns the boolean value for the specified key. If the key does not
|
| | | * exist or the value for the key can not be interpreted as a boolean, the
|
| | | * defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | |
| | | * Returns the integer value for the specified key. If the key does not
|
| | | * exist or the value for the key can not be interpreted as an integer, the
|
| | | * defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | |
| | | * Returns the long value for the specified key. If the key does not
|
| | | * exist or the value for the key can not be interpreted as an long, the
|
| | | * defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | |
| | | }
|
| | | return defaultValue;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns an int filesize from a string value such as 50m or 50mb
|
| | | * @param name
|
| | |
| | | }
|
| | | return com.gitblit.utils.FileUtils.convertSizeToInt(val, defaultValue);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns an long filesize from a string value such as 50m or 50mb
|
| | | * @param n
|
| | |
| | | * Returns the char value for the specified key. If the key does not exist
|
| | | * or the value for the key can not be interpreted as a char, the
|
| | | * defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | |
| | | * Returns the string value for the specified key. If the key does not exist
|
| | | * or the value for the key can not be interpreted as a string, the
|
| | | * defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | |
| | | }
|
| | | return defaultValue;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the string value for the specified key. If the key does not
|
| | | * exist an exception is thrown.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @return key value
|
| | | */
|
| | |
| | | if (value != null) {
|
| | | return value.trim();
|
| | | }
|
| | | } |
| | | }
|
| | | throw new RuntimeException("Property (" + name + ") does not exist");
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns a list of space-separated strings from the specified key.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return list of strings
|
| | | */
|
| | |
| | | /**
|
| | | * Returns a list of strings from the specified key using the specified
|
| | | * string separator.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @param separator
|
| | | * @return list of strings
|
| | |
| | | }
|
| | | return strings;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a list of space-separated integers from the specified key.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return list of strings
|
| | | */
|
| | |
| | | /**
|
| | | * Returns a list of integers from the specified key using the specified
|
| | | * string separator.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @param separator
|
| | | * @return list of integers
|
| | |
| | | }
|
| | | return ints;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a map of strings from the specified key.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return map of string, string
|
| | | */
|
| | |
| | | for (String string : getStrings(name)) {
|
| | | String[] kvp = string.split("=", 2);
|
| | | String key = kvp[0];
|
| | | String value = kvp[1]; |
| | | String value = kvp[1];
|
| | | map.put(key, value);
|
| | | }
|
| | | return map;
|
| | |
| | |
|
| | | /**
|
| | | * Override the specified key with the specified value.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @param value
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Override the specified key with the specified value.
|
| | | * |
| | | *
|
| | | * @param key
|
| | | * @param value
|
| | | */
|
| | |
| | | /**
|
| | | * Updates the values for the specified keys and persists the entire
|
| | | * configuration file.
|
| | | * |
| | | *
|
| | | * @param map
|
| | | * of key, value pairs
|
| | | * @return true if successful
|
| | |
| | | /**
|
| | | * Implementations of IUserService control all aspects of UserModel objects and
|
| | | * user authentication.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public interface IUserService {
|
| | |
|
| | |
| | | * Setup the user service. This method allows custom implementations to
|
| | | * retrieve settings from gitblit.properties or the web.xml file without
|
| | | * relying on the GitBlit static singleton.
|
| | | * |
| | | *
|
| | | * @param settings
|
| | | * @since 0.7.0
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Does the user service support changes to credentials?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */ |
| | | */
|
| | | boolean supportsCredentialChanges();
|
| | |
|
| | | /**
|
| | | * Does the user service support changes to user display name?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */ |
| | | */
|
| | | boolean supportsDisplayNameChanges();
|
| | |
|
| | | /**
|
| | | * Does the user service support changes to user email address?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */ |
| | | */
|
| | | boolean supportsEmailAddressChanges();
|
| | | |
| | |
|
| | | /**
|
| | | * Does the user service support changes to team memberships?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */ |
| | | */
|
| | | boolean supportsTeamMembershipChanges();
|
| | | |
| | |
|
| | | /**
|
| | | * Does the user service support cookie authentication?
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | */
|
| | | boolean supportsCookies();
|
| | |
|
| | | /**
|
| | | * Returns the cookie value for the specified user.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return cookie value
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Authenticate a user based on their cookie.
|
| | | * |
| | | *
|
| | | * @param cookie
|
| | | * @return a user object or null
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Authenticate a user based on a username and password.
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @param password
|
| | | * @return a user object or null
|
| | |
| | |
|
| | | /**
|
| | | * Logout a user.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | */
|
| | | void logout(UserModel user);
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieve the user object for the specified username.
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @return a user object or null
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Updates/writes a complete user object.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if update is successful
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Updates/writes all specified user objects.
|
| | | * |
| | | *
|
| | | * @param models a list of user models
|
| | | * @return true if update is successful
|
| | | * @since 1.2.0
|
| | | */
|
| | | boolean updateUserModels(Collection<UserModel> models);
|
| | | |
| | |
|
| | | /**
|
| | | * Adds/updates a user object keyed by username. This method allows for
|
| | | * renaming a user.
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * the old username
|
| | | * @param model
|
| | |
| | |
|
| | | /**
|
| | | * Deletes the user object from the user service.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if successful
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Delete the user object with the specified username
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @return true if successful
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the list of all users available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all usernames
|
| | | */
|
| | | List<String> getAllUsernames();
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of all users available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all users
|
| | | * @since 0.8.0
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the list of all teams available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all teams
|
| | | * @since 0.8.0
|
| | | */ |
| | | */
|
| | | List<String> getAllTeamNames();
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of all teams available to the login service.
|
| | | * |
| | | *
|
| | | * @return list of all teams
|
| | | * @since 0.8.0
|
| | | */ |
| | | */
|
| | | List<TeamModel> getAllTeams();
|
| | | |
| | |
|
| | | /**
|
| | | * 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
|
| | | * @since 0.8.0
|
| | | */ |
| | | */
|
| | | List<String> getTeamnamesForRepositoryRole(String role);
|
| | |
|
| | | /**
|
| | | * 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
|
| | |
| | | */
|
| | | @Deprecated
|
| | | boolean setTeamnamesForRepositoryRole(String role, List<String> teamnames);
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieve the team object for the specified team name.
|
| | | * |
| | | *
|
| | | * @param teamname
|
| | | * @return a team object or null
|
| | | * @since 0.8.0
|
| | | */ |
| | | */
|
| | | TeamModel getTeamModel(String teamname);
|
| | |
|
| | | /**
|
| | | * Updates/writes a complete team object.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if update is successful
|
| | | * @since 0.8.0
|
| | | */ |
| | | */
|
| | | boolean updateTeamModel(TeamModel model);
|
| | |
|
| | | /**
|
| | | * Updates/writes all specified team objects.
|
| | | * |
| | | *
|
| | | * @param models a list of team models
|
| | | * @return true if update is successful
|
| | | * @since 1.2.0
|
| | | */ |
| | | */
|
| | | boolean updateTeamModels(Collection<TeamModel> models);
|
| | | |
| | |
|
| | | /**
|
| | | * 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
|
| | |
| | |
|
| | | /**
|
| | | * Deletes the team object from the user service.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | |
| | |
|
| | | /**
|
| | | * Delete the team object with the specified teamname
|
| | | * |
| | | *
|
| | | * @param teamname
|
| | | * @return true if successful
|
| | | * @since 0.8.0
|
| | | */ |
| | | */
|
| | | boolean deleteTeam(String teamname);
|
| | |
|
| | | /**
|
| | | * 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
|
| | |
| | | /**
|
| | | * Sets the list of all uses who are allowed to bypass the access
|
| | | * restriction placed on the specified repository.
|
| | | * |
| | | *
|
| | | * @param role
|
| | | * the repository name
|
| | | * @param usernames
|
| | |
| | |
|
| | | /**
|
| | | * Renames a repository role.
|
| | | * |
| | | *
|
| | | * @param oldRole
|
| | | * @param newRole
|
| | | * @return true if successful
|
| | |
| | |
|
| | | /**
|
| | | * Removes a repository role from all users.
|
| | | * |
| | | *
|
| | | * @param role
|
| | | * @return true if successful
|
| | | */
|
| | |
| | | * @See java.lang.Object.toString();
|
| | | * @return string representation of the login service
|
| | | */
|
| | | @Override
|
| | | String toString();
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Servlet class for interpreting json requests.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class JsonServlet extends HttpServlet {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | protected final int forbiddenCode = HttpServletResponse.SC_FORBIDDEN;
|
| | | |
| | |
|
| | | protected final int notAllowedCode = HttpServletResponse.SC_METHOD_NOT_ALLOWED;
|
| | |
|
| | | protected final int failureCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
| | | |
| | |
|
| | | protected final Logger logger;
|
| | |
|
| | | public JsonServlet() {
|
| | |
| | |
|
| | | /**
|
| | | * Processes an gson request.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @param response
|
| | | * @throws javax.servlet.ServletException
|
| | |
| | | * folders and then calls the application main. Using this technique we do not |
| | | * have to specify a classpath and we can dynamically add jars to the |
| | | * distribution. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | * |
| | | */ |
| | | public class Launcher { |
| | | |
| | |
| | | |
| | | /** |
| | | * Adds a file to the classpath |
| | | * |
| | | * |
| | | * @param f |
| | | * the file to be added |
| | | * @throws IOException |
| | |
| | |
|
| | | /**
|
| | | * Implementation of an LDAP user service.
|
| | | * |
| | | *
|
| | | * @author John Crygier
|
| | | */
|
| | | public class LdapUserService extends GitblitUserService {
|
| | |
| | |
|
| | | private IStoredSettings settings;
|
| | | private AtomicLong lastLdapUserSync = new AtomicLong(0L);
|
| | | |
| | |
|
| | | public LdapUserService() {
|
| | | super();
|
| | | }
|
| | |
| | | throw new IllegalArgumentException(Keys.realm.ldap.ldapCachePeriod + " must have format '<long> <TimeUnit>' where <TimeUnit> is one of 'MILLISECONDS', 'SECONDS', 'MINUTES', 'HOURS', 'DAYS'");
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public void setup(IStoredSettings settings) {
|
| | | this.settings = settings;
|
| | | String file = settings.getString(Keys.realm.ldap.backingUserService, "${baseFolder}/users.conf");
|
| | | File realmFile = GitBlit.getFileOrFolder(file);
|
| | | |
| | |
|
| | | serviceImpl = createUserService(realmFile);
|
| | | logger.info("LDAP User Service backed by " + serviceImpl.toString());
|
| | | |
| | |
|
| | | synchronizeLdapUsers();
|
| | | }
|
| | | |
| | |
|
| | | protected synchronized void synchronizeLdapUsers() {
|
| | | final boolean enabled = settings.getBoolean(Keys.realm.ldap.synchronizeUsers.enable, false);
|
| | | if (enabled) {
|
| | |
| | | updateTeamModels(userTeams.values());
|
| | | }
|
| | | }
|
| | | lastLdapUserSync.set(System.currentTimeMillis()); |
| | | lastLdapUserSync.set(System.currentTimeMillis());
|
| | | } finally {
|
| | | ldapConnection.close();
|
| | | }
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private LDAPConnection getLdapConnection() {
|
| | | try {
|
| | | URI ldapUrl = new URI(settings.getRequiredString(Keys.realm.ldap.server));
|
| | | String bindUserName = settings.getString(Keys.realm.ldap.username, "");
|
| | | String bindPassword = settings.getString(Keys.realm.ldap.password, "");
|
| | | int ldapPort = ldapUrl.getPort();
|
| | | |
| | |
|
| | | if (ldapUrl.getScheme().equalsIgnoreCase("ldaps")) { // SSL
|
| | | if (ldapPort == -1) // Default Port
|
| | | ldapPort = 636;
|
| | | |
| | |
|
| | | LDAPConnection conn;
|
| | | SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
|
| | | if (StringUtils.isEmpty(bindUserName) && StringUtils.isEmpty(bindPassword)) {
|
| | |
| | |
|
| | | LDAPConnection conn;
|
| | | if (StringUtils.isEmpty(bindUserName) && StringUtils.isEmpty(bindPassword)) {
|
| | | conn = new LDAPConnection(ldapUrl.getHost(), ldapPort); |
| | | conn = new LDAPConnection(ldapUrl.getHost(), ldapPort);
|
| | | } else {
|
| | | conn = new LDAPConnection(ldapUrl.getHost(), ldapPort, bindUserName, bindPassword); |
| | | conn = new LDAPConnection(ldapUrl.getHost(), ldapPort, bindUserName, bindPassword);
|
| | | }
|
| | |
|
| | | if (ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) {
|
| | |
| | | } catch (LDAPException e) {
|
| | | logger.error("Error Connecting to LDAP", e);
|
| | | }
|
| | | |
| | |
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Credentials are defined in the LDAP server and can not be manipulated
|
| | | * from Gitblit.
|
| | |
| | | public boolean supportsCredentialChanges() {
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * If no displayName pattern is defined then Gitblit can manage the display name.
|
| | | *
|
| | |
| | | public boolean supportsDisplayNameChanges() {
|
| | | return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.displayName, ""));
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * If no email pattern is defined then Gitblit can manage the email address.
|
| | | *
|
| | |
| | | return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.email, ""));
|
| | | }
|
| | |
|
| | | |
| | |
|
| | | /**
|
| | | * If the LDAP server will maintain team memberships then LdapUserService
|
| | | * will not allow team membership changes. In this scenario all team
|
| | | * changes must be made on the LDAP server by the LDAP administrator.
|
| | | * |
| | | *
|
| | | * @return true or false
|
| | | * @since 1.0.0
|
| | | */ |
| | | */
|
| | | @Override
|
| | | public boolean supportsTeamMembershipChanges() {
|
| | | return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected AccountType getAccountType() {
|
| | | return AccountType.LDAP;
|
| | |
| | | // local account, bypass LDAP authentication
|
| | | return super.authenticate(username, password);
|
| | | }
|
| | | |
| | |
|
| | | String simpleUsername = getSimpleUsername(username);
|
| | | |
| | |
|
| | | LDAPConnection ldapConnection = getLdapConnection();
|
| | | if (ldapConnection != null) {
|
| | | try {
|
| | |
| | | updateTeamModel(userTeam);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | return user;
|
| | | }
|
| | | }
|
| | |
| | | ldapConnection.close();
|
| | | }
|
| | | }
|
| | | return null; |
| | | return null;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Set the admin attribute from team memberships retrieved from LDAP.
|
| | | * If we are not storing teams in LDAP and/or we have not defined any
|
| | | * administrator teams, then do not change the admin flag.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | */
|
| | | private void setAdminAttribute(UserModel user) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private void setUserAttributes(UserModel user, SearchResultEntry userEntry) {
|
| | | // Is this user an admin?
|
| | | setAdminAttribute(user);
|
| | | |
| | |
|
| | | // Don't want visibility into the real password, make up a dummy
|
| | | user.password = Constants.EXTERNAL_ACCOUNT;
|
| | | user.accountType = getAccountType();
|
| | | |
| | |
|
| | | // Get full name Attribute
|
| | | String displayName = settings.getString(Keys.realm.ldap.displayName, ""); |
| | | String displayName = settings.getString(Keys.realm.ldap.displayName, "");
|
| | | if (!StringUtils.isEmpty(displayName)) {
|
| | | // Replace embedded ${} with attributes
|
| | | if (displayName.contains("${")) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // Get email address Attribute
|
| | | String email = settings.getString(Keys.realm.ldap.email, "");
|
| | | if (!StringUtils.isEmpty(email)) {
|
| | |
| | |
|
| | | private void getTeamsFromLdap(LDAPConnection ldapConnection, String simpleUsername, SearchResultEntry loggingInUser, UserModel user) {
|
| | | String loggingInUserDN = loggingInUser.getDN();
|
| | | |
| | |
|
| | | user.teams.clear(); // Clear the users team memberships - we're going to get them from LDAP
|
| | | String groupBase = settings.getString(Keys.realm.ldap.groupBase, "");
|
| | | String groupMemberPattern = settings.getString(Keys.realm.ldap.groupMemberPattern, "(&(objectClass=group)(member=${dn}))");
|
| | | |
| | |
|
| | | groupMemberPattern = StringUtils.replace(groupMemberPattern, "${dn}", escapeLDAPSearchFilter(loggingInUserDN));
|
| | | groupMemberPattern = StringUtils.replace(groupMemberPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
|
| | | |
| | |
|
| | | // Fill in attributes into groupMemberPattern
|
| | | for (Attribute userAttribute : loggingInUser.getAttributes())
|
| | | groupMemberPattern = StringUtils.replace(groupMemberPattern, "${" + userAttribute.getName() + "}", escapeLDAPSearchFilter(userAttribute.getValue()));
|
| | | |
| | |
|
| | | SearchResult teamMembershipResult = doSearch(ldapConnection, groupBase, groupMemberPattern);
|
| | | if (teamMembershipResult != null && teamMembershipResult.getEntryCount() > 0) {
|
| | | for (int i = 0; i < teamMembershipResult.getEntryCount(); i++) {
|
| | | SearchResultEntry teamEntry = teamMembershipResult.getSearchEntries().get(i);
|
| | | String teamName = teamEntry.getAttribute("cn").getValue();
|
| | | |
| | |
|
| | | TeamModel teamModel = getTeamModel(teamName);
|
| | | if (teamModel == null)
|
| | | teamModel = createTeamFromLdap(teamEntry);
|
| | | |
| | |
|
| | | user.teams.add(teamModel);
|
| | | teamModel.addUser(user.getName());
|
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private TeamModel createTeamFromLdap(SearchResultEntry teamEntry) {
|
| | | TeamModel answer = new TeamModel(teamEntry.getAttributeValue("cn"));
|
| | | // potentially retrieve other attributes here in the future
|
| | | |
| | | return answer; |
| | |
|
| | | return answer;
|
| | | }
|
| | |
|
| | | private SearchResult doSearch(LDAPConnection ldapConnection, String base, String filter) {
|
| | |
| | | return ldapConnection.search(base, SearchScope.SUB, filter);
|
| | | } catch (LDAPSearchException e) {
|
| | | logger.error("Problem Searching LDAP", e);
|
| | | |
| | |
|
| | | return null;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private boolean isAuthenticated(LDAPConnection ldapConnection, String userDn, String password) {
|
| | | try {
|
| | | // Binding will stop any LDAP-Injection Attacks since the searched-for user needs to bind to that DN
|
| | |
| | | synchronizeLdapUsers();
|
| | | return super.getAllUsers();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a simple username without any domain prefixes.
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @return a simple username
|
| | | */
|
| | |
| | | if (lastSlash > -1) {
|
| | | username = username.substring(lastSlash + 1);
|
| | | }
|
| | | |
| | |
|
| | | return username;
|
| | | }
|
| | | |
| | |
|
| | | // From: https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java
|
| | | public static final String escapeLDAPSearchFilter(String filter) {
|
| | | StringBuilder sb = new StringBuilder();
|
| | |
| | | case ')':
|
| | | sb.append("\\29");
|
| | | break;
|
| | | case '\u0000': |
| | | sb.append("\\00"); |
| | | case '\u0000':
|
| | | sb.append("\\00");
|
| | | break;
|
| | | default:
|
| | | sb.append(curChar);
|
| | |
| | |
|
| | | /**
|
| | | * Handles requests for logo.png
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class LogoServlet extends HttpServlet {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | private static final long lastModified = System.currentTimeMillis();
|
| | |
|
| | | public LogoServlet() {
|
| | | super();
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected long getLastModified(HttpServletRequest req) {
|
| | | File file = GitBlit.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");
|
| | |
| | | return lastModified;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
| | | throws ServletException, IOException {
|
| | |
| | | // default logo
|
| | | response.setDateHeader("Last-Modified", lastModified);
|
| | | is = getClass().getResourceAsStream("/logo.png");
|
| | | } |
| | | }
|
| | | if (contentType == null) {
|
| | | contentType = "image/png";
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * The Lucene executor handles indexing and searching repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class LuceneExecutor implements Runnable {
|
| | | |
| | | |
| | |
|
| | |
|
| | | private static final int INDEX_VERSION = 5;
|
| | |
|
| | | private static final String FIELD_OBJECT_TYPE = "type";
|
| | |
| | | private static final String CONF_VERSION = "version";
|
| | | private static final String CONF_ALIAS = "aliases";
|
| | | private static final String CONF_BRANCH = "branches";
|
| | | |
| | |
|
| | | private static final Version LUCENE_VERSION = Version.LUCENE_35;
|
| | | |
| | |
|
| | | private final Logger logger = LoggerFactory.getLogger(LuceneExecutor.class);
|
| | | |
| | |
|
| | | private final IStoredSettings storedSettings;
|
| | | private final File repositoriesFolder;
|
| | | |
| | |
|
| | | private final Map<String, IndexSearcher> searchers = new ConcurrentHashMap<String, IndexSearcher>();
|
| | | private final Map<String, IndexWriter> writers = new ConcurrentHashMap<String, IndexWriter>();
|
| | | |
| | |
|
| | | private final String luceneIgnoreExtensions = "7z arc arj bin bmp dll doc docx exe gif gz jar jpg lib lzh odg odf odt pdf ppt png so swf xcf xls xlsx zip";
|
| | | private Set<String> excludedExtensions;
|
| | | |
| | |
|
| | | public LuceneExecutor(IStoredSettings settings, File repositoriesFolder) {
|
| | | this.storedSettings = settings;
|
| | | this.repositoriesFolder = repositoriesFolder;
|
| | |
| | | }
|
| | |
|
| | | /**
|
| | | * Run is executed by the Gitblit executor service. Because this is called |
| | | * Run is executed by the Gitblit executor service. Because this is called
|
| | | * by an executor service, calls will queue - i.e. there can never be
|
| | | * concurrent execution of repository index updates.
|
| | | */
|
| | |
| | | // busy collecting garbage, try again later
|
| | | return;
|
| | | }
|
| | | |
| | |
|
| | | for (String repositoryName: GitBlit.self().getRepositoryList()) {
|
| | | RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
|
| | | if (model.hasCommits && !ArrayUtils.isEmpty(model.indexedBranches)) {
|
| | |
| | | }
|
| | | continue;
|
| | | }
|
| | | index(model, repository); |
| | | index(model, repository);
|
| | | repository.close();
|
| | | System.gc();
|
| | | }
|
| | |
| | | /**
|
| | | * Synchronously indexes a repository. This may build a complete index of a
|
| | | * repository or it may update an existing index.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * the name of the repository
|
| | | * @param repository
|
| | |
| | | logger.error(MessageFormat.format("Lucene indexing failure for {0}", model.name), t);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Close the writer/searcher objects for a repository.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | */
|
| | | public synchronized void close(String repositoryName) {
|
| | |
| | | } catch (Exception e) {
|
| | | logger.error("Failed to close index searcher for " + repositoryName, e);
|
| | | }
|
| | | |
| | |
|
| | | try {
|
| | | IndexWriter writer = writers.remove(repositoryName);
|
| | | if (writer != null) {
|
| | |
| | | }
|
| | | } catch (Exception e) {
|
| | | logger.error("Failed to close index writer for " + repositoryName, e);
|
| | | } |
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * Close all Lucene indexers.
|
| | | * |
| | | *
|
| | | */
|
| | | public synchronized void close() {
|
| | | // close all writers
|
| | |
| | | searchers.clear();
|
| | | }
|
| | |
|
| | | |
| | |
|
| | | /**
|
| | | * Deletes the Lucene index for the specified repository.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @return true, if successful
|
| | | */
|
| | |
| | | throw new RuntimeException(e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the author for the commit, if this information is available.
|
| | | * |
| | | *
|
| | | * @param commit
|
| | | * @return an author or unknown
|
| | | */
|
| | |
| | | if (StringUtils.isEmpty(name)) {
|
| | | name = commit.getAuthorIdent().getEmailAddress();
|
| | | }
|
| | | } catch (NullPointerException n) { |
| | | } catch (NullPointerException n) {
|
| | | }
|
| | | return name;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the committer for the commit, if this information is available.
|
| | | * |
| | | *
|
| | | * @param commit
|
| | | * @return an committer or unknown
|
| | | */
|
| | |
| | | if (StringUtils.isEmpty(name)) {
|
| | | name = commit.getCommitterIdent().getEmailAddress();
|
| | | }
|
| | | } catch (NullPointerException n) { |
| | | } catch (NullPointerException n) {
|
| | | }
|
| | | return name;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Get the tree associated with the given commit.
|
| | | *
|
| | |
| | |
|
| | | /**
|
| | | * Construct a keyname from the branch.
|
| | | * |
| | | *
|
| | | * @param branchName
|
| | | * @return a keyname appropriate for the Git config file format
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the Lucene configuration for the specified repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return a config object
|
| | | */
|
| | |
| | | * Reads the Lucene config file for the repository to check the index
|
| | | * version. If the index version is different, then rebuild the repository
|
| | | * index.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return true of the on-disk index format is different than INDEX_VERSION
|
| | | */
|
| | |
| | | /**
|
| | | * This completely indexes the repository and will destroy any existing
|
| | | * index.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @param repository
|
| | | * @return IndexResult
|
| | | */
|
| | | public IndexResult reindex(RepositoryModel model, Repository repository) {
|
| | | IndexResult result = new IndexResult(); |
| | | IndexResult result = new IndexResult();
|
| | | if (!deleteIndex(model.name)) {
|
| | | return result;
|
| | | }
|
| | |
| | | }
|
| | | tags.get(tag.getReferencedObjectId().getName()).add(tag.displayName);
|
| | | }
|
| | | |
| | |
|
| | | ObjectReader reader = repository.newObjectReader();
|
| | |
|
| | | // get the local branches
|
| | | List<RefModel> branches = JGitUtils.getLocalBranches(repository, true, -1);
|
| | | |
| | |
|
| | | // sort them by most recently updated
|
| | | Collections.sort(branches, new Comparator<RefModel>() {
|
| | | @Override
|
| | |
| | | return ref2.getDate().compareTo(ref1.getDate());
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | // reorder default branch to first position
|
| | | RefModel defaultBranch = null;
|
| | | ObjectId defaultBranchId = JGitUtils.getDefaultBranch(repository);
|
| | |
| | | }
|
| | | branches.remove(defaultBranch);
|
| | | branches.add(0, defaultBranch);
|
| | | |
| | |
|
| | | // walk through each branch
|
| | | for (RefModel branch : branches) {
|
| | |
|
| | |
| | | // normal explicit branch check
|
| | | indexBranch = model.indexedBranches.contains(branch.getName());
|
| | | }
|
| | | |
| | |
|
| | | // if this branch is not specifically indexed then skip
|
| | | if (!indexBranch) {
|
| | | continue;
|
| | |
| | | // index the blob contents of the tree
|
| | | TreeWalk treeWalk = new TreeWalk(repository);
|
| | | treeWalk.addTree(tip.getTree());
|
| | | treeWalk.setRecursive(true); |
| | | |
| | | treeWalk.setRecursive(true);
|
| | |
|
| | | Map<String, ObjectId> paths = new TreeMap<String, ObjectId>();
|
| | | while (treeWalk.next()) {
|
| | | // ensure path is not in a submodule
|
| | | if (treeWalk.getFileMode(0) != FileMode.GITLINK) {
|
| | | paths.put(treeWalk.getPathString(), treeWalk.getObjectId(0));
|
| | | }
|
| | | } |
| | | }
|
| | |
|
| | | ByteArrayOutputStream os = new ByteArrayOutputStream();
|
| | | byte[] tmp = new byte[32767];
|
| | |
|
| | | RevWalk commitWalk = new RevWalk(reader);
|
| | | commitWalk.markStart(tip);
|
| | | |
| | |
|
| | | RevCommit commit;
|
| | | while ((paths.size() > 0) && (commit = commitWalk.next()) != null) {
|
| | | TreeWalk diffWalk = new TreeWalk(reader);
|
| | |
| | | if (!paths.containsKey(path)) {
|
| | | continue;
|
| | | }
|
| | | |
| | |
|
| | | // remove path from set
|
| | | ObjectId blobId = paths.remove(path);
|
| | | result.blobCount++;
|
| | | |
| | |
|
| | | // index the blob metadata
|
| | | String blobAuthor = getAuthor(commit);
|
| | | String blobCommitter = getCommitter(commit);
|
| | | String blobDate = DateTools.timeToString(commit.getCommitTime() * 1000L,
|
| | | Resolution.MINUTE);
|
| | | |
| | |
|
| | | Document doc = new Document();
|
| | | doc.add(new Field(FIELD_OBJECT_TYPE, SearchObjectType.blob.name(), Store.YES, Index.NOT_ANALYZED_NO_NORMS));
|
| | | doc.add(new Field(FIELD_BRANCH, branchName, Store.YES, Index.ANALYZED));
|
| | |
| | | doc.add(new Field(FIELD_PATH, path, Store.YES, Index.ANALYZED));
|
| | | doc.add(new Field(FIELD_DATE, blobDate, Store.YES, Index.NO));
|
| | | doc.add(new Field(FIELD_AUTHOR, blobAuthor, Store.YES, Index.ANALYZED));
|
| | | doc.add(new Field(FIELD_COMMITTER, blobCommitter, Store.YES, Index.ANALYZED)); |
| | | doc.add(new Field(FIELD_COMMITTER, blobCommitter, Store.YES, Index.ANALYZED));
|
| | |
|
| | | // determine extension to compare to the extension
|
| | | // blacklist
|
| | |
| | | }
|
| | |
|
| | | // index the blob content
|
| | | if (StringUtils.isEmpty(ext) || !excludedExtensions.contains(ext)) { |
| | | if (StringUtils.isEmpty(ext) || !excludedExtensions.contains(ext)) {
|
| | | ObjectLoader ldr = repository.open(blobId, Constants.OBJ_BLOB);
|
| | | InputStream in = ldr.openStream(); |
| | | InputStream in = ldr.openStream();
|
| | | int n;
|
| | | while ((n = in.read(tmp)) > 0) {
|
| | | os.write(tmp, 0, n);
|
| | | }
|
| | | in.close();
|
| | | byte[] content = os.toByteArray();
|
| | | String str = StringUtils.decodeString(content, encodings); |
| | | String str = StringUtils.decodeString(content, encodings);
|
| | | doc.add(new Field(FIELD_CONTENT, str, Store.YES, Index.ANALYZED));
|
| | | os.reset();
|
| | | } |
| | | |
| | | }
|
| | |
|
| | | // add the blob to the index
|
| | | writer.addDocument(doc);
|
| | | }
|
| | |
| | |
|
| | | // finished
|
| | | reader.release();
|
| | | |
| | |
|
| | | // commit all changes and reset the searcher
|
| | | config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);
|
| | | config.save();
|
| | |
| | | }
|
| | | return result;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Incrementally update the index with the specified commit for the
|
| | | * repository.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @param repository
|
| | | * @param branch
|
| | |
| | | * @param commit
|
| | | * @return true, if successful
|
| | | */
|
| | | private IndexResult index(String repositoryName, Repository repository, |
| | | private IndexResult index(String repositoryName, Repository repository,
|
| | | String branch, RevCommit commit) {
|
| | | IndexResult result = new IndexResult();
|
| | | try {
|
| | |
| | | }
|
| | | }
|
| | | writer.commit();
|
| | | |
| | |
|
| | | // get any annotated commit tags
|
| | | List<String> commitTags = new ArrayList<String>();
|
| | | for (RefModel ref : JGitUtils.getTags(repository, false, -1)) {
|
| | |
| | | commitTags.add(ref.displayName);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // create and write the Lucene document
|
| | | Document doc = createDocument(commit, commitTags);
|
| | | doc.add(new Field(FIELD_BRANCH, branch, Store.YES, Index.ANALYZED));
|
| | |
| | |
|
| | | /**
|
| | | * Delete a blob from the specified branch of the repository index.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @param branch
|
| | | * @param path
|
| | |
| | | public boolean deleteBlob(String repositoryName, String branch, String path) throws Exception {
|
| | | String pattern = MessageFormat.format("{0}:'{'0} AND {1}:\"'{'1'}'\" AND {2}:\"'{'2'}'\"", FIELD_OBJECT_TYPE, FIELD_BRANCH, FIELD_PATH);
|
| | | String q = MessageFormat.format(pattern, SearchObjectType.blob.name(), branch, path);
|
| | | |
| | |
|
| | | BooleanQuery query = new BooleanQuery();
|
| | | StandardAnalyzer analyzer = new StandardAnalyzer(LUCENE_VERSION);
|
| | | QueryParser qp = new QueryParser(LUCENE_VERSION, FIELD_SUMMARY, analyzer);
|
| | |
| | |
|
| | | IndexWriter writer = getIndexWriter(repositoryName);
|
| | | int numDocsBefore = writer.numDocs();
|
| | | writer.deleteDocuments(query); |
| | | writer.deleteDocuments(query);
|
| | | writer.commit();
|
| | | int numDocsAfter = writer.numDocs();
|
| | | if (numDocsBefore == numDocsAfter) {
|
| | |
| | |
|
| | | /**
|
| | | * Updates a repository index incrementally from the last indexed commits.
|
| | | * |
| | | *
|
| | | * @param model
|
| | | * @param repository
|
| | | * @return IndexResult
|
| | |
| | |
|
| | | // get the local branches
|
| | | List<RefModel> branches = JGitUtils.getLocalBranches(repository, true, -1);
|
| | | |
| | |
|
| | | // sort them by most recently updated
|
| | | Collections.sort(branches, new Comparator<RefModel>() {
|
| | | @Override
|
| | |
| | | return ref2.getDate().compareTo(ref1.getDate());
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | // reorder default branch to first position
|
| | | RefModel defaultBranch = null;
|
| | | ObjectId defaultBranchId = JGitUtils.getDefaultBranch(repository);
|
| | |
| | | }
|
| | | branches.remove(defaultBranch);
|
| | | branches.add(0, defaultBranch);
|
| | | |
| | |
|
| | | // walk through each branches
|
| | | for (RefModel branch : branches) {
|
| | | String branchName = branch.getName();
|
| | |
| | | // normal explicit branch check
|
| | | indexBranch = model.indexedBranches.contains(branch.getName());
|
| | | }
|
| | | |
| | |
|
| | | // if this branch is not specifically indexed then skip
|
| | | if (!indexBranch) {
|
| | | continue;
|
| | | }
|
| | | |
| | |
|
| | | // remove this branch from the deletedBranches set
|
| | | deletedBranches.remove(branchName);
|
| | | |
| | |
|
| | | // determine last commit
|
| | | String keyName = getBranchKey(branchName);
|
| | | String lastCommit = config.getString(CONF_BRANCH, null, keyName);
|
| | |
| | | if (revs.size() > 0) {
|
| | | result.branchCount += 1;
|
| | | }
|
| | | |
| | | // reverse the list of commits so we start with the first commit |
| | |
|
| | | // reverse the list of commits so we start with the first commit
|
| | | Collections.reverse(revs);
|
| | | for (RevCommit commit : revs) { |
| | | for (RevCommit commit : revs) {
|
| | | // index a commit
|
| | | result.add(index(model.name, repository, branchName, commit));
|
| | | }
|
| | |
| | | }
|
| | | return result;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Creates a Lucene document for a commit
|
| | | * |
| | | *
|
| | | * @param commit
|
| | | * @param tags
|
| | | * @return a Lucene document
|
| | |
| | |
|
| | | /**
|
| | | * Incrementally index an object for the repository.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @param doc
|
| | | * @return true, if successful
|
| | | */
|
| | | private boolean index(String repositoryName, Document doc) {
|
| | | try { |
| | | try {
|
| | | IndexWriter writer = getIndexWriter(repositoryName);
|
| | | writer.addDocument(doc);
|
| | | writer.commit();
|
| | |
| | | result.totalHits = totalHits;
|
| | | result.score = score;
|
| | | result.date = DateTools.stringToDate(doc.get(FIELD_DATE));
|
| | | result.summary = doc.get(FIELD_SUMMARY); |
| | | result.summary = doc.get(FIELD_SUMMARY);
|
| | | result.author = doc.get(FIELD_AUTHOR);
|
| | | result.committer = doc.get(FIELD_COMMITTER);
|
| | | result.type = SearchObjectType.fromName(doc.get(FIELD_OBJECT_TYPE));
|
| | |
| | |
|
| | | /**
|
| | | * Gets an index searcher for the repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return
|
| | | * @throws IOException
|
| | |
| | | /**
|
| | | * Gets an index writer for the repository. The index will be created if it
|
| | | * does not already exist or if forceCreate is specified.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return an IndexWriter
|
| | | * @throws IOException
|
| | | */
|
| | | private IndexWriter getIndexWriter(String repository) throws IOException {
|
| | | IndexWriter indexWriter = writers.get(repository); |
| | | IndexWriter indexWriter = writers.get(repository);
|
| | | File repositoryFolder = FileKey.resolve(new File(repositoriesFolder, repository), FS.DETECTED);
|
| | | File indexFolder = new File(repositoryFolder, LUCENE_DIR);
|
| | | Directory directory = FSDirectory.open(indexFolder); |
| | | Directory directory = FSDirectory.open(indexFolder);
|
| | |
|
| | | if (indexWriter == null) {
|
| | | if (!indexFolder.exists()) {
|
| | |
| | |
|
| | | /**
|
| | | * Searches the specified repositories for the given text or query
|
| | | * |
| | | *
|
| | | * @param text
|
| | | * if the text is null or empty, null is returned
|
| | | * @param page
|
| | |
| | | * a list of repositories to search. if no repositories are
|
| | | * specified null is returned.
|
| | | * @return a list of SearchResults in order from highest to the lowest score
|
| | | * |
| | | *
|
| | | */
|
| | | public List<SearchResult> search(String text, int page, int pageSize, List<String> repositories) {
|
| | | if (ArrayUtils.isEmpty(repositories)) {
|
| | |
| | | }
|
| | | return search(text, page, pageSize, repositories.toArray(new String[0]));
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Searches the specified repositories for the given text or query
|
| | | * |
| | | *
|
| | | * @param text
|
| | | * if the text is null or empty, null is returned
|
| | | * @param page
|
| | |
| | | * a list of repositories to search. if no repositories are
|
| | | * specified null is returned.
|
| | | * @return a list of SearchResults in order from highest to the lowest score
|
| | | * |
| | | *
|
| | | */
|
| | | public List<SearchResult> search(String text, int page, int pageSize, String... repositories) {
|
| | | if (StringUtils.isEmpty(text)) {
|
| | |
| | | qp = new QueryParser(LUCENE_VERSION, FIELD_CONTENT, analyzer);
|
| | | qp.setAllowLeadingWildcard(true);
|
| | | query.add(qp.parse(text), Occur.SHOULD);
|
| | | |
| | |
|
| | | IndexSearcher searcher;
|
| | | if (repositories.length == 1) {
|
| | | // single repository search
|
| | |
| | | MultiSourceReader reader = new MultiSourceReader(rdrs);
|
| | | searcher = new IndexSearcher(reader);
|
| | | }
|
| | | |
| | |
|
| | | Query rewrittenQuery = searcher.rewrite(query);
|
| | | logger.debug(rewrittenQuery.toString());
|
| | |
|
| | |
| | | int index = reader.getSourceIndex(docId);
|
| | | result.repository = repositories[index];
|
| | | }
|
| | | String content = doc.get(FIELD_CONTENT); |
| | | String content = doc.get(FIELD_CONTENT);
|
| | | result.fragment = getHighlightedFragment(analyzer, query, content, result);
|
| | | results.add(result);
|
| | | }
|
| | |
| | | }
|
| | | return new ArrayList<SearchResult>(results);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * |
| | | *
|
| | | * @param analyzer
|
| | | * @param query
|
| | | * @param content
|
| | |
| | | String content, SearchResult result) throws IOException, InvalidTokenOffsetsException {
|
| | | if (content == null) {
|
| | | content = "";
|
| | | } |
| | | }
|
| | |
|
| | | int fragmentLength = SearchObjectType.commit == result.type ? 512 : 150;
|
| | |
|
| | | QueryScorer scorer = new QueryScorer(query, "content");
|
| | | Fragmenter fragmenter = new SimpleSpanFragmenter(scorer, fragmentLength); |
| | | Fragmenter fragmenter = new SimpleSpanFragmenter(scorer, fragmentLength);
|
| | |
|
| | | // use an artificial delimiter for the token
|
| | | String termTag = "!!--[";
|
| | | String termTagEnd = "]--!!";
|
| | | SimpleHTMLFormatter formatter = new SimpleHTMLFormatter(termTag, termTagEnd);
|
| | | Highlighter highlighter = new Highlighter(formatter, scorer); |
| | | Highlighter highlighter = new Highlighter(formatter, scorer);
|
| | | highlighter.setTextFragmenter(fragmenter);
|
| | |
|
| | | String [] fragments = highlighter.getBestFragments(analyzer, "content", content, 3);
|
| | |
| | | }
|
| | | return "<pre class=\"text\">" + StringUtils.escapeForHtml(fragment, true) + "</pre>";
|
| | | }
|
| | | |
| | |
|
| | | // make sure we have unique fragments
|
| | | Set<String> uniqueFragments = new LinkedHashSet<String>();
|
| | | for (String fragment : fragments) {
|
| | | uniqueFragments.add(fragment);
|
| | | }
|
| | | fragments = uniqueFragments.toArray(new String[uniqueFragments.size()]);
|
| | | |
| | |
|
| | | StringBuilder sb = new StringBuilder();
|
| | | for (int i = 0, len = fragments.length; i < len; i++) {
|
| | | String fragment = fragments[i];
|
| | |
| | |
|
| | | // determine position of the raw fragment in the content
|
| | | int pos = content.indexOf(raw);
|
| | | |
| | |
|
| | | // restore complete first line of fragment
|
| | | int c = pos;
|
| | | while (c > 0) {
|
| | |
| | | // inject leading chunk of first fragment line
|
| | | fragment = content.substring(c + 1, pos) + fragment;
|
| | | }
|
| | | |
| | |
|
| | | if (SearchObjectType.blob == result.type) {
|
| | | // count lines as offset into the content for this fragment
|
| | | int line = Math.max(1, StringUtils.countLines(content.substring(0, pos)));
|
| | | |
| | |
|
| | | // create fragment tag with line number and language
|
| | | String lang = "";
|
| | | String ext = StringUtils.getFileExtension(result.path).toLowerCase();
|
| | |
| | | lang = " lang-" + ext;
|
| | | }
|
| | | tag = MessageFormat.format("<pre class=\"prettyprint linenums:{0,number,0}{1}\">", line, lang);
|
| | | |
| | |
|
| | | }
|
| | | |
| | |
|
| | | sb.append(tag);
|
| | |
|
| | | // replace the artificial delimiter with html tags
|
| | |
| | | }
|
| | | }
|
| | | return sb.toString();
|
| | | } |
| | | |
| | | }
|
| | |
|
| | | /**
|
| | | * Simple class to track the results of an index update. |
| | | * Simple class to track the results of an index update.
|
| | | */
|
| | | private class IndexResult {
|
| | | long startTime = System.currentTimeMillis();
|
| | |
| | | int branchCount;
|
| | | int commitCount;
|
| | | int blobCount;
|
| | | |
| | |
|
| | | void add(IndexResult result) {
|
| | | this.branchCount += result.branchCount;
|
| | | this.commitCount += result.commitCount;
|
| | | this.blobCount += result.blobCount;
|
| | | }
|
| | | |
| | |
|
| | | void success() {
|
| | | success = true;
|
| | | endTime = System.currentTimeMillis();
|
| | | }
|
| | | |
| | |
|
| | | float duration() {
|
| | | return (endTime - startTime)/1000f;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Custom subclass of MultiReader to identify the source index for a given
|
| | | * doc id. This would not be necessary of there was a public method to
|
| | | * obtain this information.
|
| | | * |
| | | *
|
| | | */
|
| | | private class MultiSourceReader extends MultiReader {
|
| | | |
| | |
|
| | | final Method method;
|
| | | |
| | |
|
| | | MultiSourceReader(IndexReader[] subReaders) {
|
| | | super(subReaders);
|
| | | Method m = null;
|
| | |
| | | }
|
| | | method = m;
|
| | | }
|
| | | |
| | |
|
| | | int getSourceIndex(int docId) {
|
| | | int index = -1;
|
| | | try {
|
| | |
| | |
|
| | | /**
|
| | | * The mail executor handles sending email messages asynchronously from queue.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class MailExecutor implements Runnable {
|
| | |
|
| | |
| | | if (!StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword)) {
|
| | | // SMTP requires authentication
|
| | | session = Session.getInstance(props, new Authenticator() {
|
| | | @Override
|
| | | protected PasswordAuthentication getPasswordAuthentication() {
|
| | | PasswordAuthentication passwordAuthentication = new PasswordAuthentication(
|
| | | mailUser, mailPassword);
|
| | |
| | |
|
| | | /**
|
| | | * Indicates if the mail executor can send emails.
|
| | | * |
| | | *
|
| | | * @return true if the mail executor is ready to send emails
|
| | | */
|
| | | public boolean isReady() {
|
| | |
| | |
|
| | | /**
|
| | | * Create a message.
|
| | | * |
| | | *
|
| | | * @param toAddresses
|
| | | * @return a message
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Create a message.
|
| | | * |
| | | *
|
| | | * @param toAddresses
|
| | | * @return a message
|
| | | */
|
| | |
| | | for (String address : toAddresses) {
|
| | | uniques.add(address.toLowerCase());
|
| | | }
|
| | | |
| | |
|
| | | Pattern validEmail = Pattern
|
| | | .compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$");
|
| | | List<InternetAddress> tos = new ArrayList<InternetAddress>();
|
| | |
| | | } catch (Throwable t) {
|
| | | }
|
| | | }
|
| | | } |
| | | }
|
| | | message.setRecipients(Message.RecipientType.BCC,
|
| | | tos.toArray(new InternetAddress[tos.size()]));
|
| | | message.setSentDate(new Date());
|
| | |
| | |
|
| | | /**
|
| | | * Returns the status of the mail queue.
|
| | | * |
| | | *
|
| | | * @return true, if the queue is empty
|
| | | */
|
| | | public boolean hasEmptyQueue() {
|
| | |
| | |
|
| | | /**
|
| | | * Queue's an email message to be sent.
|
| | | * |
| | | *
|
| | | * @param message
|
| | | * @return true if the message was queued
|
| | | */
|
| | |
| | | failures.add(message);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // push the failures back onto the queue for the next cycle
|
| | | queue.addAll(failures);
|
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public void sendNow(Message message) throws Exception {
|
| | | Transport.send(message);
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Implementation of a PAM user service for Linux/Unix/MacOSX.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class PAMUserService extends GitblitUserService {
|
| | |
| | | private final Logger logger = LoggerFactory.getLogger(PAMUserService.class);
|
| | |
|
| | | private IStoredSettings settings;
|
| | | |
| | |
|
| | | public PAMUserService() {
|
| | | super();
|
| | | }
|
| | |
| | |
|
| | | serviceImpl = createUserService(realmFile);
|
| | | logger.info("PAM User Service backed by " + serviceImpl.toString());
|
| | | |
| | |
|
| | | // Try to identify the passwd database
|
| | | String [] files = { "/etc/shadow", "/etc/master.passwd" };
|
| | | File passwdFile = null;
|
| | |
| | | logger.error("PAM User Service can not read passwd database {}! PAM authentications may fail!", passwdFile);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean supportsCredentialChanges() {
|
| | | return false;
|
| | |
| | | public boolean supportsTeamMembershipChanges() {
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected AccountType getAccountType() {
|
| | | return AccountType.PAM;
|
| | |
| | | // local account, bypass PAM authentication
|
| | | return super.authenticate(username, password);
|
| | | }
|
| | | |
| | |
|
| | | if (CLibrary.libc.getpwnam(username) == null) {
|
| | | logger.warn("Can not get PAM passwd for " + username);
|
| | | return null;
|
| | |
| | |
|
| | | // push the changes to the backing user service
|
| | | super.updateUserModel(user);
|
| | | |
| | |
|
| | | return user;
|
| | | }
|
| | | }
|
| | |
| | | /**
|
| | | * The PagesFilter is an AccessRestrictionFilter which ensures the gh-pages
|
| | | * requests for a view-restricted repository are authenticated and authorized.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class PagesFilter extends AccessRestrictionFilter {
|
| | |
|
| | | /**
|
| | | * Extract the repository name from the url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return repository name
|
| | | */
|
| | | @Override
|
| | | protected String extractRepositoryName(String url) { |
| | | protected String extractRepositoryName(String url) {
|
| | | // get the repository name from the url by finding a known url suffix
|
| | | String repository = ""; |
| | | String repository = "";
|
| | | Repository r = null;
|
| | | int offset = 0;
|
| | | while (r == null) {
|
| | |
| | | r = GitBlit.self().getRepository(repository, false);
|
| | | if (r == null) {
|
| | | // try again
|
| | | offset = slash + 1; |
| | | offset = slash + 1;
|
| | | } else {
|
| | | // close the repo
|
| | | r.close();
|
| | | } |
| | | }
|
| | | if (repository.equals(url)) {
|
| | | // either only repository in url or no repository found
|
| | | break;
|
| | |
| | |
|
| | | /**
|
| | | * Analyze the url and returns the action of the request.
|
| | | * |
| | | *
|
| | | * @param cloneUrl
|
| | | * @return action of the request
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Determine if a non-existing repository can be created using this filter.
|
| | | * |
| | | *
|
| | | * @return true if the filter allows repository creation
|
| | | */
|
| | | @Override
|
| | |
| | |
|
| | | /**
|
| | | * Determine if the action may be executed on the repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if the action may be performed
|
| | |
| | | protected boolean isActionAllowed(RepositoryModel repository, String action) {
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Determine if the repository requires authentication.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param action
|
| | | * @return true if authentication required
|
| | |
| | | /**
|
| | | * Determine if the user can access the repository and perform the specified
|
| | | * action.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param user
|
| | | * @param action
|
| | | * @return true if user may execute the action on the repository
|
| | | */
|
| | | @Override
|
| | | protected boolean canAccess(RepositoryModel repository, UserModel user, String action) { |
| | | protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
|
| | | return user.canView(repository);
|
| | | }
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Serves the content of a gh-pages branch.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class PagesServlet extends HttpServlet {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns an url to this servlet for the specified parameters.
|
| | | * |
| | | *
|
| | | * @param baseURL
|
| | | * @param repository
|
| | | * @param path
|
| | |
| | | /**
|
| | | * Retrieves the specified resource from the gh-pages branch of the
|
| | | * repository.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @param response
|
| | | * @throws javax.servlet.ServletException
|
| | |
| | | /*
|
| | | * Copyright 2012 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;
|
| | |
| | | public boolean supportsTeamMembershipChanges() {
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected AccountType getAccountType() {
|
| | | return AccountType.REDMINE;
|
| | |
| | | return null;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | if (StringUtils.isEmpty(jsonString)) {
|
| | | logger.error("Received empty authentication response from Redmine");
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | RedmineCurrent current = null;
|
| | | try {
|
| | | current = new Gson().fromJson(jsonString, RedmineCurrent.class);
|
| | |
| | | InputStreamReader reader = new InputStreamReader(http.getInputStream());
|
| | | return IOUtils.toString(reader);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * set json response. do NOT invoke from production code.
|
| | | * @param json json
|
| | |
| | |
|
| | | /**
|
| | | * Handles requests for robots.txt
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RobotsTxtServlet extends HttpServlet {
|
| | |
|
| | |
| | | public RobotsTxtServlet() {
|
| | | super();
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
| | | throws ServletException, java.io.IOException {
|
| | |
| | |
|
| | | /**
|
| | | * The RpcFilter is a servlet filter that secures the RpcServlet.
|
| | | * |
| | | *
|
| | | * The filter extracts the rpc request type from the url and determines if the
|
| | | * requested action requires a Basic authentication prompt. If authentication is
|
| | | * required and no credentials are stored in the "Authorization" header, then a
|
| | | * basic authentication challenge is issued.
|
| | | * |
| | | *
|
| | | * http://en.wikipedia.org/wiki/Basic_access_authentication
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RpcFilter extends AuthenticationFilter {
|
| | |
|
| | | /**
|
| | | * doFilter does the actual work of preprocessing the request to ensure that
|
| | | * the user may proceed.
|
| | | * |
| | | *
|
| | | * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
|
| | | * javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Handles remote procedure calls.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RpcServlet extends JsonServlet {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Processes an rpc request.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @param response
|
| | | * @throws javax.servlet.ServletException
|
| | |
| | | import com.sforce.ws.ConnectorConfig; |
| | | |
| | | public class SalesforceUserService extends GitblitUserService { |
| | | public static final Logger logger = LoggerFactory |
| | | .getLogger(SalesforceUserService.class); |
| | | |
| | | public static final Logger logger = LoggerFactory.getLogger(SalesforceUserService.class); |
| | | private IStoredSettings settings; |
| | | |
| | | |
| | | @Override |
| | | protected AccountType getAccountType() { |
| | | return AccountType.SALESFORCE; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void setup(IStoredSettings settings) { |
| | | this.settings = settings; |
| | |
| | |
|
| | | /**
|
| | | * Handles requests for Sparkleshare Invites
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SparkleShareInviteServlet extends HttpServlet {
|
| | |
|
| | |
| | | public SparkleShareInviteServlet() {
|
| | | super();
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
| | | throws ServletException, java.io.IOException {
|
| | |
| | |
|
| | | protected void processRequest(javax.servlet.http.HttpServletRequest request,
|
| | | javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
|
| | | java.io.IOException { |
| | | |
| | | java.io.IOException {
|
| | |
|
| | | // extract repo name from request
|
| | | String repoUrl = request.getPathInfo().substring(1);
|
| | |
|
| | |
| | | if (repoUrl.endsWith(".xml")) {
|
| | | repoUrl = repoUrl.substring(0, repoUrl.length() - 4);
|
| | | }
|
| | | |
| | |
|
| | | String servletPath = Constants.GIT_PATH;
|
| | | |
| | |
|
| | | int schemeIndex = repoUrl.indexOf("://") + 3;
|
| | | String host = repoUrl.substring(0, repoUrl.indexOf('/', schemeIndex)); |
| | | String host = repoUrl.substring(0, repoUrl.indexOf('/', schemeIndex));
|
| | | String path = repoUrl.substring(repoUrl.indexOf(servletPath) + servletPath.length());
|
| | | String username = null;
|
| | | int fetchIndex = repoUrl.indexOf('@');
|
| | |
| | | user = UserModel.ANONYMOUS;
|
| | | username = "";
|
| | | }
|
| | | |
| | |
|
| | | // ensure that the requested repository exists
|
| | | RepositoryModel model = GitBlit.self().getRepositoryModel(path);
|
| | | if (model == null) {
|
| | |
| | | response.getWriter().append(MessageFormat.format("Repository \"{0}\" not found!", path));
|
| | | return;
|
| | | }
|
| | | |
| | | StringBuilder sb = new StringBuilder(); |
| | |
|
| | | StringBuilder sb = new StringBuilder();
|
| | | sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
| | | sb.append("<sparkleshare><invite>\n");
|
| | | sb.append(MessageFormat.format("<address>{0}</address>\n", host));
|
| | |
| | | * The SyndicationFilter is an AuthenticationFilter which ensures that feed
|
| | | * requests for projects or view-restricted repositories have proper authentication
|
| | | * credentials and are authorized for the requested feed.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SyndicationFilter extends AuthenticationFilter {
|
| | |
|
| | | /**
|
| | | * Extract the repository name from the url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return repository name
|
| | | */
|
| | |
| | | /**
|
| | | * doFilter does the actual work of preprocessing the request to ensure that
|
| | | * the user may proceed.
|
| | | * |
| | | *
|
| | | * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
|
| | | * javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
| | | */
|
| | |
| | |
|
| | | ProjectModel project = GitBlit.self().getProjectModel(name);
|
| | | RepositoryModel model = null;
|
| | | |
| | |
|
| | | if (project == null) {
|
| | | // try loading a repository model
|
| | | model = GitBlit.self().getRepositoryModel(name);
|
| | |
| | | return;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // Wrap the HttpServletRequest with the AccessRestrictionRequest which
|
| | | // overrides the servlet container user principal methods.
|
| | | // JGit requires either:
|
| | |
| | |
|
| | | /**
|
| | | * SyndicationServlet generates RSS 2.0 feeds and feed links.
|
| | | * |
| | | *
|
| | | * Access to this servlet is protected by the SyndicationFilter.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SyndicationServlet extends HttpServlet {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Create a feed link for the specified repository and branch/tag/commit id.
|
| | | * |
| | | *
|
| | | * @param baseURL
|
| | | * @param repository
|
| | | * the repository name
|
| | |
| | |
|
| | | /**
|
| | | * Determines the appropriate title for a feed.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * @return title of the feed
|
| | |
| | |
|
| | | /**
|
| | | * Generates the feed content.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @param response
|
| | | * @throws javax.servlet.ServletException
|
| | |
| | | }
|
| | |
|
| | | response.setContentType("application/rss+xml; charset=UTF-8");
|
| | | |
| | |
|
| | | boolean isProjectFeed = false;
|
| | | String feedName = null;
|
| | | String feedTitle = null;
|
| | | String feedDescription = null;
|
| | | |
| | |
|
| | | List<String> repositories = null;
|
| | | if (repositoryName.indexOf('/') == -1 && !repositoryName.toLowerCase().endsWith(".git")) {
|
| | | // try to find a project
|
| | |
| | | if (project != null) {
|
| | | isProjectFeed = true;
|
| | | repositories = new ArrayList<String>(project.repositories);
|
| | | |
| | |
|
| | | // project feed
|
| | | feedName = project.name;
|
| | | feedTitle = project.title;
|
| | | feedDescription = project.description;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | if (repositories == null) {
|
| | | // could not find project, assume this is a repository
|
| | | repositories = Arrays.asList(repositoryName);
|
| | |
| | | if (repository == null) {
|
| | | if (model.isCollectingGarbage) {
|
| | | logger.warn(MessageFormat.format("Temporarily excluding {0} from feed, busy collecting garbage", name));
|
| | | } |
| | | }
|
| | | continue;
|
| | | }
|
| | | if (!isProjectFeed) {
|
| | |
| | | feedTitle = model.name;
|
| | | feedDescription = model.description;
|
| | | }
|
| | | |
| | |
|
| | | List<RevCommit> commits;
|
| | | if (StringUtils.isEmpty(searchString)) {
|
| | | // standard log/history lookup
|
| | |
| | | commit.getFullMessage());
|
| | | entry.content = message;
|
| | | entry.repository = model.name;
|
| | | entry.branch = objectId; |
| | | entry.branch = objectId;
|
| | | entry.tags = new ArrayList<String>();
|
| | |
|
| | | // add commit id and parent commit ids
|
| | |
| | | for (RefModel ref : refs) {
|
| | | entry.tags.add("ref:" + ref.getName());
|
| | | }
|
| | | } |
| | | }
|
| | | entries.add(entry);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // sort & truncate the feed
|
| | | Collections.sort(entries);
|
| | | if (entries.size() > length) {
|
| | | // clip the list
|
| | | entries = entries.subList(0, length);
|
| | | }
|
| | | |
| | |
|
| | | String feedLink;
|
| | | if (isProjectFeed) {
|
| | | // project feed
|
| | |
| | |
|
| | | /**
|
| | | * Loads Gitblit settings from the context-parameter values of a web.xml file.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class WebXmlSettings extends IStoredSettings {
|
| | |
|
| | |
| | |
|
| | | public void applyOverrides(File overrideFile) {
|
| | | this.overrideFile = overrideFile;
|
| | | |
| | |
|
| | | // apply any web-configured overrides
|
| | | if (overrideFile.exists()) {
|
| | | try {
|
| | |
| | |
|
| | | /**
|
| | | * Implementation of a Windows user service.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class WindowsUserService extends GitblitUserService {
|
| | |
| | | private final Logger logger = LoggerFactory.getLogger(WindowsUserService.class);
|
| | |
|
| | | private IStoredSettings settings;
|
| | | |
| | |
|
| | | private IWindowsAuthProvider waffle;
|
| | |
|
| | | public WindowsUserService() {
|
| | |
| | |
|
| | | serviceImpl = createUserService(realmFile);
|
| | | logger.info("Windows User Service backed by " + serviceImpl.toString());
|
| | | |
| | |
|
| | | waffle = new WindowsAuthProviderImpl();
|
| | | IWindowsComputer computer = waffle.getCurrentComputer();
|
| | | logger.info(" name = " + computer.getComputerName());
|
| | |
| | | logger.info(" memberOf = " + computer.getMemberOf());
|
| | | //logger.info(" groups = " + Arrays.asList(computer.getGroups()));
|
| | | }
|
| | | |
| | |
|
| | | protected String describeJoinStatus(String value) {
|
| | | if ("NetSetupUnknownStatus".equals(value)) {
|
| | | return "unknown";
|
| | |
| | | public boolean supportsTeamMembershipChanges() {
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected AccountType getAccountType() {
|
| | | return AccountType.WINDOWS;
|
| | |
| | | identity.dispose();
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | UserModel user = getUserModel(username);
|
| | | if (user == null) // create user object for new authenticated user
|
| | | user = new UserModel(username.toLowerCase());
|
| | |
| | | for (IWindowsAccount group : identity.getGroups()) {
|
| | | groupNames.add(group.getFqn());
|
| | | }
|
| | | |
| | |
|
| | | if (groupNames.contains("BUILTIN\\Administrators")) {
|
| | | // local administrator
|
| | | user.canAdmin = true;
|
| | | }
|
| | | |
| | |
|
| | | // TODO consider mapping Windows groups to teams
|
| | |
|
| | | // push the changes to the backing user service
|
| | |
| | |
|
| | | // cleanup resources
|
| | | identity.dispose();
|
| | | |
| | |
|
| | | return user;
|
| | | }
|
| | | }
|
| | |
| | | return doRequest();
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void done() {
|
| | | parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
| | | try {
|
| | |
| | | /**
|
| | | * Displays a subscribed icon on the left of the repository name, if there is at
|
| | | * least one subscribed branch.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class CertificateStatusRenderer extends DefaultTableCellRenderer {
|
| | |
|
| | |
| | | okIcon = new ImageIcon(getClass().getResource("/bullet_green.png"));
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a list of user certificate models.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class CertificatesTableModel extends AbstractTableModel {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | UserCertificateModel ucm;
|
| | | |
| | |
|
| | | enum Columns {
|
| | | SerialNumber, Status, Reason, Issued, Expires;
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | Columns col = Columns.values()[columnIndex];
|
| | | switch (col) {
|
| | |
| | | if (ucm.getStatus(cert).equals(CertificateStatus.revoked)) {
|
| | | RevocationReason r = ucm.getRevocationReason(cert.getSerialNumber());
|
| | | return Translation.get("gb." + r.name());
|
| | | } |
| | | }
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
| | | public X509Certificate get(int modelRow) {
|
| | | return ucm.certs.get(modelRow);
|
| | | }
|
| | | |
| | |
|
| | | public void setUserCertificateModel(UserCertificateModel ucm) {
|
| | | this.ucm = ucm;
|
| | | if (ucm == null) {
|
| | |
| | | import com.gitblit.utils.X509Utils.X509Metadata; |
| | | |
| | | public class DefaultOidsPanel extends JPanel { |
| | | |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | |
| | | private JTextField organizationalUnit; |
| | | private JTextField organization; |
| | | private JTextField locality; |
| | |
| | | |
| | | public DefaultOidsPanel(X509Metadata metadata) { |
| | | super(); |
| | | |
| | | |
| | | organizationalUnit = new JTextField(metadata.getOID("OU", ""), 20); |
| | | organization = new JTextField(metadata.getOID("O", ""), 20); |
| | | locality = new JTextField(metadata.getOID("L", ""), 20); |
| | | stateProvince = new JTextField(metadata.getOID("ST", ""), 20); |
| | | countryCode = new JTextField(metadata.getOID("C", ""), 20); |
| | | |
| | | |
| | | setLayout(new GridLayout(0, 1, Utils.MARGIN, Utils.MARGIN)); |
| | | add(Utils.newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnit)); |
| | | add(Utils.newFieldPanel(Translation.get("gb.organization") + " (O)", organization)); |
| | |
| | | add(Utils.newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvince)); |
| | | add(Utils.newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCode)); |
| | | } |
| | | |
| | | |
| | | public void update(X509Metadata metadata) { |
| | | metadata.setOID("OU", organizationalUnit.getText()); |
| | | metadata.setOID("O", organization.getText()); |
| | |
| | | metadata.setOID("ST", stateProvince.getText()); |
| | | metadata.setOID("C", countryCode.getText()); |
| | | } |
| | | |
| | | |
| | | public String getOrganizationalUnit() { |
| | | return organizationalUnit.getText(); |
| | | } |
| | | |
| | | |
| | | public String getOrganization() { |
| | | return organization.getText(); |
| | | } |
| | | |
| | | |
| | | public String getLocality() { |
| | | return locality.getText(); |
| | | } |
| | |
| | |
|
| | | /**
|
| | | * Simple GUI tool for administering Gitblit client certificates.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | public class GitblitAuthority extends JFrame implements X509Log {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | private final UserCertificateTableModel tableModel;
|
| | |
|
| | | private UserCertificatePanel userCertificatePanel;
|
| | | |
| | |
|
| | | private File folder;
|
| | | |
| | |
|
| | | private IStoredSettings gitblitSettings;
|
| | | |
| | |
|
| | | private IUserService userService;
|
| | | |
| | |
|
| | | private String caKeystorePassword;
|
| | |
|
| | | private JTable table;
|
| | | |
| | |
|
| | | private int defaultDuration;
|
| | | |
| | |
|
| | | private TableRowSorter<UserCertificateTableModel> defaultSorter;
|
| | | |
| | |
|
| | | private MailExecutor mail;
|
| | |
|
| | | private JButton certificateDefaultsButton;
|
| | |
| | | }
|
| | | final String baseFolder = folder;
|
| | | EventQueue.invokeLater(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | try {
|
| | | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
| | |
| | | tableModel = new UserCertificateTableModel();
|
| | | defaultSorter = new TableRowSorter<UserCertificateTableModel>(tableModel);
|
| | | }
|
| | | |
| | |
|
| | | public void initialize(String baseFolder) {
|
| | | setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
|
| | | setTitle("Gitblit Certificate Authority v" + Constants.getVersion() + " (" + Constants.getBuildDate() + ")");
|
| | |
| | | @Override
|
| | | public void windowOpened(WindowEvent event) {
|
| | | }
|
| | | }); |
| | | });
|
| | |
|
| | | File folder = new File(baseFolder).getAbsoluteFile();
|
| | | load(folder);
|
| | | |
| | |
|
| | | setSizeAndPosition();
|
| | | }
|
| | | |
| | |
|
| | | private void setSizeAndPosition() {
|
| | | String sz = null;
|
| | | String pos = null;
|
| | |
| | | Utils.showException(GitblitAuthority.this, t);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private StoredConfig getConfig() throws IOException, ConfigInvalidException {
|
| | | File configFile = new File(folder, X509Utils.CA_CONFIG);
|
| | | FileBasedConfig config = new FileBasedConfig(configFile, FS.detect());
|
| | | config.load();
|
| | | return config;
|
| | | }
|
| | | |
| | |
|
| | | private IUserService loadUsers(File folder) {
|
| | | File file = new File(folder, "gitblit.properties");
|
| | | if (!file.exists()) {
|
| | |
| | | } else {
|
| | | throw new RuntimeException("Unsupported user service: " + us);
|
| | | }
|
| | | |
| | |
|
| | | service = new ConfigUserService(FileUtils.resolveParameter(Constants.baseFolder$, folder, us));
|
| | | return service;
|
| | | }
|
| | | |
| | |
|
| | | private void load(File folder) {
|
| | | this.folder = folder;
|
| | | this.userService = loadUsers(folder);
|
| | |
| | | Map<String, UserCertificateModel> map = new HashMap<String, UserCertificateModel>();
|
| | | for (String user : userService.getAllUsernames()) {
|
| | | UserModel model = userService.getUserModel(user);
|
| | | UserCertificateModel ucm = new UserCertificateModel(model); |
| | | UserCertificateModel ucm = new UserCertificateModel(model);
|
| | | map.put(user, ucm);
|
| | | }
|
| | | File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG);
|
| | |
| | | try {
|
| | | config.load();
|
| | | // replace user certificate model with actual data
|
| | | List<UserCertificateModel> list = UserCertificateConfig.KEY.parse(config).list; |
| | | for (UserCertificateModel ucm : list) { |
| | | List<UserCertificateModel> list = UserCertificateConfig.KEY.parse(config).list;
|
| | | for (UserCertificateModel ucm : list) {
|
| | | ucm.user = userService.getUserModel(ucm.user.username);
|
| | | map.put(ucm.user.username, ucm);
|
| | | }
|
| | |
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | tableModel.list = new ArrayList<UserCertificateModel>(map.values());
|
| | | Collections.sort(tableModel.list);
|
| | | tableModel.fireTableDataChanged();
|
| | | Utils.packColumns(table, Utils.MARGIN);
|
| | | |
| | |
|
| | | File caKeystore = new File(folder, X509Utils.CA_KEY_STORE);
|
| | | if (!caKeystore.exists()) {
|
| | | |
| | |
|
| | | if (!X509Utils.unlimitedStrength) {
|
| | | // prompt to confirm user understands JCE Standard Strength encryption
|
| | | int res = JOptionPane.showConfirmDialog(GitblitAuthority.this, Translation.get("gb.jceWarning"),
|
| | |
| | | System.exit(1);
|
| | | }
|
| | | }
|
| | | |
| | | // show certificate defaults dialog |
| | |
|
| | | // show certificate defaults dialog
|
| | | certificateDefaultsButton.doClick();
|
| | | |
| | |
|
| | | // create "localhost" ssl certificate
|
| | | prepareX509Infrastructure();
|
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private boolean prepareX509Infrastructure() {
|
| | | if (caKeystorePassword == null) {
|
| | | JPasswordField pass = new JPasswordField(10);
|
| | |
| | | X509Utils.prepareX509Infrastructure(metadata, folder, this);
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | private List<X509Certificate> findCerts(File folder, String username) {
|
| | | List<X509Certificate> list = new ArrayList<X509Certificate>();
|
| | | File userFolder = new File(folder, X509Utils.CERTS + File.separator + username);
|
| | |
| | | });
|
| | | try {
|
| | | CertificateFactory factory = CertificateFactory.getInstance("X.509");
|
| | | for (File cert : certs) { |
| | | for (File cert : certs) {
|
| | | BufferedInputStream is = new BufferedInputStream(new FileInputStream(cert));
|
| | | X509Certificate x509 = (X509Certificate) factory.generateCertificate(is);
|
| | | is.close();
|
| | |
| | | }
|
| | | return list;
|
| | | }
|
| | | |
| | | private Container getUI() { |
| | |
|
| | | private Container getUI() {
|
| | | userCertificatePanel = new UserCertificatePanel(this) {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return Utils.INSETS;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean isAllowEmail() {
|
| | | return mail.isReady();
|
| | |
| | | c.set(Calendar.MILLISECOND, 0);
|
| | | return c.getTime();
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean saveUser(String username, UserCertificateModel ucm) {
|
| | | return userService.updateUserModel(username, ucm.user);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail) {
|
| | | if (!prepareX509Infrastructure()) {
|
| | |
| | | Date notAfter = metadata.notAfter;
|
| | | setMetadataDefaults(metadata);
|
| | | metadata.notAfter = notAfter;
|
| | | |
| | |
|
| | | // set user's specified OID values
|
| | | UserModel user = ucm.user; |
| | | UserModel user = ucm.user;
|
| | | if (!StringUtils.isEmpty(user.organizationalUnit)) {
|
| | | metadata.oids.put("OU", user.organizationalUnit);
|
| | | }
|
| | |
| | | if (ucm.expires == null || metadata.notAfter.before(ucm.expires)) {
|
| | | ucm.expires = metadata.notAfter;
|
| | | }
|
| | | |
| | |
|
| | | updateAuthorityConfig(ucm);
|
| | | |
| | |
|
| | | // refresh user
|
| | | ucm.certs = null;
|
| | | int selectedIndex = table.getSelectedRow();
|
| | | tableModel.fireTableDataChanged();
|
| | | table.getSelectionModel().setSelectionInterval(selectedIndex, selectedIndex);
|
| | | |
| | |
|
| | | if (sendEmail) {
|
| | | sendEmail(user, metadata, zip);
|
| | | }
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason) {
|
| | | if (!prepareX509Infrastructure()) {
|
| | |
| | | } catch (Exception e) {
|
| | | Utils.showException(GitblitAuthority.this, e);
|
| | | }
|
| | | |
| | |
|
| | | // refresh user
|
| | | ucm.certs = null;
|
| | | int modelIndex = table.convertRowIndexToModel(table.getSelectedRow());
|
| | | tableModel.fireTableDataChanged();
|
| | | table.getSelectionModel().setSelectionInterval(modelIndex, modelIndex);
|
| | | |
| | |
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | return false;
|
| | | }
|
| | | };
|
| | | |
| | |
|
| | | table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
|
| | | table.setRowSorter(defaultSorter);
|
| | | table.setDefaultRenderer(CertificateStatus.class, new CertificateStatusRenderer());
|
| | |
| | | userCertificatePanel.setUserCertificateModel(ucm);
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JPanel usersPanel = new JPanel(new BorderLayout()) {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | |
| | | usersPanel.add(new HeaderPanel(Translation.get("gb.users"), "users_16x16.png"), BorderLayout.NORTH);
|
| | | usersPanel.add(new JScrollPane(table), BorderLayout.CENTER);
|
| | | usersPanel.setMinimumSize(new Dimension(400, 10));
|
| | | |
| | |
|
| | | certificateDefaultsButton = new JButton(new ImageIcon(getClass().getResource("/settings_16x16.png")));
|
| | | certificateDefaultsButton.setFocusable(false);
|
| | | certificateDefaultsButton.setToolTipText(Translation.get("gb.newCertificateDefaults")); |
| | | certificateDefaultsButton.setToolTipText(Translation.get("gb.newCertificateDefaults"));
|
| | | certificateDefaultsButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | |
| | | certificateConfig.update(metadata);
|
| | | }
|
| | | InputVerifier verifier = new InputVerifier() {
|
| | | @Override
|
| | | public boolean verify(JComponent comp) {
|
| | | boolean returnValue;
|
| | | JTextField textField = (JTextField) comp;
|
| | |
| | | validityTF.setText("" + certificateConfig.duration);
|
| | | JPanel validityPanel = Utils.newFieldPanel(Translation.get("gb.validity"),
|
| | | validityTF, Translation.get("gb.duration.days").replace("{0}", "").trim());
|
| | | |
| | |
|
| | | JPanel p1 = new JPanel(new GridLayout(0, 1, 5, 2));
|
| | | p1.add(siteNamePanel);
|
| | | p1.add(validityPanel);
|
| | | |
| | |
|
| | | DefaultOidsPanel oids = new DefaultOidsPanel(metadata);
|
| | |
|
| | | JPanel panel = new JPanel(new BorderLayout());
|
| | | panel.add(p1, BorderLayout.NORTH);
|
| | | panel.add(oids, BorderLayout.CENTER);
|
| | |
|
| | | int result = JOptionPane.showConfirmDialog(GitblitAuthority.this, |
| | | int result = JOptionPane.showConfirmDialog(GitblitAuthority.this,
|
| | | panel, Translation.get("gb.newCertificateDefaults"), JOptionPane.OK_CANCEL_OPTION,
|
| | | JOptionPane.QUESTION_MESSAGE, new ImageIcon(getClass().getResource("/settings_32x32.png")));
|
| | | if (result == JOptionPane.OK_OPTION) {
|
| | |
| | | certificateConfig.duration = Integer.parseInt(validityTF.getText());
|
| | | certificateConfig.store(config, metadata);
|
| | | config.save();
|
| | | |
| | |
|
| | | Map<String, String> updates = new HashMap<String, String>();
|
| | | updates.put(Keys.web.siteName, siteNameTF.getText());
|
| | | gitblitSettings.saveSettings(updates);
|
| | |
| | | }
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | newSSLCertificate = new JButton(new ImageIcon(getClass().getResource("/rosette_16x16.png")));
|
| | | newSSLCertificate.setFocusable(false);
|
| | | newSSLCertificate.setToolTipText(Translation.get("gb.newSSLCertificate")); |
| | | newSSLCertificate.setToolTipText(Translation.get("gb.newSSLCertificate"));
|
| | | newSSLCertificate.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | |
| | | final Date expires = dialog.getExpiration();
|
| | | final String hostname = dialog.getHostname();
|
| | | final boolean serveCertificate = dialog.isServeCertificate();
|
| | | |
| | |
|
| | | AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) {
|
| | |
|
| | | @Override
|
| | |
| | | if (!prepareX509Infrastructure()) {
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | // read CA private key and certificate
|
| | | File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE);
|
| | | PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword);
|
| | | X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword);
|
| | | |
| | |
|
| | | // generate new SSL certificate
|
| | | X509Metadata metadata = new X509Metadata(hostname, caKeystorePassword);
|
| | | setMetadataDefaults(metadata);
|
| | |
| | | @Override
|
| | | protected void onSuccess() {
|
| | | if (serveCertificate) {
|
| | | JOptionPane.showMessageDialog(GitblitAuthority.this, |
| | | JOptionPane.showMessageDialog(GitblitAuthority.this,
|
| | | MessageFormat.format(Translation.get("gb.sslCertificateGeneratedRestart"), hostname),
|
| | | Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE);
|
| | | } else {
|
| | | JOptionPane.showMessageDialog(GitblitAuthority.this, |
| | | JOptionPane.showMessageDialog(GitblitAuthority.this,
|
| | | MessageFormat.format(Translation.get("gb.sslCertificateGenerated"), hostname),
|
| | | Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE);
|
| | | }
|
| | | }
|
| | | };
|
| | | |
| | |
|
| | | worker.execute();
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JButton emailBundle = new JButton(new ImageIcon(getClass().getResource("/mail_16x16.png")));
|
| | | emailBundle.setFocusable(false);
|
| | | emailBundle.setToolTipText(Translation.get("gb.emailCertificateBundle")); |
| | | emailBundle.setToolTipText(Translation.get("gb.emailCertificateBundle"));
|
| | | emailBundle.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | |
| | | if (!zip.exists()) {
|
| | | return;
|
| | | }
|
| | | |
| | |
|
| | | AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) {
|
| | | @Override
|
| | | protected Boolean doRequest() throws IOException {
|
| | |
| | | JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.clientCertificateBundleSent"),
|
| | | ucm.user.getDisplayName()));
|
| | | }
|
| | | |
| | |
|
| | | };
|
| | | worker.execute(); |
| | | worker.execute();
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JButton logButton = new JButton(new ImageIcon(getClass().getResource("/script_16x16.png")));
|
| | | logButton.setFocusable(false);
|
| | | logButton.setToolTipText(Translation.get("gb.log")); |
| | | logButton.setToolTipText(Translation.get("gb.log"));
|
| | | logButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | |
| | | }
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | final JTextField filterTextfield = new JTextField(15);
|
| | | filterTextfield.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | filterUsers(filterTextfield.getText());
|
| | | }
|
| | | });
|
| | | filterTextfield.addKeyListener(new KeyAdapter() {
|
| | | @Override
|
| | | public void keyReleased(KeyEvent e) {
|
| | | filterUsers(filterTextfield.getText());
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JToolBar buttonControls = new JToolBar(JToolBar.HORIZONTAL);
|
| | | buttonControls.setFloatable(false);
|
| | | buttonControls.add(certificateDefaultsButton);
|
| | |
| | | JPanel userControls = new JPanel(new FlowLayout(FlowLayout.RIGHT, Utils.MARGIN, Utils.MARGIN));
|
| | | userControls.add(new JLabel(Translation.get("gb.filter")));
|
| | | userControls.add(filterTextfield);
|
| | | |
| | |
|
| | | JPanel topPanel = new JPanel(new BorderLayout(0, 0));
|
| | | topPanel.add(buttonControls, BorderLayout.WEST);
|
| | | topPanel.add(userControls, BorderLayout.EAST);
|
| | | |
| | |
|
| | | JPanel leftPanel = new JPanel(new BorderLayout());
|
| | | leftPanel.add(topPanel, BorderLayout.NORTH);
|
| | | leftPanel.add(usersPanel, BorderLayout.CENTER);
|
| | | |
| | |
|
| | | userCertificatePanel.setMinimumSize(new Dimension(375, 10));
|
| | | |
| | |
|
| | | JLabel statusLabel = new JLabel();
|
| | | statusLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
| | | if (X509Utils.unlimitedStrength) {
|
| | |
| | | } else {
|
| | | statusLabel.setText("JCE Standard Encryption Policy");
|
| | | }
|
| | | |
| | |
|
| | | JPanel root = new JPanel(new BorderLayout()) {
|
| | | private static final long serialVersionUID = 1L;
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return Utils.INSETS;
|
| | | }
|
| | |
| | | root.add(statusLabel, BorderLayout.SOUTH);
|
| | | return root;
|
| | | }
|
| | | |
| | |
|
| | | private void filterUsers(final String fragment) {
|
| | | table.clearSelection();
|
| | | userCertificatePanel.setUserCertificateModel(null);
|
| | |
| | | return;
|
| | | }
|
| | | RowFilter<UserCertificateTableModel, Object> containsFilter = new RowFilter<UserCertificateTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(Entry<? extends UserCertificateTableModel, ? extends Object> entry) {
|
| | | for (int i = entry.getValueCount() - 1; i >= 0; i--) {
|
| | | if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
|
| | |
| | | sorter.setRowFilter(containsFilter);
|
| | | table.setRowSorter(sorter);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public void log(String message) {
|
| | | BufferedWriter writer = null;
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private boolean sendEmail(UserModel user, X509Metadata metadata, File zip) {
|
| | | // send email
|
| | | try {
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | private void setMetadataDefaults(X509Metadata metadata) {
|
| | | metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, Constants.NAME);
|
| | | if (StringUtils.isEmpty(metadata.serverHostname)) {
|
| | | metadata.serverHostname = Constants.NAME;
|
| | | }
|
| | | |
| | |
|
| | | // set default values from config file
|
| | | File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG);
|
| | | FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect());
|
| | |
| | | certificateConfig.update(metadata);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private void updateAuthorityConfig(UserCertificateModel ucm) {
|
| | | File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG);
|
| | | FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect());
|
| | |
| | |
|
| | | /**
|
| | | * Downloads dependencies and launches Gitblit Authority.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class Launcher {
|
| | |
|
| | |
| | |
|
| | | public static void main(String[] args) {
|
| | | final SplashScreen splash = SplashScreen.getSplashScreen();
|
| | | |
| | |
|
| | | File libFolder = new File("ext");
|
| | | List<File> jars = findJars(libFolder.getAbsoluteFile());
|
| | | |
| | |
|
| | | // sort the jars by name and then reverse the order so the newer version
|
| | | // of the library gets loaded in the event that this is an upgrade
|
| | | Collections.sort(jars);
|
| | |
| | |
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | updateSplash(splash, Translation.get("gb.starting") + " Gitblit Authority...");
|
| | | GitblitAuthority.main(args);
|
| | | }
|
| | |
| | | }
|
| | | try {
|
| | | EventQueue.invokeAndWait(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | Graphics2D g = splash.createGraphics();
|
| | | if (g != null) {
|
| | | // Splash is 320x120
|
| | | FontMetrics fm = g.getFontMetrics();
|
| | | |
| | |
|
| | | // paint startup status
|
| | | g.setColor(Color.darkGray);
|
| | | int h = fm.getHeight() + fm.getMaxDescent();
|
| | |
| | | g.setColor(Color.WHITE);
|
| | | int xw = fm.stringWidth(string);
|
| | | g.drawString(string, x + ((w - xw) / 2), y - 5);
|
| | | |
| | |
|
| | | // paint version
|
| | | String ver = "v" + Constants.getVersion();
|
| | | int vw = g.getFontMetrics().stringWidth(ver);
|
| | |
| | | t.printStackTrace();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static List<File> findJars(File folder) {
|
| | | List<File> jars = new ArrayList<File>();
|
| | | if (folder.exists()) {
|
| | |
| | |
|
| | | /**
|
| | | * Adds a file to the classpath
|
| | | * |
| | | *
|
| | | * @param f
|
| | | * the file to be added
|
| | | * @throws IOException
|
| | |
| | |
|
| | | /**
|
| | | * Certificate config file parser.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class NewCertificateConfig {
|
| | | public static final SectionParser<NewCertificateConfig> KEY = new SectionParser<NewCertificateConfig>() {
|
| | | @Override
|
| | | public NewCertificateConfig parse(final Config cfg) {
|
| | | return new NewCertificateConfig(cfg);
|
| | | }
|
| | |
| | | public String L;
|
| | | public String ST;
|
| | | public String C;
|
| | | |
| | |
|
| | | public int duration;
|
| | | |
| | |
|
| | | private NewCertificateConfig(final Config c) {
|
| | | duration = c.getInt("new", null, "duration", 0);
|
| | | OU = c.getString("new", null, "organizationalUnit");
|
| | | O = c.getString("new", null, "organization");
|
| | | L = c.getString("new", null, "locality");
|
| | | ST = c.getString("new", null, "stateProvince");
|
| | | C = c.getString("new", null, "countryCode"); |
| | | C = c.getString("new", null, "countryCode");
|
| | | }
|
| | | |
| | |
|
| | | public void update(X509Metadata metadata) {
|
| | | update(metadata, "OU", OU);
|
| | | update(metadata, "O", O);
|
| | |
| | | metadata.notAfter = new Date(System.currentTimeMillis() + duration*TimeUtils.ONEDAY);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private void update(X509Metadata metadata, String oid, String value) {
|
| | | if (!StringUtils.isEmpty(value)) {
|
| | | metadata.oids.put(oid, value);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public void store(Config c, X509Metadata metadata) {
|
| | | store(c, "new", "organizationalUnit", metadata.getOID("OU", null));
|
| | | store(c, "new", "organization", metadata.getOID("O", null));
|
| | |
| | | c.setInt("new", null, "duration", duration);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private void store(Config c, String section, String name, String value) {
|
| | | if (StringUtils.isEmpty(value)) {
|
| | | c.unset(section, null, name);
|
| | |
| | | public class NewClientCertificateDialog extends JDialog {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | JDateChooser expirationDate;
|
| | | JPasswordField pw1;
|
| | | JPasswordField pw2;
|
| | |
| | |
|
| | | public NewClientCertificateDialog(Frame owner, String displayname, Date defaultExpiration, boolean allowEmail) {
|
| | | super(owner);
|
| | | |
| | |
|
| | | setTitle(Translation.get("gb.newCertificate"));
|
| | | |
| | | JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) { |
| | |
|
| | | JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | |
| | |
|
| | | return Utils.INSETS;
|
| | | }
|
| | | };
|
| | | |
| | |
|
| | | expirationDate = new JDateChooser(defaultExpiration);
|
| | | pw1 = new JPasswordField(20);
|
| | | pw2 = new JPasswordField(20);
|
| | | hint = new JTextField(20);
|
| | | sendEmail = new JCheckBox(Translation.get("gb.sendEmail"));
|
| | | |
| | |
|
| | | JPanel panel = new JPanel(new GridLayout(0, 2, Utils.MARGIN, Utils.MARGIN));
|
| | | |
| | |
|
| | | panel.add(new JLabel(Translation.get("gb.expires")));
|
| | | panel.add(expirationDate);
|
| | | |
| | |
|
| | | panel.add(new JLabel(Translation.get("gb.password")));
|
| | | panel.add(pw1);
|
| | |
|
| | | panel.add(new JLabel(Translation.get("gb.confirmPassword")));
|
| | | panel.add(pw2);
|
| | | |
| | |
|
| | | panel.add(new JLabel(Translation.get("gb.passwordHint")));
|
| | | panel.add(hint);
|
| | | |
| | |
|
| | | if (allowEmail) {
|
| | | panel.add(new JLabel(""));
|
| | | panel.add(sendEmail);
|
| | | }
|
| | |
|
| | | |
| | |
|
| | | JButton ok = new JButton(Translation.get("gb.ok"));
|
| | | ok.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | if (validateInputs()) {
|
| | | isCanceled = false;
|
| | |
| | | });
|
| | | JButton cancel = new JButton(Translation.get("gb.cancel"));
|
| | | cancel.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | isCanceled = true;
|
| | | setVisible(false);
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JPanel controls = new JPanel();
|
| | | controls.add(ok);
|
| | | controls.add(cancel);
|
| | | |
| | |
|
| | | JTextArea message = new JTextArea(Translation.get("gb.newClientCertificateMessage"));
|
| | | message.setLineWrap(true);
|
| | | message.setWrapStyleWord(true);
|
| | | message.setEditable(false);
|
| | | message.setRows(6);
|
| | | message.setPreferredSize(new Dimension(300, 100));
|
| | | |
| | |
|
| | | content.add(new JScrollPane(message), BorderLayout.CENTER);
|
| | | content.add(panel, BorderLayout.NORTH);
|
| | | content.add(controls, BorderLayout.SOUTH);
|
| | | |
| | |
|
| | | getContentPane().add(new HeaderPanel(Translation.get("gb.newCertificate") + " (" + displayname + ")", "rosette_16x16.png"), BorderLayout.NORTH);
|
| | | getContentPane().add(content, BorderLayout.CENTER);
|
| | | pack();
|
| | | |
| | |
|
| | | setLocationRelativeTo(owner);
|
| | | }
|
| | | |
| | |
|
| | | private boolean validateInputs() {
|
| | | if (getExpiration().getTime() < System.currentTimeMillis()) {
|
| | | // expires before now
|
| | |
| | | }
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | public String getPassword() {
|
| | | return new String(pw1.getPassword());
|
| | | }
|
| | | |
| | |
|
| | | public String getPasswordHint() {
|
| | | return hint.getText();
|
| | | }
|
| | | |
| | |
|
| | | public Date getExpiration() {
|
| | | return expirationDate.getDate();
|
| | | }
|
| | | |
| | |
|
| | | public boolean sendEmail() {
|
| | | return sendEmail.isSelected();
|
| | | }
|
| | | |
| | |
|
| | | public boolean isCanceled() {
|
| | | return isCanceled;
|
| | | }
|
| | |
| | | public class NewSSLCertificateDialog extends JDialog {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | JDateChooser expirationDate;
|
| | | JTextField hostname;
|
| | | JCheckBox serveCertificate;
|
| | |
| | |
|
| | | public NewSSLCertificateDialog(Frame owner, Date defaultExpiration) {
|
| | | super(owner);
|
| | | |
| | |
|
| | | setTitle(Translation.get("gb.newSSLCertificate"));
|
| | | |
| | | JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) { |
| | |
|
| | | JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | |
| | |
|
| | | return Utils.INSETS;
|
| | | }
|
| | | };
|
| | | |
| | |
|
| | | expirationDate = new JDateChooser(defaultExpiration);
|
| | | hostname = new JTextField(20);
|
| | | serveCertificate = new JCheckBox(Translation.get("gb.serveCertificate"), true);
|
| | | |
| | |
|
| | | JPanel panel = new JPanel(new GridLayout(0, 2, Utils.MARGIN, Utils.MARGIN));
|
| | | |
| | |
|
| | | panel.add(new JLabel(Translation.get("gb.hostname")));
|
| | | panel.add(hostname);
|
| | |
|
| | | panel.add(new JLabel(Translation.get("gb.expires")));
|
| | | panel.add(expirationDate);
|
| | | |
| | |
|
| | | panel.add(new JLabel(""));
|
| | | panel.add(serveCertificate);
|
| | | |
| | |
|
| | | JButton ok = new JButton(Translation.get("gb.ok"));
|
| | | ok.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | if (validateInputs()) {
|
| | | isCanceled = false;
|
| | |
| | | });
|
| | | JButton cancel = new JButton(Translation.get("gb.cancel"));
|
| | | cancel.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | isCanceled = true;
|
| | | setVisible(false);
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JPanel controls = new JPanel();
|
| | | controls.add(ok);
|
| | | controls.add(cancel);
|
| | |
|
| | | content.add(panel, BorderLayout.CENTER);
|
| | | content.add(controls, BorderLayout.SOUTH);
|
| | | |
| | |
|
| | | getContentPane().add(new HeaderPanel(Translation.get("gb.newSSLCertificate"), "rosette_16x16.png"), BorderLayout.NORTH);
|
| | | getContentPane().add(content, BorderLayout.CENTER);
|
| | | pack();
|
| | | |
| | |
|
| | | setLocationRelativeTo(owner);
|
| | | }
|
| | | |
| | |
|
| | | private boolean validateInputs() {
|
| | | if (getExpiration().getTime() < System.currentTimeMillis()) {
|
| | | // expires before now
|
| | |
| | | }
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | public String getHostname() {
|
| | | return hostname.getText();
|
| | | }
|
| | | |
| | |
|
| | | public Date getExpiration() {
|
| | | return expirationDate.getDate();
|
| | | }
|
| | |
| | | * allows you to specify a boolean value of false to prevent the
|
| | | * AncestorListener from being removed when the event is generated. This will
|
| | | * allow you to reuse the listener each time the event is generated.
|
| | | * |
| | | *
|
| | | * @author Rob Camick
|
| | | */
|
| | | public class RequestFocusListener implements AncestorListener
|
| | |
| | |
|
| | | /**
|
| | | * User certificate config section parser.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class UserCertificateConfig {
|
| | | public static final SectionParser<UserCertificateConfig> KEY = new SectionParser<UserCertificateConfig>() {
|
| | | public UserCertificateConfig parse(final Config cfg) { |
| | | @Override
|
| | | public UserCertificateConfig parse(final Config cfg) {
|
| | | return new UserCertificateConfig(cfg);
|
| | | }
|
| | | };
|
| | | |
| | |
|
| | | public final List<UserCertificateModel> list;
|
| | |
|
| | | private UserCertificateConfig(final Config c) {
|
| | | SimpleDateFormat df = new SimpleDateFormat(Constants.ISO8601);
|
| | | list = new ArrayList<UserCertificateModel>(); |
| | | list = new ArrayList<UserCertificateModel>();
|
| | | for (String username : c.getSubsections("user")) {
|
| | | UserCertificateModel uc = new UserCertificateModel(new UserModel(username));
|
| | | try {
|
| | | uc.expires = df.parse(c.getString("user", username, "expires"));
|
| | | } catch (ParseException e) {
|
| | | LoggerFactory.getLogger(UserCertificateConfig.class).error("Failed to parse date!", e);
|
| | | } catch (NullPointerException e) { |
| | | } catch (NullPointerException e) {
|
| | | }
|
| | | uc.notes = c.getString("user", username, "notes");
|
| | | uc.revoked = new ArrayList<String>(Arrays.asList(c.getStringList("user", username, "revoked"))); |
| | | uc.revoked = new ArrayList<String>(Arrays.asList(c.getStringList("user", username, "revoked")));
|
| | | list.add(uc);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public UserCertificateModel getUserCertificateModel(String username) {
|
| | | for (UserCertificateModel ucm : list) {
|
| | | if (ucm.user.username.equalsIgnoreCase(username)) {
|
| | |
| | | public UserCertificateModel(UserModel user) {
|
| | | this.user = user;
|
| | | }
|
| | | |
| | |
|
| | | public void update(Config config) {
|
| | | if (expires == null) {
|
| | | config.unset("user", user.username, "expires");
|
| | |
| | | public int compareTo(UserCertificateModel o) {
|
| | | return user.compareTo(o.user);
|
| | | }
|
| | | |
| | |
|
| | | public void revoke(BigInteger serial, RevocationReason reason) {
|
| | | if (revoked == null) {
|
| | | revoked = new ArrayList<String>();
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public boolean isRevoked(BigInteger serial) {
|
| | | return isRevoked(serial.toString());
|
| | | }
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | public RevocationReason getRevocationReason(BigInteger serial) {
|
| | | try {
|
| | | String sn = serial + ":";
|
| | |
| | | }
|
| | | return RevocationReason.unspecified;
|
| | | }
|
| | | |
| | |
|
| | | public CertificateStatus getStatus() {
|
| | | if (expires == null) {
|
| | | return CertificateStatus.unknown;
|
| | |
| | | }
|
| | | return CertificateStatus.ok;
|
| | | }
|
| | | |
| | |
|
| | | private boolean isExpiring(Date date) {
|
| | | return (date.getTime() - System.currentTimeMillis()) <= TimeUtils.ONEDAY * 30;
|
| | | }
|
| | | |
| | |
|
| | | private boolean isExpired(Date date) {
|
| | | return date.getTime() < System.currentTimeMillis();
|
| | | }
|
| | |
| | | public abstract class UserCertificatePanel extends JPanel {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | private Frame owner;
|
| | | |
| | |
|
| | | private UserCertificateModel ucm;
|
| | | |
| | |
|
| | | private UserOidsPanel oidsPanel;
|
| | | |
| | |
|
| | | private CertificatesTableModel tableModel;
|
| | |
|
| | | private JButton saveUserButton;
|
| | |
| | | private JButton editUserButton;
|
| | |
|
| | | private JButton newCertificateButton;
|
| | | |
| | |
|
| | | private JButton revokeCertificateButton;
|
| | |
|
| | | private JTable table;
|
| | | |
| | |
|
| | | public UserCertificatePanel(Frame owner) {
|
| | | super(new BorderLayout());
|
| | | |
| | |
|
| | | this.owner = owner;
|
| | | oidsPanel = new UserOidsPanel();
|
| | | |
| | |
|
| | | JPanel fp = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
|
| | | fp.add(oidsPanel, BorderLayout.NORTH);
|
| | | |
| | |
|
| | | JPanel fieldsPanel = new JPanel(new BorderLayout());
|
| | | fieldsPanel.add(new HeaderPanel(Translation.get("gb.properties"), "vcard_16x16.png"), BorderLayout.NORTH);
|
| | | fieldsPanel.add(fp, BorderLayout.CENTER);
|
| | | |
| | |
|
| | | saveUserButton = new JButton(Translation.get("gb.save"));
|
| | | saveUserButton.setEnabled(false);
|
| | | saveUserButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | setEditable(false);
|
| | | String username = ucm.user.username;
|
| | |
| | | saveUser(username, ucm);
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | editUserButton = new JButton(Translation.get("gb.edit"));
|
| | | editUserButton.setEnabled(false);
|
| | | editUserButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | setEditable(true);
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JPanel userControls = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
| | | userControls.add(editUserButton);
|
| | | userControls.add(saveUserButton);
|
| | | fieldsPanel.add(userControls, BorderLayout.SOUTH);
|
| | | |
| | |
|
| | | JPanel certificatesPanel = new JPanel(new BorderLayout());
|
| | | certificatesPanel.add(new HeaderPanel(Translation.get("gb.certificates"), "rosette_16x16.png"), BorderLayout.NORTH); |
| | | certificatesPanel.add(new HeaderPanel(Translation.get("gb.certificates"), "rosette_16x16.png"), BorderLayout.NORTH);
|
| | | tableModel = new CertificatesTableModel();
|
| | | table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
|
| | | table.setRowSorter(new TableRowSorter<CertificatesTableModel>(tableModel));
|
| | |
| | | }
|
| | | });
|
| | | table.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2) { |
| | | if (e.getClickCount() == 2) {
|
| | | int row = table.rowAtPoint(e.getPoint());
|
| | | int modelIndex = table.convertRowIndexToModel(row);
|
| | | X509Certificate cert = tableModel.get(modelIndex);
|
| | | X509CertificateViewer viewer = new X509CertificateViewer(UserCertificatePanel.this.owner, cert); |
| | | X509CertificateViewer viewer = new X509CertificateViewer(UserCertificatePanel.this.owner, cert);
|
| | | viewer.setVisible(true);
|
| | | }
|
| | | }
|
| | | });
|
| | | certificatesPanel.add(new JScrollPane(table), BorderLayout.CENTER);
|
| | | |
| | |
|
| | | newCertificateButton = new JButton(Translation.get("gb.newCertificate"));
|
| | | newCertificateButton.setEnabled(false);
|
| | | newCertificateButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | try {
|
| | | if (saveUserButton.isEnabled()) {
|
| | |
| | | oidsPanel.updateUser(ucm);
|
| | | saveUser(username, ucm);
|
| | | }
|
| | | |
| | |
|
| | | NewClientCertificateDialog dialog = new NewClientCertificateDialog(UserCertificatePanel.this.owner,
|
| | | ucm.user.getDisplayName(), getDefaultExpiration(), isAllowEmail());
|
| | | dialog.setModal(true);
|
| | |
| | | final UserModel user = ucm.user;
|
| | | final X509Metadata metadata = new X509Metadata(user.username, dialog.getPassword());
|
| | | metadata.userDisplayname = user.getDisplayName();
|
| | | metadata.emailAddress = user.emailAddress; |
| | | metadata.emailAddress = user.emailAddress;
|
| | | metadata.passwordHint = dialog.getPasswordHint();
|
| | | metadata.notAfter = dialog.getExpiration();
|
| | |
|
| | |
| | |
|
| | | @Override
|
| | | protected void onSuccess() {
|
| | | JOptionPane.showMessageDialog(UserCertificatePanel.this.owner, |
| | | JOptionPane.showMessageDialog(UserCertificatePanel.this.owner,
|
| | | MessageFormat.format(Translation.get("gb.clientCertificateGenerated"), user.getDisplayName()),
|
| | | Translation.get("gb.newCertificate"), JOptionPane.INFORMATION_MESSAGE);
|
| | | }
|
| | | };
|
| | | worker.execute(); |
| | | worker.execute();
|
| | | } catch (Exception x) {
|
| | | Utils.showException(UserCertificatePanel.this, x);
|
| | | }
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | revokeCertificateButton = new JButton(Translation.get("gb.revokeCertificate"));
|
| | | revokeCertificateButton.setEnabled(false);
|
| | | revokeCertificateButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | try {
|
| | | int row = table.getSelectedRow();
|
| | |
| | | }
|
| | | int modelIndex = table.convertRowIndexToModel(row);
|
| | | final X509Certificate cert = tableModel.get(modelIndex);
|
| | | |
| | |
|
| | | String [] choices = new String[RevocationReason.reasons.length];
|
| | | for (int i = 0; i < choices.length; i++) {
|
| | | choices[i] = Translation.get("gb." + RevocationReason.reasons[i].name());
|
| | | }
|
| | | |
| | |
|
| | | Object choice = JOptionPane.showInputDialog(UserCertificatePanel.this.owner,
|
| | | Translation.get("gb.revokeCertificateReason"), Translation.get("gb.revokeCertificate"),
|
| | | JOptionPane.PLAIN_MESSAGE, new ImageIcon(getClass().getResource("/rosette_32x32.png")), choices, Translation.get("gb.unspecified"));
|
| | |
| | | } else {
|
| | | // determine new expires date for user
|
| | | Date newExpires = null;
|
| | | for (X509Certificate c : ucm.certs) { |
| | | for (X509Certificate c : ucm.certs) {
|
| | | if (!c.equals(cert)) {
|
| | | if (!ucm.isRevoked(c.getSerialNumber())) {
|
| | | if (newExpires == null || c.getNotAfter().after(newExpires)) {
|
| | |
| | | }
|
| | | ucm.expires = newExpires;
|
| | | }
|
| | | |
| | |
|
| | | AuthorityWorker worker = new AuthorityWorker(UserCertificatePanel.this.owner) {
|
| | |
|
| | | @Override
|
| | |
| | |
|
| | | @Override
|
| | | protected void onSuccess() {
|
| | | JOptionPane.showMessageDialog(UserCertificatePanel.this.owner, |
| | | JOptionPane.showMessageDialog(UserCertificatePanel.this.owner,
|
| | | MessageFormat.format(Translation.get("gb.certificateRevoked"), cert.getSerialNumber(), cert.getIssuerDN().getName()),
|
| | | Translation.get("gb.revokeCertificate"), JOptionPane.INFORMATION_MESSAGE);
|
| | | }
|
| | | |
| | |
|
| | | };
|
| | | worker.execute();
|
| | | }
|
| | |
| | | }
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JPanel certificateControls = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
| | | certificateControls.add(newCertificateButton);
|
| | | certificateControls.add(revokeCertificateButton);
|
| | | certificatesPanel.add(certificateControls, BorderLayout.SOUTH);
|
| | | |
| | |
|
| | | add(fieldsPanel, BorderLayout.NORTH);
|
| | | add(certificatesPanel, BorderLayout.CENTER);
|
| | | setEditable(false);
|
| | | }
|
| | | |
| | |
|
| | | public void setUserCertificateModel(UserCertificateModel ucm) {
|
| | | this.ucm = ucm;
|
| | | setEditable(false);
|
| | | oidsPanel.setUserCertificateModel(ucm);
|
| | | |
| | |
|
| | | tableModel.setUserCertificateModel(ucm);
|
| | | tableModel.fireTableDataChanged();
|
| | | Utils.packColumns(table, Utils.MARGIN);
|
| | | }
|
| | | |
| | |
|
| | | public void setEditable(boolean editable) {
|
| | | oidsPanel.setEditable(editable);
|
| | | |
| | |
|
| | | editUserButton.setEnabled(!editable && ucm != null);
|
| | | saveUserButton.setEnabled(editable && ucm != null);
|
| | | |
| | |
|
| | | newCertificateButton.setEnabled(ucm != null);
|
| | | revokeCertificateButton.setEnabled(false);
|
| | | }
|
| | | |
| | |
|
| | | public abstract Date getDefaultExpiration();
|
| | | public abstract boolean isAllowEmail();
|
| | | |
| | |
|
| | | public abstract boolean saveUser(String username, UserCertificateModel ucm);
|
| | | public abstract boolean newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail);
|
| | | public abstract boolean revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason);
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a list of user certificate models.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class UserCertificateTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | Columns col = Columns.values()[columnIndex];
|
| | | switch (col) {
|
| | |
| | | import com.gitblit.client.Translation; |
| | | |
| | | public class UserOidsPanel extends JPanel { |
| | | |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | |
| | | private JTextField displayname; |
| | | private JTextField username; |
| | | private JTextField emailAddress; |
| | |
| | | |
| | | public UserOidsPanel() { |
| | | super(); |
| | | |
| | | |
| | | displayname = new JTextField(20); |
| | | username = new JTextField(20); |
| | | username.setEditable(false); |
| | |
| | | locality = new JTextField(20); |
| | | stateProvince = new JTextField(20); |
| | | countryCode = new JTextField(20); |
| | | |
| | | |
| | | setLayout(new GridLayout(0, 1, Utils.MARGIN, Utils.MARGIN)); |
| | | add(Utils.newFieldPanel(Translation.get("gb.displayName"), displayname)); |
| | | add(Utils.newFieldPanel(Translation.get("gb.username") + " (CN)", username)); |
| | |
| | | add(Utils.newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvince)); |
| | | add(Utils.newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCode)); |
| | | } |
| | | |
| | | |
| | | public void setUserCertificateModel(UserCertificateModel ucm) { |
| | | setEditable(false); |
| | | displayname.setText(ucm == null ? "" : ucm.user.getDisplayName()); |
| | |
| | | stateProvince.setText(ucm == null ? "" : ucm.user.stateProvince); |
| | | countryCode.setText(ucm == null ? "" : ucm.user.countryCode); |
| | | } |
| | | |
| | | |
| | | public void setEditable(boolean editable) { |
| | | displayname.setEditable(editable); |
| | | // username.setEditable(editable); |
| | |
| | | stateProvince.setEditable(editable); |
| | | countryCode.setEditable(editable); |
| | | } |
| | | |
| | | |
| | | protected void updateUser(UserCertificateModel ucm) { |
| | | ucm.user.displayName = displayname.getText(); |
| | | ucm.user.username = username.getText(); |
| | |
| | | /*
|
| | | * 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.authority;
|
| | |
|
| | | import java.awt.Color;
|
| | |
| | | import com.gitblit.utils.StringUtils;
|
| | |
|
| | | public class Utils {
|
| | | |
| | |
|
| | | public final static int LABEL_WIDTH = 175;
|
| | |
|
| | | public final static int MARGIN = 5;
|
| | |
| | | new DateCellRenderer(datePattern, Color.orange.darker()));
|
| | | return table;
|
| | | }
|
| | | |
| | |
|
| | | public static JPanel newFieldPanel(String label, Component c) {
|
| | | return newFieldPanel(label, c, null);
|
| | | }
|
| | | |
| | |
|
| | | public static JPanel newFieldPanel(String label, Component c, String trailingLabel) {
|
| | | JLabel jlabel = new JLabel(label);
|
| | | jlabel.setPreferredSize(new Dimension(Utils.LABEL_WIDTH, 20));
|
| | |
| | | panel.add(jlabel);
|
| | | panel.add(c);
|
| | | if (!StringUtils.isEmpty(trailingLabel)) {
|
| | | panel.add(new JLabel(trailingLabel)); |
| | | panel.add(new JLabel(trailingLabel));
|
| | | }
|
| | | return panel;
|
| | | }
|
| | | |
| | |
|
| | | public static void showException(Component c, Throwable t) {
|
| | | StringWriter writer = new StringWriter();
|
| | | t.printStackTrace(new PrintWriter(writer));
|
| | |
| | | JOptionPane.showMessageDialog(c, jsp, Translation.get("gb.error"),
|
| | | JOptionPane.ERROR_MESSAGE);
|
| | | }
|
| | | |
| | |
|
| | | public static void packColumns(JTable table, int margin) {
|
| | | for (int c = 0; c < table.getColumnCount(); c++) {
|
| | | packColumn(table, c, 4);
|
| | |
| | | public class X509CertificateViewer extends JDialog {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | public X509CertificateViewer(Frame owner, X509Certificate cert) {
|
| | | super(owner);
|
| | | |
| | |
|
| | | setTitle(Translation.get("gb.viewCertificate"));
|
| | | |
| | | JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) { |
| | |
|
| | | JPanel content = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN)) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | |
| | |
|
| | | return Utils.INSETS;
|
| | | }
|
| | | };
|
| | | |
| | |
|
| | | DateFormat df = DateFormat.getDateTimeInstance();
|
| | | |
| | |
|
| | | int l1 = 15;
|
| | | int l2 = 25;
|
| | | int l3 = 45;
|
| | |
| | | }
|
| | |
|
| | | content.add(panel, BorderLayout.CENTER);
|
| | | |
| | |
|
| | | JButton ok = new JButton(Translation.get("gb.ok"));
|
| | | ok.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | setVisible(false);
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | JPanel controls = new JPanel();
|
| | | controls.add(ok);
|
| | | |
| | |
|
| | | content.add(controls, BorderLayout.SOUTH);
|
| | | |
| | |
|
| | | getContentPane().add(new HeaderPanel(Translation.get("gb.certificate"), "rosette_16x16.png"), BorderLayout.NORTH);
|
| | | getContentPane().add(content, BorderLayout.CENTER);
|
| | | pack();
|
| | | |
| | |
|
| | | setLocationRelativeTo(owner);
|
| | | }
|
| | | |
| | |
|
| | | private JPanel newField(String label, String value, int cols) {
|
| | | JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2*Utils.MARGIN, 0));
|
| | | JLabel lbl = new JLabel(label);
|
| | |
| | | panel.add(tf);
|
| | | return panel;
|
| | | }
|
| | | |
| | |
|
| | | private String fingerprint(String value) {
|
| | | value = value.toUpperCase();
|
| | | StringBuilder sb = new StringBuilder();
|
| | |
| | |
|
| | | /**
|
| | | * Boolean checkbox cell renderer.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class BooleanCellRenderer extends JCheckBox implements TableCellRenderer, Serializable {
|
| | |
|
| | |
| | | setHorizontalAlignment(SwingConstants.CENTER);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | if (value instanceof Boolean) {
|
| | |
| | | /**
|
| | | * Branch renderer displays refs/heads and refs/remotes in a color similar to
|
| | | * the site.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class BranchRenderer extends DefaultTableCellRenderer implements ListCellRenderer {
|
| | |
|
| | |
| | | private static final String R_HEADS = "refs/heads/";
|
| | |
|
| | | private static final String R_REMOTES = "refs/remotes/";
|
| | | |
| | |
|
| | | private static final String R_CHANGES = "refs/changes/";
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
| | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private static final MouseListener BUTTON_MOUSE_LISTENER = new MouseAdapter() { |
| | | @Override |
| | | public void mouseEntered(MouseEvent e) { |
| | | Component component = e.getComponent(); |
| | | if (component instanceof AbstractButton) { |
| | |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void mouseExited(MouseEvent e) { |
| | | Component component = e.getComponent(); |
| | | if (component instanceof AbstractButton) { |
| | |
| | | addActionListener(this); |
| | | } |
| | | |
| | | @Override |
| | | public void actionPerformed(ActionEvent e) { |
| | | int i = pane.indexOfTabComponent(ClosableTabComponent.this); |
| | | Component c = pane.getComponentAt(i); |
| | |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void updateUI() { |
| | | } |
| | | |
| | |
| | |
|
| | | /**
|
| | | * Time ago cell renderer with real date tooltip.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class DateCellRenderer extends DefaultTableCellRenderer {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | private final String pattern;
|
| | | |
| | |
|
| | | public DateCellRenderer(String pattern, Color foreground) {
|
| | | this.pattern = (pattern == null ? "yyyy-MM-dd HH:mm" : pattern);
|
| | | setForeground(foreground);
|
| | | setHorizontalAlignment(SwingConstants.CENTER);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
| | |
| | |
|
| | | /**
|
| | | * Dialog to create or edit a Gitblit registration.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class EditRegistrationDialog extends JDialog {
|
| | |
|
| | |
| | | KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
| | | JRootPane rootPane = new JRootPane();
|
| | | rootPane.registerKeyboardAction(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent actionEvent) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | |
|
| | | JButton cancel = new JButton(Translation.get("gb.cancel"));
|
| | | cancel.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | |
|
| | | final JButton save = new JButton(Translation.get(isLogin ? "gb.login" : "gb.save"));
|
| | | save.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | if (validateFields()) {
|
| | | canceled = false;
|
| | |
| | |
|
| | | // on enter in password field, save or login
|
| | | passwordField.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | save.doClick();
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Dialog to create/edit a repository.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class EditRepositoryDialog extends JDialog {
|
| | |
| | | private JCheckBox useDocs;
|
| | |
|
| | | private JCheckBox useIncrementalPushTags;
|
| | | |
| | |
|
| | | private JCheckBox showRemoteBranches;
|
| | |
|
| | | private JCheckBox showReadme;
|
| | |
| | | private JTextField mailingListsField;
|
| | |
|
| | | private JComboBox accessRestriction;
|
| | | |
| | |
|
| | | private JRadioButton allowAuthenticated;
|
| | | |
| | |
|
| | | private JRadioButton allowNamed;
|
| | | |
| | |
|
| | | private JCheckBox allowForks;
|
| | |
|
| | | private JCheckBox verifyCommitter;
|
| | |
| | | private JPalette<String> ownersPalette;
|
| | |
|
| | | private JComboBox headRefField;
|
| | | |
| | |
|
| | | private JComboBox gcPeriod;
|
| | | |
| | |
|
| | | private JTextField gcThreshold;
|
| | | |
| | |
|
| | | private JComboBox maxActivityCommits;
|
| | | |
| | |
|
| | | private RegistrantPermissionsPanel usersPalette;
|
| | |
|
| | | private JPalette<String> setsPalette;
|
| | |
|
| | | private RegistrantPermissionsPanel teamsPalette;
|
| | | |
| | |
|
| | | private JPalette<String> indexedBranchesPalette;
|
| | |
|
| | | private JPalette<String> preReceivePalette;
|
| | |
| | | private JLabel postReceiveInherited;
|
| | |
|
| | | private Set<String> repositoryNames;
|
| | | |
| | |
|
| | | private JPanel customFieldsPanel;
|
| | | |
| | |
|
| | | private List<JTextField> customTextfields;
|
| | |
|
| | | public EditRepositoryDialog(int protocolVersion) {
|
| | |
| | | KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
| | | JRootPane rootPane = new JRootPane();
|
| | | rootPane.registerKeyboardAction(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent actionEvent) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | |
|
| | | if (ArrayUtils.isEmpty(anRepository.availableRefs)) {
|
| | | headRefField = new JComboBox();
|
| | | headRefField.setEnabled(false); |
| | | headRefField.setEnabled(false);
|
| | | } else {
|
| | | headRefField = new JComboBox(
|
| | | anRepository.availableRefs.toArray());
|
| | | headRefField.setSelectedItem(anRepository.HEAD);
|
| | | }
|
| | | |
| | |
|
| | | Integer [] gcPeriods = { 1, 2, 3, 4, 5, 7, 10, 14 };
|
| | | gcPeriod = new JComboBox(gcPeriods);
|
| | | gcPeriod.setSelectedItem(anRepository.gcPeriod);
|
| | | |
| | |
|
| | | gcThreshold = new JTextField(8);
|
| | | gcThreshold.setText(anRepository.gcThreshold);
|
| | |
|
| | |
| | | }
|
| | | }
|
| | | });
|
| | | |
| | | boolean authenticated = anRepository.authorizationControl != null |
| | |
|
| | | boolean authenticated = anRepository.authorizationControl != null
|
| | | && AuthorizationControl.AUTHENTICATED.equals(anRepository.authorizationControl);
|
| | | allowAuthenticated = new JRadioButton(Translation.get("gb.allowAuthenticatedDescription"));
|
| | | allowAuthenticated.setSelected(authenticated);
|
| | | allowAuthenticated.addItemListener(new ItemListener() {
|
| | | @Override
|
| | | public void itemStateChanged(ItemEvent e) {
|
| | | if (e.getStateChange() == ItemEvent.SELECTED) { |
| | | if (e.getStateChange() == ItemEvent.SELECTED) {
|
| | | usersPalette.setEnabled(false);
|
| | | teamsPalette.setEnabled(false);
|
| | | }
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | allowNamed = new JRadioButton(Translation.get("gb.allowNamedDescription"));
|
| | | allowNamed.setSelected(!authenticated);
|
| | | allowNamed.addItemListener(new ItemListener() {
|
| | |
| | | }
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | ButtonGroup group = new ButtonGroup();
|
| | | group.add(allowAuthenticated);
|
| | | group.add(allowNamed);
|
| | | |
| | |
|
| | | JPanel authorizationPanel = new JPanel(new GridLayout(0, 1));
|
| | | authorizationPanel.add(allowAuthenticated);
|
| | | authorizationPanel.add(allowNamed);
|
| | | |
| | |
|
| | | allowForks = new JCheckBox(Translation.get("gb.allowForksDescription"), anRepository.allowForks);
|
| | | verifyCommitter = new JCheckBox(Translation.get("gb.verifyCommitterDescription"), anRepository.verifyCommitter);
|
| | |
|
| | |
| | | JPanel postReceivePanel = new JPanel(new BorderLayout(5, 5));
|
| | | postReceivePanel.add(postReceivePalette, BorderLayout.CENTER);
|
| | | postReceivePanel.add(postReceiveInherited, BorderLayout.WEST);
|
| | | |
| | |
|
| | | customFieldsPanel = new JPanel();
|
| | | customFieldsPanel.setLayout(new BoxLayout(customFieldsPanel, BoxLayout.Y_AXIS));
|
| | | JScrollPane customFieldsScrollPane = new JScrollPane(customFieldsPanel);
|
| | |
| | | }
|
| | | panel.addTab(Translation.get("gb.preReceiveScripts"), preReceivePanel);
|
| | | panel.addTab(Translation.get("gb.postReceiveScripts"), postReceivePanel);
|
| | | |
| | |
|
| | | panel.addTab(Translation.get("gb.customFields"), customFieldsScrollPane);
|
| | | |
| | |
|
| | |
|
| | | setupAccessPermissions(anRepository.accessRestriction);
|
| | |
|
| | | JButton createButton = new JButton(Translation.get("gb.save"));
|
| | | createButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | if (validateFields()) {
|
| | | canceled = false;
|
| | |
| | |
|
| | | JButton cancelButton = new JButton(Translation.get("gb.cancel"));
|
| | | cancelButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | canceled = true;
|
| | | setVisible(false);
|
| | |
| | | pack();
|
| | | nameField.requestFocus();
|
| | | }
|
| | | |
| | |
|
| | | private JPanel newFieldPanel(String label, JComponent comp) {
|
| | | return newFieldPanel(label, 150, comp);
|
| | | }
|
| | |
| | | panel.add(comp);
|
| | | return panel;
|
| | | }
|
| | | |
| | |
|
| | | private void setupAccessPermissions(AccessRestrictionType art) {
|
| | | if (AccessRestrictionType.NONE.equals(art)) {
|
| | | usersPalette.setEnabled(false);
|
| | | teamsPalette.setEnabled(false);
|
| | | |
| | |
|
| | | allowAuthenticated.setEnabled(false);
|
| | | allowNamed.setEnabled(false);
|
| | | verifyCommitter.setEnabled(false);
|
| | |
| | | allowAuthenticated.setEnabled(true);
|
| | | allowNamed.setEnabled(true);
|
| | | verifyCommitter.setEnabled(true);
|
| | | |
| | |
|
| | | if (allowNamed.isSelected()) {
|
| | | usersPalette.setEnabled(true);
|
| | | teamsPalette.setEnabled(true);
|
| | |
| | | repository.skipSizeCalculation = skipSizeCalculation.isSelected();
|
| | | repository.skipSummaryMetrics = skipSummaryMetrics.isSelected();
|
| | | repository.maxActivityCommits = (Integer) maxActivityCommits.getSelectedItem();
|
| | | |
| | |
|
| | | repository.isFrozen = isFrozen.isSelected();
|
| | | repository.allowForks = allowForks.isSelected();
|
| | | repository.verifyCommitter = verifyCommitter.isSelected();
|
| | |
| | |
|
| | | repository.accessRestriction = (AccessRestrictionType) accessRestriction
|
| | | .getSelectedItem();
|
| | | repository.authorizationControl = allowAuthenticated.isSelected() ? |
| | | repository.authorizationControl = allowAuthenticated.isSelected() ?
|
| | | AuthorizationControl.AUTHENTICATED : AuthorizationControl.NAMED;
|
| | | repository.federationStrategy = (FederationStrategy) federationStrategy
|
| | | .getSelectedItem();
|
| | |
| | | if (repository.federationStrategy.exceeds(FederationStrategy.EXCLUDE)) {
|
| | | repository.federationSets = setsPalette.getSelections();
|
| | | }
|
| | | |
| | |
|
| | | repository.indexedBranches = indexedBranchesPalette.getSelections();
|
| | | repository.preReceiveScripts = preReceivePalette.getSelections();
|
| | | repository.postReceiveScripts = postReceivePalette.getSelections();
|
| | | |
| | |
|
| | | // Custom Fields
|
| | | repository.customFields = new LinkedHashMap<String, String>();
|
| | | if (customTextfields != null) {
|
| | |
| | | JOptionPane.showMessageDialog(EditRepositoryDialog.this, message,
|
| | | Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
|
| | | }
|
| | | |
| | |
|
| | | public void setAccessRestriction(AccessRestrictionType restriction) {
|
| | | this.accessRestriction.setSelectedItem(restriction);
|
| | | setupAccessPermissions(restriction);
|
| | |
| | | public void setFederationSets(List<String> all, List<String> selected) {
|
| | | setsPalette.setObjects(all, selected);
|
| | | }
|
| | | |
| | |
|
| | | public void setIndexedBranches(List<String> all, List<String> selected) {
|
| | | indexedBranchesPalette.setObjects(all, selected);
|
| | | }
|
| | |
| | | public List<RegistrantAccessPermission> getTeamAccessPermissions() {
|
| | | return teamsPalette.getPermissions();
|
| | | }
|
| | | |
| | |
|
| | | public void setCustomFields(RepositoryModel repository, Map<String, String> customFields) {
|
| | | customFieldsPanel.removeAll();
|
| | | customTextfields = new ArrayList<JTextField>();
|
| | | |
| | |
|
| | | final Insets insets = new Insets(5, 5, 5, 5);
|
| | | JPanel fields = new JPanel(new GridLayout(0, 1, 0, 5)) {
|
| | |
|
| | |
| | | public Insets getInsets() {
|
| | | return insets;
|
| | | }
|
| | | }; |
| | | |
| | | };
|
| | |
|
| | | for (Map.Entry<String, String> entry : customFields.entrySet()) {
|
| | | String field = entry.getKey();
|
| | | String value = "";
|
| | |
| | | }
|
| | | JTextField textField = new JTextField(value);
|
| | | textField.setName(field);
|
| | | |
| | |
|
| | | textField.setPreferredSize(new Dimension(450, 26));
|
| | | |
| | |
|
| | | fields.add(newFieldPanel(entry.getValue(), 250, textField));
|
| | | |
| | |
|
| | | customTextfields.add(textField);
|
| | | }
|
| | | JScrollPane jsp = new JScrollPane(fields); |
| | | JScrollPane jsp = new JScrollPane(fields);
|
| | | jsp.getVerticalScrollBar().setBlockIncrement(100);
|
| | | jsp.getVerticalScrollBar().setUnitIncrement(100);
|
| | | jsp.setViewportBorder(null);
|
| | |
| | | /**
|
| | | * ListCellRenderer to display descriptive text about the access
|
| | | * restriction.
|
| | | * |
| | | *
|
| | | */
|
| | | private class AccessRestrictionRenderer extends DefaultListCellRenderer {
|
| | |
|
| | |
| | | public Component getListCellRendererComponent(JList list, Object value,
|
| | | int index, boolean isSelected, boolean cellHasFocus) {
|
| | | super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
| | | |
| | |
|
| | | if (value instanceof AccessRestrictionType) {
|
| | | AccessRestrictionType restriction = (AccessRestrictionType) value;
|
| | | switch (restriction) {
|
| | |
| | | private boolean canceled = true;
|
| | |
|
| | | private JTextField teamnameField;
|
| | | |
| | |
|
| | | private JCheckBox canAdminCheckbox;
|
| | | |
| | |
|
| | | private JCheckBox canForkCheckbox;
|
| | | |
| | |
|
| | | private JCheckBox canCreateCheckbox;
|
| | |
|
| | | private JTextField mailingListsField;
|
| | |
| | | KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
| | | JRootPane rootPane = new JRootPane();
|
| | | rootPane.registerKeyboardAction(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent actionEvent) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | | private void initialize(int protocolVersion, TeamModel aTeam) {
|
| | | teamnameField = new JTextField(aTeam.name == null ? "" : aTeam.name, 25);
|
| | |
|
| | | canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), aTeam.canAdmin); |
| | | canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), aTeam.canAdmin);
|
| | | canForkCheckbox = new JCheckBox(Translation.get("gb.canForkDescription"), aTeam.canFork);
|
| | | canCreateCheckbox = new JCheckBox(Translation.get("gb.canCreateDescription"), aTeam.canCreate);
|
| | |
|
| | |
| | | repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY);
|
| | | userPalette = new JPalette<String>();
|
| | | userPalette.setEnabled(settings.supportsTeamMembershipChanges);
|
| | | |
| | |
|
| | | JPanel fieldsPanelTop = new JPanel(new BorderLayout());
|
| | | fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);
|
| | |
|
| | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return _insets;
|
| | | }
|
| | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return _insets;
|
| | | }
|
| | |
| | | JPanel preReceivePanel = new JPanel(new BorderLayout(5, 5));
|
| | | preReceivePanel.add(preReceivePalette, BorderLayout.CENTER);
|
| | | preReceivePanel.add(preReceiveInherited, BorderLayout.WEST);
|
| | | |
| | |
|
| | | postReceivePalette = new JPalette<String>(true);
|
| | | postReceiveInherited = new JLabel();
|
| | | JPanel postReceivePanel = new JPanel(new BorderLayout(5, 5));
|
| | |
| | |
|
| | | JButton createButton = new JButton(Translation.get("gb.save"));
|
| | | createButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | if (validateFields()) {
|
| | | canceled = false;
|
| | |
| | |
|
| | | JButton cancelButton = new JButton(Translation.get("gb.cancel"));
|
| | | cancelButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | canceled = true;
|
| | | setVisible(false);
|
| | |
| | | if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)
|
| | | && repo.authorizationControl.equals(AuthorizationControl.NAMED)) {
|
| | | restricted.add(repo.name);
|
| | | } |
| | | }
|
| | | }
|
| | | StringUtils.sortRepositorynames(restricted);
|
| | | |
| | |
|
| | | List<String> list = new ArrayList<String>();
|
| | | // repositories
|
| | | list.add(".*");
|
| | |
| | | // all repositories excluding personal repositories
|
| | | list.add("[^" + prefix + "].*");
|
| | | }
|
| | | |
| | |
|
| | | String lastProject = null;
|
| | | for (String repo : restricted) {
|
| | | String projectPath = StringUtils.getFirstPathElement(repo);
|
| | |
| | | private JPasswordField passwordField;
|
| | |
|
| | | private JPasswordField confirmPasswordField;
|
| | | |
| | |
|
| | | private JTextField displayNameField;
|
| | | |
| | |
|
| | | private JTextField emailAddressField;
|
| | |
|
| | | private JCheckBox canAdminCheckbox;
|
| | | |
| | |
|
| | | private JCheckBox canForkCheckbox;
|
| | | |
| | |
|
| | | private JCheckBox canCreateCheckbox;
|
| | |
|
| | | private JCheckBox notFederatedCheckbox;
|
| | | |
| | |
|
| | | private JTextField organizationalUnitField;
|
| | | |
| | |
|
| | | private JTextField organizationField;
|
| | |
|
| | | private JTextField localityField;
|
| | | |
| | |
|
| | | private JTextField stateProvinceField;
|
| | | |
| | |
|
| | | private JTextField countryCodeField;
|
| | | |
| | |
|
| | | private RegistrantPermissionsPanel repositoryPalette;
|
| | |
|
| | | private JPalette<TeamModel> teamsPalette;
|
| | |
| | | KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
| | | JRootPane rootPane = new JRootPane();
|
| | | rootPane.registerKeyboardAction(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent actionEvent) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | | 25);
|
| | | displayNameField = new JTextField(anUser.displayName == null ? "" : anUser.displayName, 25);
|
| | | emailAddressField = new JTextField(anUser.emailAddress == null ? "" : anUser.emailAddress, 25);
|
| | | canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), anUser.canAdmin); |
| | | canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), anUser.canAdmin);
|
| | | canForkCheckbox = new JCheckBox(Translation.get("gb.canForkDescription"), anUser.canFork);
|
| | | canCreateCheckbox = new JCheckBox(Translation.get("gb.canCreateDescription"), anUser.canCreate);
|
| | | notFederatedCheckbox = new JCheckBox(
|
| | | Translation.get("gb.excludeFromFederationDescription"),
|
| | | anUser.excludeFromFederation);
|
| | | |
| | |
|
| | | organizationalUnitField = new JTextField(anUser.organizationalUnit == null ? "" : anUser.organizationalUnit, 25);
|
| | | organizationField = new JTextField(anUser.organization == null ? "" : anUser.organization, 25);
|
| | | localityField = new JTextField(anUser.locality == null ? "" : anUser.locality, 25);
|
| | | stateProvinceField = new JTextField(anUser.stateProvince == null ? "" : anUser.stateProvince, 25);
|
| | | countryCodeField = new JTextField(anUser.countryCode == null ? "" : anUser.countryCode, 15);
|
| | | |
| | |
|
| | | // credentials are optionally controlled by 3rd-party authentication
|
| | | usernameField.setEnabled(settings.supportsCredentialChanges);
|
| | | passwordField.setEnabled(settings.supportsCredentialChanges);
|
| | |
| | |
|
| | | displayNameField.setEnabled(settings.supportsDisplayNameChanges);
|
| | | emailAddressField.setEnabled(settings.supportsEmailAddressChanges);
|
| | | |
| | |
|
| | | organizationalUnitField.setEnabled(settings.supportsDisplayNameChanges);
|
| | | organizationField.setEnabled(settings.supportsDisplayNameChanges);
|
| | | localityField.setEnabled(settings.supportsDisplayNameChanges);
|
| | |
| | | attributesPanel.add(newFieldPanel(Translation.get("gb.locality") + " (L)", localityField));
|
| | | attributesPanel.add(newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvinceField));
|
| | | attributesPanel.add(newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCodeField));
|
| | | |
| | |
|
| | | final Insets _insets = new Insets(5, 5, 5, 5);
|
| | | repositoryPalette = new RegistrantPermissionsPanel(RegistrantType.REPOSITORY);
|
| | | teamsPalette = new JPalette<TeamModel>();
|
| | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return _insets;
|
| | | }
|
| | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return _insets;
|
| | | }
|
| | |
| | |
|
| | | JButton createButton = new JButton(Translation.get("gb.save"));
|
| | | createButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | if (validateFields()) {
|
| | | canceled = false;
|
| | |
| | |
|
| | | JButton cancelButton = new JButton(Translation.get("gb.cancel"));
|
| | | cancelButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | canceled = true;
|
| | | setVisible(false);
|
| | |
| | | // no change in password
|
| | | user.password = password;
|
| | | }
|
| | | |
| | |
|
| | | user.displayName = displayNameField.getText().trim();
|
| | | user.emailAddress = emailAddressField.getText().trim();
|
| | |
|
| | |
| | | user.locality = localityField.getText().trim();
|
| | | user.stateProvince = stateProvinceField.getText().trim();
|
| | | user.countryCode = countryCodeField.getText().trim();
|
| | | |
| | |
|
| | | for (RegistrantAccessPermission rp : repositoryPalette.getPermissions()) {
|
| | | user.setRepositoryPermission(rp.registrant, rp.permission);
|
| | | }
|
| | |
| | | if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)
|
| | | && repo.authorizationControl.equals(AuthorizationControl.NAMED)) {
|
| | | restricted.add(repo.name);
|
| | | } |
| | | }
|
| | | }
|
| | | repoMap.put(repo.name.toLowerCase(), repo);
|
| | | }
|
| | | StringUtils.sortRepositorynames(restricted);
|
| | | |
| | |
|
| | | List<String> list = new ArrayList<String>();
|
| | | // repositories
|
| | | list.add(".*");
|
| | | |
| | |
|
| | | String prefix;
|
| | | if (settings.hasKey(Keys.git.userRepositoryPrefix)) {
|
| | | prefix = settings.get(Keys.git.userRepositoryPrefix).currentValue;
|
| | |
| | | // all repositories excluding personal repositories
|
| | | list.add("[^" + prefix + "].*");
|
| | | }
|
| | | |
| | |
|
| | | String lastProject = null;
|
| | | for (String repo : restricted) {
|
| | | String projectPath = StringUtils.getFirstPathElement(repo).toLowerCase();
|
| | |
| | | list.remove(rp.registrant.toLowerCase());
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // update owner and missing permissions for editing
|
| | | for (RegistrantAccessPermission permission : permissions) {
|
| | | if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) {
|
| | |
| | | }
|
| | | teamsPalette.setObjects(teams, selected);
|
| | | }
|
| | | |
| | |
|
| | | public UserModel getUser() {
|
| | | if (canceled) {
|
| | | return null;
|
| | |
| | |
|
| | | /**
|
| | | * Table model for a list of retrieved feed entries.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class FeedEntryTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | if (Columns.Date.ordinal() == columnIndex) {
|
| | | return Date.class;
|
| | |
| | | /**
|
| | | * RSS Feeds Panel displays recent entries and launches the browser to view the
|
| | | * commit. commitdiff, or tree of a commit.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class FeedsPanel extends JPanel {
|
| | |
|
| | |
| | | prev.setToolTipText(Translation.get("gb.pagePrevious"));
|
| | | prev.setEnabled(false);
|
| | | prev.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshFeeds(--page);
|
| | | }
|
| | |
| | | next.setToolTipText(Translation.get("gb.pageNext"));
|
| | | next.setEnabled(false);
|
| | | next.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshFeeds(++page);
|
| | | }
|
| | |
| | |
|
| | | JButton refreshFeeds = new JButton(Translation.get("gb.refresh"));
|
| | | refreshFeeds.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshFeeds(0);
|
| | | }
|
| | |
| | | final JButton viewCommit = new JButton(Translation.get("gb.view"));
|
| | | viewCommit.setEnabled(false);
|
| | | viewCommit.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | viewCommit();
|
| | | }
|
| | |
| | | final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff"));
|
| | | viewCommitDiff.setEnabled(false);
|
| | | viewCommitDiff.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | viewCommitDiff();
|
| | | }
|
| | |
| | | final JButton viewTree = new JButton(Translation.get("gb.tree"));
|
| | | viewTree.setEnabled(false);
|
| | | viewTree.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | viewTree();
|
| | | }
|
| | |
| | |
|
| | | JButton subscribeFeeds = new JButton(Translation.get("gb.subscribe") + "...");
|
| | | subscribeFeeds.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | subscribeFeeds(gitblit.getAvailableFeeds());
|
| | | }
|
| | |
| | | table.getColumn(name).setCellRenderer(new MessageRenderer(gitblit));
|
| | |
|
| | | table.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2) {
|
| | | if (e.isControlDown()) {
|
| | |
| | | repositorySelector.setRenderer(nameRenderer);
|
| | | repositorySelector.setForeground(nameRenderer.getForeground());
|
| | | repositorySelector.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | // repopulate the author list based on repository selection
|
| | | // preserve author selection, if possible
|
| | |
| | | authorSelector.setRenderer(nameRenderer);
|
| | | authorSelector.setForeground(nameRenderer.getForeground());
|
| | | authorSelector.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | filterFeeds();
|
| | | }
|
| | |
| | | if (repository.equals(ALL)) {
|
| | | // author filter
|
| | | containsFilter = new RowFilter<FeedEntryTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(
|
| | | Entry<? extends FeedEntryTableModel, ? extends Object> entry) {
|
| | | return entry.getStringValue(authorIndex).equalsIgnoreCase(author);
|
| | |
| | | } else if (author.equals(ALL)) {
|
| | | // repository filter
|
| | | containsFilter = new RowFilter<FeedEntryTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(
|
| | | Entry<? extends FeedEntryTableModel, ? extends Object> entry) {
|
| | | return entry.getStringValue(repositoryIndex).equalsIgnoreCase(repository);
|
| | |
| | | } else {
|
| | | // repository-author filter
|
| | | containsFilter = new RowFilter<FeedEntryTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(
|
| | | Entry<? extends FeedEntryTableModel, ? extends Object> entry) {
|
| | | boolean authorMatch = entry.getStringValue(authorIndex)
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a list of available feeds.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class FeedsTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | Columns col = Columns.values()[columnIndex];
|
| | | switch (col) {
|
| | |
| | | /**
|
| | | * GitblitClient is a object that retrieves data from a Gitblit server, caches
|
| | | * it for local operations, and allows updating or creating Gitblit objects.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitblitClient implements Serializable {
|
| | |
|
| | |
| | | return sb.toString();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public AccessRestrictionType getDefaultAccessRestriction() {
|
| | | String restriction = "PUSH";
|
| | | if (settings.hasKey(Keys.git.defaultAccessRestriction)) {
|
| | |
| | | /**
|
| | | * Returns the list of pre-receive scripts the repository inherited from the
|
| | | * global settings and team affiliations.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * if null only the globally specified scripts are returned
|
| | | * @return a list of scripts
|
| | |
| | | * Returns the list of all available Groovy pre-receive push hook scripts
|
| | | * that are not already inherited by the repository. Script files must have
|
| | | * .groovy extension
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * optional parameter
|
| | | * @return list of available hook scripts
|
| | |
| | | /**
|
| | | * Returns the list of post-receive scripts the repository inherited from
|
| | | * the global settings and team affiliations.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * if null only the globally specified scripts are returned
|
| | | * @return a list of scripts
|
| | |
| | | * Returns the list of unused Groovy post-receive push hook scripts that are
|
| | | * not already inherited by the repository. Script files must have .groovy
|
| | | * extension
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * optional parameter
|
| | | * @return list of available hook scripts
|
| | |
| | |
|
| | | // create list of available scripts by excluding inherited scripts
|
| | | List<String> scripts = new ArrayList<String>();
|
| | | if (!ArrayUtils.isEmpty(settings.pushScripts)) { |
| | | if (!ArrayUtils.isEmpty(settings.pushScripts)) {
|
| | | for (String script : settings.pushScripts) {
|
| | | if (!inherited.contains(script)) {
|
| | | scripts.add(script);
|
| | |
| | | public List<UserModel> getUsers() {
|
| | | return allUsers;
|
| | | }
|
| | | |
| | |
|
| | | public UserModel getUser(String username) {
|
| | | for (UserModel user : getUsers()) {
|
| | | if (user.username.equalsIgnoreCase(username)) {
|
| | |
| | | }
|
| | | return usernames;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the effective list of permissions for this user, taking into account
|
| | | * team memberships, ownerships.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | * @return the effective list of permissions for the user
|
| | | */
|
| | |
| | | set.add(rp);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(set);
|
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) {
|
| | | List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
|
| | | if (AccessRestrictionType.NONE.equals(repository.accessRestriction)) {
|
| | |
| | | }
|
| | | return teamnames;
|
| | | }
|
| | | |
| | |
|
| | | public List<RegistrantAccessPermission> getTeamAccessPermissions(RepositoryModel repository) {
|
| | | List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
|
| | | for (TeamModel team : allTeams) {
|
| | |
| | | public List<RepositoryModel> getRepositories() {
|
| | | return allRepositories;
|
| | | }
|
| | | |
| | |
|
| | | public RepositoryModel getRepository(String name) {
|
| | | for (RepositoryModel repository : allRepositories) {
|
| | | if (repository.name.equalsIgnoreCase(name)) {
|
| | |
| | | public boolean deleteRepository(RepositoryModel repository) throws IOException {
|
| | | return RpcUtils.deleteRepository(repository, url, account, password);
|
| | | }
|
| | | |
| | |
|
| | | public boolean clearRepositoryCache() throws IOException {
|
| | | return RpcUtils.clearRepositoryCache(url, account, password);
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Gitblit Manager issues JSON RPC requests to a Gitblit server.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitblitManager extends JFrame implements RegistrationsDialog.RegistrationListener {
|
| | |
|
| | |
| | | JMenuItem manage = new JMenuItem(Translation.get("gb.manage") + "...");
|
| | | manage.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK, false));
|
| | | manage.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | manageRegistrations();
|
| | | }
|
| | |
| | | item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1 + i, KeyEvent.CTRL_DOWN_MASK,
|
| | | false));
|
| | | item.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | login(reg);
|
| | | }
|
| | |
| | | GitblitRegistration reg = new GitblitRegistration(server, url, account, password) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | protected void cacheFeeds() {
|
| | | writeFeedCache(this);
|
| | | }
|
| | |
| | |
|
| | | public static void main(String[] args) {
|
| | | EventQueue.invokeLater(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | try {
|
| | | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
| | |
| | |
|
| | | /**
|
| | | * Downloads dependencies and launches Gitblit Manager.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitblitManagerLauncher {
|
| | |
|
| | |
| | |
|
| | | public static void main(String[] args) {
|
| | | final SplashScreen splash = SplashScreen.getSplashScreen();
|
| | | |
| | |
|
| | | File libFolder = new File("ext");
|
| | | List<File> jars = findJars(libFolder.getAbsoluteFile());
|
| | | |
| | |
|
| | | // sort the jars by name and then reverse the order so the newer version
|
| | | // of the library gets loaded in the event that this is an upgrade
|
| | | Collections.sort(jars);
|
| | |
| | |
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | updateSplash(splash, Translation.get("gb.starting") + " Gitblit Manager...");
|
| | | GitblitManager.main(args);
|
| | | }
|
| | |
| | | }
|
| | | try {
|
| | | EventQueue.invokeAndWait(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | Graphics2D g = splash.createGraphics();
|
| | | if (g != null) {
|
| | | // Splash is 320x120
|
| | | FontMetrics fm = g.getFontMetrics();
|
| | | |
| | |
|
| | | // paint startup status
|
| | | g.setColor(Color.darkGray);
|
| | | int h = fm.getHeight() + fm.getMaxDescent();
|
| | |
| | | g.setColor(Color.WHITE);
|
| | | int xw = fm.stringWidth(string);
|
| | | g.drawString(string, x + ((w - xw) / 2), y - 5);
|
| | | |
| | |
|
| | | // paint version
|
| | | String ver = "v" + Constants.getVersion();
|
| | | int vw = g.getFontMetrics().stringWidth(ver);
|
| | |
| | | t.printStackTrace();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static List<File> findJars(File folder) {
|
| | | List<File> jars = new ArrayList<File>();
|
| | | if (folder.exists()) {
|
| | |
| | |
|
| | | /**
|
| | | * Adds a file to the classpath
|
| | | * |
| | | *
|
| | | * @param f
|
| | | * the file to be added
|
| | | * @throws IOException
|
| | |
| | |
|
| | | /**
|
| | | * GitblitPanel is a container for the repository, users, settings, etc panels.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitblitPanel extends JPanel implements CloseTabListener {
|
| | |
|
| | |
| | | private FeedsPanel feedsPanel;
|
| | |
|
| | | private UsersPanel usersPanel;
|
| | | |
| | |
|
| | | private TeamsPanel teamsPanel;
|
| | |
|
| | | private SettingsPanel settingsPanel;
|
| | |
| | | tabs.addTab(Translation.get("gb.settings"), createSettingsPanel());
|
| | | tabs.addTab(Translation.get("gb.status"), createStatusPanel());
|
| | | tabs.addChangeListener(new ChangeListener() {
|
| | | @Override
|
| | | public void stateChanged(ChangeEvent e) {
|
| | | tabs.getSelectedComponent().requestFocus();
|
| | | }
|
| | |
| | | protected void updateUsersTable() {
|
| | | usersPanel.updateTable(false);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void updateTeamsTable() {
|
| | | teamsPanel.updateTable(false);
|
| | |
| | |
|
| | | private JPanel createUsersPanel() {
|
| | | usersPanel = new UsersPanel(gitblit) {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | @Override
|
| | | protected void updateTeamsTable() {
|
| | | teamsPanel.updateTable(false);
|
| | |
| | | };
|
| | | return usersPanel;
|
| | | }
|
| | | |
| | |
|
| | | private JPanel createTeamsPanel() {
|
| | | teamsPanel = new TeamsPanel(gitblit) {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | |
| | | }
|
| | | };
|
| | | return teamsPanel;
|
| | | } |
| | | }
|
| | |
|
| | | private JPanel createSettingsPanel() {
|
| | | settingsPanel = new SettingsPanel(gitblit);
|
| | |
| | |
|
| | | /**
|
| | | * Simple class to encapsulate a Gitblit server registration.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitblitRegistration implements Serializable, Comparable<GitblitRegistration> {
|
| | |
|
| | |
| | | return doRequest();
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void done() {
|
| | | parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
| | | try {
|
| | |
| | | /**
|
| | | * Renders the type indicators (tickets, frozen, access restriction, etc) in a
|
| | | * single cell.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class IndicatorsRenderer extends JPanel implements TableCellRenderer, Serializable {
|
| | |
|
| | |
| | | private final ImageIcon frozenIcon;
|
| | |
|
| | | private final ImageIcon federatedIcon;
|
| | | |
| | |
|
| | | private final ImageIcon forkIcon;
|
| | | |
| | |
|
| | | private final ImageIcon sparkleshareIcon;
|
| | |
|
| | | public IndicatorsRenderer() {
|
| | |
| | |
|
| | | add = new JButton("->");
|
| | | add.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | List<T> move = new ArrayList<T>();
|
| | | if (available.getSelectedRowCount() <= 0) {
|
| | |
| | | }
|
| | | for (int row : available.getSelectedRows()) {
|
| | | int modelIndex = available.convertRowIndexToModel(row);
|
| | | T item = (T) availableModel.list.get(modelIndex);
|
| | | T item = availableModel.list.get(modelIndex);
|
| | | move.add(item);
|
| | | }
|
| | | availableModel.list.removeAll(move);
|
| | |
| | | });
|
| | | subtract = new JButton("<-");
|
| | | subtract.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | List<T> move = new ArrayList<T>();
|
| | | if (selected.getSelectedRowCount() <= 0) {
|
| | |
| | | }
|
| | | for (int row : selected.getSelectedRows()) {
|
| | | int modelIndex = selected.convertRowIndexToModel(row);
|
| | | T item = (T) selectedModel.list.get(modelIndex);
|
| | | T item = selectedModel.list.get(modelIndex);
|
| | | move.add(item);
|
| | | }
|
| | | selectedModel.list.removeAll(move);
|
| | |
| | |
|
| | | up = new JButton("\u2191");
|
| | | up.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | int row = selected.getSelectedRow();
|
| | | if (row > 0) {
|
| | |
| | |
|
| | | down = new JButton("\u2193");
|
| | | down.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | int row = selected.getSelectedRow();
|
| | | if (row < selected.getRowCount() - 1) {
|
| | |
| | | panel.add(jsp, BorderLayout.CENTER);
|
| | | return panel;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public void setEnabled(boolean enabled) {
|
| | | super.setEnabled(enabled);
|
| | |
| | | return Translation.get("gb.name");
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | return String.class;
|
| | | }
|
| | |
| | | /**
|
| | | * Message renderer displays the short log message and then any refs in a style
|
| | | * like the site.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class MessageRenderer extends JPanel implements TableCellRenderer, Serializable {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | private final GitblitClient gitblit;
|
| | | |
| | |
|
| | | private final ImageIcon mergeIcon;
|
| | | |
| | |
|
| | | private final ImageIcon blankIcon;
|
| | | |
| | |
|
| | | private final JLabel messageLabel;
|
| | |
|
| | | private final JLabel headLabel;
|
| | |
| | | public MessageRenderer(GitblitClient gitblit) {
|
| | | super(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, 1));
|
| | | this.gitblit = gitblit;
|
| | | |
| | |
|
| | | mergeIcon = new ImageIcon(getClass().getResource("/commit_merge_16x16.png"));
|
| | | blankIcon = new ImageIcon(getClass().getResource("/blank.png"));
|
| | |
|
| | | messageLabel = new JLabel();
|
| | | |
| | |
|
| | | headLabel = newRefLabel();
|
| | | branchLabel = newRefLabel();
|
| | | remoteLabel = newRefLabel();
|
| | |
| | | add(tagLabel);
|
| | | }
|
| | |
|
| | | private JLabel newRefLabel() { |
| | | private JLabel newRefLabel() {
|
| | | JLabel label = new JLabel();
|
| | | label.setOpaque(true);
|
| | | Font font = label.getFont();
|
| | |
| | | label.setVisible(true);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | if (isSelected)
|
| | |
| | | /**
|
| | | * Repository name cell renderer. This renderer shows the group name in a gray
|
| | | * color and accentuates the repository name in a cornflower blue color.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class NameRenderer extends DefaultTableCellRenderer implements ListCellRenderer {
|
| | |
|
| | |
| | | return sb.toString();
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a map of properties.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class PropertiesTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | return String.class;
|
| | | }
|
| | |
| | | public class RegistrantPermissionsPanel extends JPanel {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | private JTable permissionsTable;
|
| | |
|
| | | private RegistrantPermissionsTableModel tableModel;
|
| | |
| | | permissionsTable = Utils.newTable(tableModel, Utils.DATE_FORMAT, new RowRenderer() {
|
| | | Color clear = new Color(0, 0, 0, 0);
|
| | | Color iceGray = new Color(0xf0, 0xf0, 0xf0);
|
| | | |
| | |
|
| | | @Override
|
| | | public void prepareRow(Component c, boolean isSelected, int row, int column) {
|
| | | if (isSelected) {
|
| | |
| | | permissionsTable.setPreferredScrollableViewportSize(new Dimension(400, 150));
|
| | | JScrollPane jsp = new JScrollPane(permissionsTable);
|
| | | add(jsp, BorderLayout.CENTER);
|
| | | |
| | |
|
| | | permissionsTable.getColumnModel().getColumn(RegistrantPermissionsTableModel.Columns.Registrant.ordinal())
|
| | | .setCellRenderer(new NameRenderer());
|
| | | permissionsTable.getColumnModel().getColumn(RegistrantPermissionsTableModel.Columns.Type.ordinal())
|
| | | .setCellRenderer(new PermissionTypeRenderer());
|
| | | permissionsTable.getColumnModel().getColumn(RegistrantPermissionsTableModel.Columns.Permission.ordinal())
|
| | | .setCellEditor(new AccessPermissionEditor());
|
| | | |
| | |
|
| | | registrantModel = new DefaultComboBoxModel();
|
| | | registrantSelector = new JComboBox(registrantModel);
|
| | | permissionSelector = new JComboBox(AccessPermission.NEWPERMISSIONS);
|
| | | addButton = new JButton(Translation.get("gb.add"));
|
| | | addButton.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | if (registrantSelector.getSelectedIndex() < 0) {
|
| | | return;
|
| | |
| | | if (permissionSelector.getSelectedIndex() < 0) {
|
| | | return;
|
| | | }
|
| | | |
| | |
|
| | | RegistrantAccessPermission rp = new RegistrantAccessPermission(registrantType);
|
| | | rp.registrant = registrantSelector.getSelectedItem().toString();
|
| | | rp.permission = (AccessPermission) permissionSelector.getSelectedItem();
|
| | |
| | | tableModel.permissions.add(rp);
|
| | | // resort permissions after insert to convey idea of eval order
|
| | | Collections.sort(tableModel.permissions);
|
| | | |
| | |
|
| | | registrantModel.removeElement(rp.registrant);
|
| | | registrantSelector.setSelectedIndex(-1);
|
| | | registrantSelector.invalidate();
|
| | | addPanel.setVisible(registrantModel.getSize() > 0);
|
| | | |
| | |
|
| | | tableModel.fireTableDataChanged();
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | addPanel = new JPanel();
|
| | | addPanel.add(registrantSelector);
|
| | | addPanel.add(permissionSelector);
|
| | |
| | | registrantModel.addElement(registrant);
|
| | | }
|
| | | tableModel.setPermissions(permissions);
|
| | | |
| | |
|
| | | registrantSelector.setSelectedIndex(-1);
|
| | | permissionSelector.setSelectedIndex(-1);
|
| | | addPanel.setVisible(filtered.size() > 0);
|
| | |
| | | public List<RegistrantAccessPermission> getPermissions() {
|
| | | return tableModel.permissions;
|
| | | }
|
| | | |
| | |
|
| | | private class AccessPermissionEditor extends DefaultCellEditor {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | public AccessPermissionEditor() {
|
| | | super(new JComboBox(AccessPermission.values())); |
| | | super(new JComboBox(AccessPermission.values()));
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private class PermissionTypeRenderer extends DefaultTableCellRenderer {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a registrant permissions.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RegistrantPermissionsTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | if (columnIndex == Columns.Permission.ordinal()) {
|
| | | return AccessPermission.class;
|
| | |
| | | }
|
| | | return String.class;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean isCellEditable(int rowIndex, int columnIndex) {
|
| | | if (columnIndex == Columns.Permission.ordinal()) {
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public void setValueAt(Object o, int rowIndex, int columnIndex) {
|
| | | RegistrantAccessPermission rp = permissions.get(rowIndex);
|
| | |
| | | /**
|
| | | * Displays a list of registrations and allows management of server
|
| | | * registrations.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RegistrationsDialog extends JDialog {
|
| | |
|
| | |
| | | KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
| | | JRootPane rootPane = new JRootPane();
|
| | | rootPane.registerKeyboardAction(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent actionEvent) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | | .getColumnName(RegistrationsTableModel.Columns.Name.ordinal());
|
| | | registrationsTable.getColumn(id).setCellRenderer(nameRenderer);
|
| | | registrationsTable.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2) {
|
| | | login();
|
| | |
| | |
|
| | | final JButton create = new JButton(Translation.get("gb.create"));
|
| | | create.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | create();
|
| | | }
|
| | |
| | | final JButton login = new JButton(Translation.get("gb.login"));
|
| | | login.setEnabled(false);
|
| | | login.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | login();
|
| | | }
|
| | |
| | | final JButton edit = new JButton(Translation.get("gb.edit"));
|
| | | edit.setEnabled(false);
|
| | | edit.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | edit();
|
| | | }
|
| | |
| | | final JButton delete = new JButton(Translation.get("gb.delete"));
|
| | | delete.setEnabled(false);
|
| | | delete.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | delete();
|
| | | }
|
| | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return insets;
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a list of Gitblit server registrations.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RegistrationsTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | if (columnIndex == Columns.Last_Login.ordinal()) {
|
| | | return Date.class;
|
| | |
| | | /**
|
| | | * RSS Feeds Panel displays recent entries and launches the browser to view the
|
| | | * commit. commitdiff, or tree of a commit.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class RepositoriesPanel extends JPanel {
|
| | |
|
| | |
| | | final JButton browseRepository = new JButton(Translation.get("gb.browse"));
|
| | | browseRepository.setEnabled(false);
|
| | | browseRepository.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | RepositoryModel model = getSelectedRepositories().get(0);
|
| | | String url = gitblit.getURL("summary", model.name, null);
|
| | |
| | |
|
| | | JButton refreshRepositories = new JButton(Translation.get("gb.refresh"));
|
| | | refreshRepositories.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshRepositories();
|
| | | }
|
| | | });
|
| | | |
| | |
|
| | | clearCache = new JButton(Translation.get("gb.clearCache"));
|
| | | clearCache.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | clearCache();
|
| | | }
|
| | |
| | |
|
| | | createRepository = new JButton(Translation.get("gb.create"));
|
| | | createRepository.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | createRepository();
|
| | | }
|
| | |
| | | editRepository = new JButton(Translation.get("gb.edit"));
|
| | | editRepository.setEnabled(false);
|
| | | editRepository.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | editRepository(getSelectedRepositories().get(0));
|
| | | }
|
| | |
| | | delRepository = new JButton(Translation.get("gb.delete"));
|
| | | delRepository.setEnabled(false);
|
| | | delRepository.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | deleteRepositories(getSelectedRepositories());
|
| | | }
|
| | |
| | | final JButton subscribeRepository = new JButton(Translation.get("gb.subscribe") + "...");
|
| | | subscribeRepository.setEnabled(false);
|
| | | subscribeRepository.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | List<FeedModel> feeds = gitblit.getAvailableFeeds(getSelectedRepositories().get(0));
|
| | | subscribeFeeds(feeds);
|
| | |
| | | final JButton logRepository = new JButton(Translation.get("gb.log") + "...");
|
| | | logRepository.setEnabled(false);
|
| | | logRepository.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | RepositoryModel model = getSelectedRepositories().get(0);
|
| | | showSearchDialog(false, model);
|
| | |
| | | final JButton searchRepository = new JButton(Translation.get("gb.search") + "...");
|
| | | searchRepository.setEnabled(false);
|
| | | searchRepository.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | RepositoryModel model = getSelectedRepositories().get(0);
|
| | | showSearchDialog(true, model);
|
| | |
| | | });
|
| | |
|
| | | table.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2 && gitblit.allowManagement()) {
|
| | | editRepository(getSelectedRepositories().get(0));
|
| | |
| | |
|
| | | filterTextfield = new JTextField();
|
| | | filterTextfield.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | filterRepositories(filterTextfield.getText());
|
| | | }
|
| | | });
|
| | | filterTextfield.addKeyListener(new KeyAdapter() {
|
| | | @Override
|
| | | public void keyReleased(KeyEvent e) {
|
| | | filterRepositories(filterTextfield.getText());
|
| | | }
|
| | |
| | | return;
|
| | | }
|
| | | RowFilter<RepositoriesTableModel, Object> containsFilter = new RowFilter<RepositoriesTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(Entry<? extends RepositoriesTableModel, ? extends Object> entry) {
|
| | | for (int i = entry.getValueCount() - 1; i >= 0; i--) {
|
| | | if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
|
| | |
| | | };
|
| | | worker.execute();
|
| | | }
|
| | | |
| | |
|
| | | protected void clearCache() {
|
| | | GitblitWorker worker = new GitblitWorker(RepositoriesPanel.this,
|
| | | RpcRequest.CLEAR_REPOSITORY_CACHE) {
|
| | |
| | | /**
|
| | | * Displays the create repository dialog and fires a SwingWorker to update
|
| | | * the server, if appropriate.
|
| | | * |
| | | *
|
| | | */
|
| | | protected void createRepository() {
|
| | | EditRepositoryDialog dialog = new EditRepositoryDialog(gitblit.getProtocolVersion());
|
| | |
| | | /**
|
| | | * Displays the edit repository dialog and fires a SwingWorker to update the
|
| | | * server, if appropriate.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | */
|
| | | protected void editRepository(final RepositoryModel repository) {
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a list of repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RepositoriesTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | Columns col = Columns.values()[columnIndex];
|
| | | switch (col) {
|
| | |
| | | /**
|
| | | * The search dialog allows searching of a repository branch. This matches the
|
| | | * search implementation of the site.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SearchDialog extends JFrame {
|
| | |
|
| | |
| | | prev.setToolTipText(Translation.get("gb.pagePrevious"));
|
| | | prev.setEnabled(false);
|
| | | prev.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | search(--page);
|
| | | }
|
| | |
| | | next.setToolTipText(Translation.get("gb.pageNext"));
|
| | | next.setEnabled(false);
|
| | | next.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | search(++page);
|
| | | }
|
| | |
| | |
|
| | | final JButton search = new JButton(Translation.get(isSearch ? "gb.search" : "gb.refresh"));
|
| | | search.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | search(0);
|
| | | }
|
| | |
| | | final JButton viewCommit = new JButton(Translation.get("gb.view"));
|
| | | viewCommit.setEnabled(false);
|
| | | viewCommit.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | viewCommit();
|
| | | }
|
| | |
| | | final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff"));
|
| | | viewCommitDiff.setEnabled(false);
|
| | | viewCommitDiff.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | viewCommitDiff();
|
| | | }
|
| | |
| | | final JButton viewTree = new JButton(Translation.get("gb.tree"));
|
| | | viewTree.setEnabled(false);
|
| | | viewTree.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | viewTree();
|
| | | }
|
| | |
| | | table.getColumn(name).setCellRenderer(new MessageRenderer());
|
| | |
|
| | | table.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2) {
|
| | | if (e.isControlDown()) {
|
| | |
| | | repositorySelector.setRenderer(nameRenderer);
|
| | | repositorySelector.setForeground(nameRenderer.getForeground());
|
| | | repositorySelector.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | // repopulate the branch list based on repository selection
|
| | | // preserve branch selection, if possible
|
| | |
| | |
|
| | | searchFragment = new JTextField(25);
|
| | | searchFragment.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | search(0);
|
| | | }
|
| | |
| | | /**
|
| | | * SettingModel cell renderer that indicates if a setting is the default or
|
| | | * modified.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SettingCellRenderer extends DefaultTableCellRenderer {
|
| | |
|
| | |
| | | modifiedFont = defaultFont.deriveFont(Font.BOLD);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
| | |
| | |
|
| | | /**
|
| | | * This panel displays the metadata for a particular setting.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class SettingPanel extends JPanel {
|
| | |
| | | /**
|
| | | * Settings panel displays a list of server settings and their associated
|
| | | * metadata. This panel also allows editing of a setting.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SettingsPanel extends JPanel {
|
| | |
|
| | |
| | | private void initialize() {
|
| | | JButton refreshSettings = new JButton(Translation.get("gb.refresh"));
|
| | | refreshSettings.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshSettings();
|
| | | }
|
| | |
| | |
|
| | | final JButton editSetting = new JButton(Translation.get("gb.edit"));
|
| | | editSetting.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | int viewRow = table.getSelectedRow();
|
| | | int modelRow = table.convertRowIndexToModel(viewRow);
|
| | |
| | | }
|
| | | });
|
| | | table.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2) {
|
| | | int viewRow = table.getSelectedRow();
|
| | |
| | |
|
| | | filterTextfield = new JTextField();
|
| | | filterTextfield.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | filterSettings(filterTextfield.getText());
|
| | | }
|
| | | });
|
| | | filterTextfield.addKeyListener(new KeyAdapter() {
|
| | | @Override
|
| | | public void keyReleased(KeyEvent e) {
|
| | | filterSettings(filterTextfield.getText());
|
| | | }
|
| | |
| | | add(settingsTablePanel, BorderLayout.CENTER);
|
| | | add(settingsControls, BorderLayout.SOUTH);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public void requestFocus() {
|
| | | filterTextfield.requestFocus();
|
| | |
| | | return;
|
| | | }
|
| | | RowFilter<SettingsTableModel, Object> containsFilter = new RowFilter<SettingsTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(Entry<? extends SettingsTableModel, ? extends Object> entry) {
|
| | | for (int i = entry.getValueCount() - 1; i >= 0; i--) {
|
| | | if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
|
| | |
| | |
|
| | | /**
|
| | | * Table model of Map<String, SettingModel>.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SettingsTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | if (Columns.Value.ordinal() == columnIndex) {
|
| | | return SettingModel.class;
|
| | |
| | |
|
| | | /**
|
| | | * This panel displays the server status.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class StatusPanel extends JPanel {
|
| | |
| | | private void initialize() {
|
| | | JButton refreshStatus = new JButton(Translation.get("gb.refresh"));
|
| | | refreshStatus.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshStatus();
|
| | | }
|
| | |
| | | ServerStatus status = gitblit.getStatus();
|
| | | header.setText(Translation.get("gb.status"));
|
| | | version.setText(Constants.NAME + (status.isGO ? " GO v" : " WAR v") + status.version);
|
| | | releaseDate.setText(status.releaseDate); |
| | | releaseDate.setText(status.releaseDate);
|
| | | bootDate.setText(status.bootDate.toString() + " (" + Translation.getTimeUtils().timeAgo(status.bootDate)
|
| | | + ")");
|
| | | url.setText(gitblit.url);
|
| | |
| | | /**
|
| | | * Displays a subscribed icon on the left of the repository name, if there is at
|
| | | * least one subscribed branch.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SubscribedRepositoryRenderer extends NameRenderer {
|
| | |
|
| | |
| | | subscribedIcon = new ImageIcon(getClass().getResource("/bullet_feed.png"));
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
| | | boolean hasFocus, int row, int column) {
|
| | | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
| | |
| | | /**
|
| | | * Displays a list of repository branches and allows the user to check or
|
| | | * uncheck branches.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class SubscriptionsDialog extends JDialog {
|
| | |
|
| | |
| | | KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
| | | JRootPane rootPane = new JRootPane();
|
| | | rootPane.registerKeyboardAction(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent actionEvent) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | |
|
| | | final JButton cancel = new JButton(Translation.get("gb.cancel"));
|
| | | cancel.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | setVisible(false);
|
| | | }
|
| | |
| | |
|
| | | final JButton save = new JButton(Translation.get("gb.save"));
|
| | | save.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent event) {
|
| | | save();
|
| | | }
|
| | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public Insets getInsets() {
|
| | | return insets;
|
| | | }
|
| | |
| | | /**
|
| | | * Users panel displays a list of user accounts and allows management of those
|
| | | * accounts.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class TeamsPanel extends JPanel {
|
| | |
|
| | |
| | | private void initialize() {
|
| | | JButton refreshTeams = new JButton(Translation.get("gb.refresh"));
|
| | | refreshTeams.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshTeams();
|
| | | }
|
| | |
| | |
|
| | | JButton createTeam = new JButton(Translation.get("gb.create"));
|
| | | createTeam.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | createTeam();
|
| | | }
|
| | |
| | | final JButton editTeam = new JButton(Translation.get("gb.edit"));
|
| | | editTeam.setEnabled(false);
|
| | | editTeam.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | editTeam(getSelectedTeams().get(0));
|
| | | }
|
| | |
| | | final JButton delTeam = new JButton(Translation.get("gb.delete"));
|
| | | delTeam.setEnabled(false);
|
| | | delTeam.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | deleteTeams(getSelectedTeams());
|
| | | }
|
| | |
| | | });
|
| | |
|
| | | table.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2) {
|
| | | editTeam(getSelectedTeams().get(0));
|
| | |
| | |
|
| | | filterTextfield = new JTextField();
|
| | | filterTextfield.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | filterTeams(filterTextfield.getText());
|
| | | }
|
| | | });
|
| | | filterTextfield.addKeyListener(new KeyAdapter() {
|
| | | @Override
|
| | | public void keyReleased(KeyEvent e) {
|
| | | filterTeams(filterTextfield.getText());
|
| | | }
|
| | |
| | | return;
|
| | | }
|
| | | RowFilter<TeamsTableModel, Object> containsFilter = new RowFilter<TeamsTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(Entry<? extends TeamsTableModel, ? extends Object> entry) {
|
| | | for (int i = entry.getValueCount() - 1; i >= 0; i--) {
|
| | | if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
|
| | |
| | | /**
|
| | | * Displays the create team dialog and fires a SwingWorker to update the
|
| | | * server, if appropriate.
|
| | | * |
| | | *
|
| | | */
|
| | | protected void createTeam() {
|
| | | EditTeamDialog dialog = new EditTeamDialog(gitblit.getProtocolVersion(),
|
| | |
| | | /**
|
| | | * Displays the edit team dialog and fires a SwingWorker to update the
|
| | | * server, if appropriate.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | */
|
| | | protected void editTeam(final TeamModel team) {
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a list of teams.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class TeamsTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | return String.class;
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Loads the Gitblit language resource file.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class Translation {
|
| | |
|
| | | private final static ResourceBundle translation;
|
| | | |
| | |
|
| | | private final static TimeUtils timeUtils;
|
| | |
|
| | | static {
|
| | |
| | | bundle = ResourceBundle.getBundle("GitBlitWebApp");
|
| | | }
|
| | | translation = bundle;
|
| | | |
| | |
|
| | | timeUtils = new TimeUtils(translation, null);
|
| | | }
|
| | |
|
| | |
| | | }
|
| | | return key;
|
| | | }
|
| | | |
| | |
|
| | | public static TimeUtils getTimeUtils() {
|
| | | return timeUtils;
|
| | | }
|
| | |
| | | /**
|
| | | * Users panel displays a list of user accounts and allows management of those
|
| | | * accounts.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class UsersPanel extends JPanel {
|
| | |
|
| | |
| | | private void initialize() {
|
| | | JButton refreshUsers = new JButton(Translation.get("gb.refresh"));
|
| | | refreshUsers.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | refreshUsers();
|
| | | }
|
| | |
| | |
|
| | | JButton createUser = new JButton(Translation.get("gb.create"));
|
| | | createUser.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | createUser();
|
| | | }
|
| | |
| | | final JButton editUser = new JButton(Translation.get("gb.edit"));
|
| | | editUser.setEnabled(false);
|
| | | editUser.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | editUser(getSelectedUsers().get(0));
|
| | | }
|
| | |
| | | final JButton delUser = new JButton(Translation.get("gb.delete"));
|
| | | delUser.setEnabled(false);
|
| | | delUser.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | deleteUsers(getSelectedUsers());
|
| | | }
|
| | |
| | | table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
|
| | | String name = table.getColumnName(UsersTableModel.Columns.Name.ordinal());
|
| | | table.getColumn(name).setCellRenderer(nameRenderer);
|
| | | |
| | |
|
| | | int w = 130;
|
| | | name = table.getColumnName(UsersTableModel.Columns.Type.ordinal());
|
| | | table.getColumn(name).setMinWidth(w);
|
| | |
| | | name = table.getColumnName(UsersTableModel.Columns.Repositories.ordinal());
|
| | | table.getColumn(name).setMinWidth(w);
|
| | | table.getColumn(name).setMaxWidth(w);
|
| | | |
| | |
|
| | | table.setRowSorter(defaultSorter);
|
| | | table.getRowSorter().toggleSortOrder(UsersTableModel.Columns.Name.ordinal());
|
| | | table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
| | |
| | | });
|
| | |
|
| | | table.addMouseListener(new MouseAdapter() {
|
| | | @Override
|
| | | public void mouseClicked(MouseEvent e) {
|
| | | if (e.getClickCount() == 2) {
|
| | | editUser(getSelectedUsers().get(0));
|
| | |
| | |
|
| | | filterTextfield = new JTextField();
|
| | | filterTextfield.addActionListener(new ActionListener() {
|
| | | @Override
|
| | | public void actionPerformed(ActionEvent e) {
|
| | | filterUsers(filterTextfield.getText());
|
| | | }
|
| | | });
|
| | | filterTextfield.addKeyListener(new KeyAdapter() {
|
| | | @Override
|
| | | public void keyReleased(KeyEvent e) {
|
| | | filterUsers(filterTextfield.getText());
|
| | | }
|
| | |
| | | }
|
| | |
|
| | | protected abstract void updateTeamsTable();
|
| | | |
| | |
|
| | | protected void updateTable(boolean pack) {
|
| | | tableModel.list.clear();
|
| | | tableModel.list.addAll(gitblit.getUsers());
|
| | |
| | | return;
|
| | | }
|
| | | RowFilter<UsersTableModel, Object> containsFilter = new RowFilter<UsersTableModel, Object>() {
|
| | | @Override
|
| | | public boolean include(Entry<? extends UsersTableModel, ? extends Object> entry) {
|
| | | for (int i = entry.getValueCount() - 1; i >= 0; i--) {
|
| | | if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
|
| | |
| | | /**
|
| | | * Displays the create user dialog and fires a SwingWorker to update the
|
| | | * server, if appropriate.
|
| | | * |
| | | *
|
| | | */
|
| | | protected void createUser() {
|
| | | EditUserDialog dialog = new EditUserDialog(gitblit.getProtocolVersion(),
|
| | |
| | | /**
|
| | | * Displays the edit user dialog and fires a SwingWorker to update the
|
| | | * server, if appropriate.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | */
|
| | | protected void editUser(final UserModel user) {
|
| | |
| | |
|
| | | /**
|
| | | * Table model of a list of users.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class UsersTableModel extends AbstractTableModel {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
|
| | | * |
| | | *
|
| | | * @param columnIndex
|
| | | * the column being queried
|
| | | * @return the Object.class
|
| | | */
|
| | | @Override
|
| | | public Class<?> getColumnClass(int columnIndex) {
|
| | | return String.class;
|
| | | }
|
| | |
| | | public static JTable newTable(TableModel model, String datePattern) {
|
| | | return newTable(model, datePattern, null);
|
| | | }
|
| | | |
| | |
|
| | | public static JTable newTable(TableModel model, String datePattern, final RowRenderer rowRenderer) {
|
| | | JTable table;
|
| | | if (rowRenderer == null) {
|
| | | table = new JTable(model);
|
| | | } else {
|
| | | table = new JTable(model) {
|
| | | |
| | |
|
| | | @Override
|
| | | public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
|
| | | Component c = super.prepareRenderer(renderer, row, column);
|
| | |
| | | showException(null, x);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static abstract class RowRenderer {
|
| | | public abstract void prepareRow(Component c, boolean isSelected, int row, int column);
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Fanout client class.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | |
| | | private volatile Selector selector;
|
| | | private volatile SocketChannel socketCh;
|
| | | private Thread clientThread;
|
| | | |
| | |
|
| | | private final AtomicBoolean isConnected;
|
| | | private final AtomicBoolean isRunning;
|
| | | private final AtomicBoolean isAutomaticReconnect;
|
| | | private final ByteBuffer writeBuffer;
|
| | | private final ByteBuffer readBuffer;
|
| | | private final CharsetDecoder decoder;
|
| | | |
| | |
|
| | | private final Set<String> subscriptions;
|
| | | private boolean resubscribe;
|
| | | |
| | |
|
| | | public interface FanoutListener {
|
| | | public void pong(Date timestamp);
|
| | | public void announcement(String channel, String message);
|
| | | }
|
| | | |
| | |
|
| | | public static class FanoutAdapter implements FanoutListener {
|
| | | @Override
|
| | | public void pong(Date timestamp) { }
|
| | | @Override
|
| | | public void announcement(String channel, String message) { }
|
| | | }
|
| | |
|
| | |
| | | public void pong(Date timestamp) {
|
| | | System.out.println("Pong. " + timestamp);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public void announcement(String channel, String message) {
|
| | | System.out.println(MessageFormat.format("Here ye, Here ye. {0} says {1}", channel, message));
|
| | | }
|
| | | });
|
| | | client.start();
|
| | | |
| | |
|
| | | Thread.sleep(5000);
|
| | | client.ping();
|
| | | client.subscribe("james");
|
| | | client.announce("james", "12345"); |
| | | client.announce("james", "12345");
|
| | | client.subscribe("c52f99d16eb5627877ae957df7ce1be102783bd5");
|
| | | |
| | |
|
| | | while (true) {
|
| | | Thread.sleep(10000);
|
| | | client.ping();
|
| | |
| | | public void removeListener(FanoutListener listener) {
|
| | | listeners.remove(listener);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isAutomaticReconnect() {
|
| | | return isAutomaticReconnect.get();
|
| | | }
|
| | | |
| | |
|
| | | public void setAutomaticReconnect(boolean value) {
|
| | | isAutomaticReconnect.set(value);
|
| | | }
|
| | |
| | | confirmConnection();
|
| | | write("status");
|
| | | }
|
| | | |
| | |
|
| | | public void subscribe(String channel) {
|
| | | confirmConnection();
|
| | | if (subscriptions.add(channel)) {
|
| | | write("subscribe " + channel);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public void unsubscribe(String channel) {
|
| | | confirmConnection();
|
| | | if (subscriptions.remove(channel)) {
|
| | | write("unsubscribe " + channel);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public void announce(String channel, String message) {
|
| | | confirmConnection();
|
| | | write("announce " + channel + " " + message);
|
| | |
| | | throw new RuntimeException("Fanout client is disconnected!");
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public boolean isConnected() {
|
| | | return isRunning.get() && socketCh != null && isConnected.get();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Start client connection and return immediately.
|
| | | */
|
| | |
| | | clientThread = new Thread(this, "Fanout client");
|
| | | clientThread.start();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Start client connection and wait until it has connected.
|
| | | */
|
| | | public void startSynchronously() {
|
| | | start();
|
| | | while (!isConnected()) { |
| | | while (!isConnected()) {
|
| | | try {
|
| | | Thread.sleep(100);
|
| | | } catch (Exception e) {
|
| | |
| | | @Override
|
| | | public void run() {
|
| | | resetState();
|
| | | |
| | | isRunning.set(true); |
| | |
|
| | | isRunning.set(true);
|
| | | while (isRunning.get()) {
|
| | | // (re)connect
|
| | | if (socketCh == null) {
|
| | |
| | | socketCh = SocketChannel.open(new InetSocketAddress(addr, port));
|
| | | socketCh.configureBlocking(false);
|
| | | selector = Selector.open();
|
| | | id = FanoutConstants.getLocalSocketId(socketCh.socket()); |
| | | id = FanoutConstants.getLocalSocketId(socketCh.socket());
|
| | | socketCh.register(selector, SelectionKey.OP_READ);
|
| | | } catch (Exception e) {
|
| | | logger.error(MessageFormat.format("failed to open client connection to {0}:{1,number,0}", host, port), e);
|
| | |
| | | continue;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // read/write
|
| | | try {
|
| | | selector.select(clientTimeout);
|
| | |
| | | while (i.hasNext()) {
|
| | | SelectionKey key = i.next();
|
| | | i.remove();
|
| | | |
| | |
|
| | | if (key.isReadable()) {
|
| | | // read message
|
| | | String content = read();
|
| | |
| | | // resubscribe
|
| | | if (resubscribe) {
|
| | | resubscribe = false;
|
| | | logger.info(MessageFormat.format("fanout client {0} re-subscribing to {1} channels", id, subscriptions.size())); |
| | | logger.info(MessageFormat.format("fanout client {0} re-subscribing to {1} channels", id, subscriptions.size()));
|
| | | for (String subscription : subscriptions) {
|
| | | write("subscribe " + subscription);
|
| | | }
|
| | |
| | | }
|
| | | } catch (IOException e) {
|
| | | logger.error(MessageFormat.format("fanout client {0} error: {1}", id, e.getMessage()));
|
| | | closeChannel(); |
| | | closeChannel();
|
| | | if (!isAutomaticReconnect.get()) {
|
| | | isRunning.set(false);
|
| | | continue;
|
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | closeChannel();
|
| | | resetState();
|
| | | }
|
| | | |
| | |
|
| | | protected void resetState() {
|
| | | readBuffer.clear();
|
| | | writeBuffer.clear();
|
| | | isRunning.set(false);
|
| | | isConnected.set(false);
|
| | | }
|
| | | |
| | |
|
| | | private void closeChannel() {
|
| | | try {
|
| | | if (socketCh != null) {
|
| | |
| | | long time = Long.parseLong(fields[0]);
|
| | | Date date = new Date(time);
|
| | | firePong(date);
|
| | | } catch (Exception e) { |
| | | } catch (Exception e) {
|
| | | }
|
| | | return true;
|
| | | } else if (fields.length == 2) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | protected synchronized String read() throws IOException {
|
| | | readBuffer.clear();
|
| | | long len = socketCh.read(readBuffer);
|
| | |
| | | return content;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | protected synchronized boolean write(String message) {
|
| | | try {
|
| | | logger.info(MessageFormat.format("fanout client {0} > {1}", id, message));
|
| | |
| | | public final static String CH_DEBUG = "debug";
|
| | | public final static String MSG_CONNECTED = "connected...";
|
| | | public final static String MSG_BUSY = "busy";
|
| | | |
| | |
|
| | | public static String getRemoteSocketId(Socket socket) {
|
| | | return socket.getInetAddress().getHostAddress() + ":" + socket.getPort();
|
| | | }
|
| | | |
| | |
|
| | | public static String getLocalSocketId(Socket socket) {
|
| | | return socket.getInetAddress().getHostAddress() + ":" + socket.getLocalPort();
|
| | | }
|
| | |
| | | *
|
| | | * This implementation uses channels and selectors, which are the Java analog of
|
| | | * the Linux epoll mechanism used in the original fanout C code.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | |
| | |
|
| | | private volatile ServerSocketChannel serviceCh;
|
| | | private volatile Selector selector;
|
| | | |
| | |
|
| | | public static void main(String[] args) throws Exception {
|
| | | FanoutNioService pubsub = new FanoutNioService(null, DEFAULT_PORT);
|
| | | pubsub.setStrictRequestTermination(false);
|
| | |
| | |
|
| | | /**
|
| | | * Create a single-threaded fanout service.
|
| | | * |
| | | *
|
| | | * @param host
|
| | | * @param port
|
| | | * the port for running the fanout PubSub service
|
| | |
| | | public FanoutNioService(int port) {
|
| | | this(null, port);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Create a single-threaded fanout service.
|
| | | * |
| | | *
|
| | | * @param bindInterface
|
| | | * the ip address to bind for the service, may be null
|
| | | * @param port
|
| | |
| | | public FanoutNioService(String bindInterface, int port) {
|
| | | super(bindInterface, port, "Fanout nio service");
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected boolean isConnected() {
|
| | | return serviceCh != null;
|
| | |
| | | serviceCh.socket().bind(host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port));
|
| | | selector = Selector.open();
|
| | | serviceCh.register(selector, SelectionKey.OP_ACCEPT);
|
| | | logger.info(MessageFormat.format("{0} is ready on {1}:{2,number,0}", |
| | | logger.info(MessageFormat.format("{0} is ready on {1}:{2,number,0}",
|
| | | name, host == null ? "0.0.0.0" : host, port));
|
| | | } catch (IOException e) {
|
| | | logger.error(MessageFormat.format("failed to open {0} on {1}:{2,number,0}", |
| | | logger.error(MessageFormat.format("failed to open {0} on {1}:{2,number,0}",
|
| | | name, name, host == null ? "0.0.0.0" : host, port), e);
|
| | | return false;
|
| | | }
|
| | |
| | | for (Map.Entry<String, SocketChannel> client : clients.entrySet()) {
|
| | | closeClientSocket(client.getKey(), client.getValue());
|
| | | }
|
| | | |
| | |
|
| | | // close service socket channel
|
| | | logger.debug(MessageFormat.format("closing {0} socket channel", name));
|
| | | serviceCh.socket().close();
|
| | | serviceCh.close(); |
| | | serviceCh.close();
|
| | | serviceCh = null;
|
| | | selector.close();
|
| | | selector = null;
|
| | |
| | | Set<SelectionKey> keys = selector.selectedKeys();
|
| | | Iterator<SelectionKey> keyItr = keys.iterator();
|
| | | while (keyItr.hasNext()) {
|
| | | SelectionKey key = (SelectionKey) keyItr.next();
|
| | | SelectionKey key = keyItr.next();
|
| | | if (key.isAcceptable()) {
|
| | | // new fanout client connection
|
| | | ServerSocketChannel sch = (ServerSocketChannel) key.channel();
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | protected void closeClientSocket(String id, SocketChannel ch) {
|
| | | try {
|
| | | ch.close();
|
| | |
| | | logger.error(MessageFormat.format("fanout connection {0}", id), e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void broadcast(Collection<FanoutServiceConnection> connections, String channel, String message) {
|
| | | super.broadcast(connections, channel, message);
|
| | | |
| | |
|
| | | // register queued write
|
| | | Map<String, SocketChannel> sockets = getCurrentClientSockets();
|
| | | for (FanoutServiceConnection connection : connections) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | protected Map<String, SocketChannel> getCurrentClientSockets() {
|
| | | Map<String, SocketChannel> sockets = new HashMap<String, SocketChannel>();
|
| | | for (SelectionKey key : selector.keys()) {
|
| | |
| | | }
|
| | | return sockets;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * FanoutNioConnection handles reading/writing messages from a remote fanout
|
| | | * connection.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | |
| | | replyQueue = new ArrayList<String>();
|
| | | decoder = Charset.forName(FanoutConstants.CHARSET).newDecoder();
|
| | | }
|
| | | |
| | |
|
| | | protected void read(SocketChannel ch, boolean strictRequestTermination) throws CharacterCodingException, IOException {
|
| | | long bytesRead = 0;
|
| | | readBuffer.clear();
|
| | |
| | | String [] lines = req.split(strictRequestTermination ? "\n" : "\n|\r");
|
| | | requestQueue.addAll(Arrays.asList(lines));
|
| | | }
|
| | | |
| | |
|
| | | protected void write(SocketChannel ch) throws IOException {
|
| | | Iterator<String> itr = replyQueue.iterator();
|
| | | while (itr.hasNext()) {
|
| | |
| | | writeBuffer.put((byte) 0xa);
|
| | | }
|
| | | writeBuffer.flip();
|
| | | |
| | |
|
| | | // loop until write buffer has been completely sent
|
| | | int written = 0;
|
| | | int toWrite = writeBuffer.remaining();
|
| | |
| | | Thread.sleep(10);
|
| | | } catch (Exception x) {
|
| | | }
|
| | | } |
| | | }
|
| | | itr.remove();
|
| | | }
|
| | | writeBuffer.clear();
|
| | |
| | |
|
| | | /**
|
| | | * Base class for Fanout service implementations.
|
| | | * |
| | | *
|
| | | * Subclass implementations can be used as a Sparkleshare PubSub notification
|
| | | * server. This allows Sparkleshare to be used in conjunction with Gitblit
|
| | | * behind a corporate firewall that restricts or prohibits client internet access
|
| | | * to the default Sparkleshare PubSub server: notifications.sparkleshare.org
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | public abstract class FanoutService implements Runnable {
|
| | |
|
| | | private final static Logger logger = LoggerFactory.getLogger(FanoutService.class);
|
| | | |
| | |
|
| | | public final static int DEFAULT_PORT = 17000;
|
| | | |
| | |
|
| | | protected final static int serviceTimeout = 5000;
|
| | |
|
| | | protected final String host;
|
| | | protected final int port;
|
| | | protected final String name; |
| | | |
| | | protected final String name;
|
| | |
|
| | | private Thread serviceThread;
|
| | | |
| | |
|
| | | private final Map<String, FanoutServiceConnection> connections;
|
| | | private final Map<String, Set<FanoutServiceConnection>> subscriptions;
|
| | |
|
| | |
| | | private final AtomicBoolean strictRequestTermination;
|
| | | private final AtomicBoolean allowAllChannelAnnouncements;
|
| | | private final AtomicInteger concurrentConnectionLimit;
|
| | | |
| | |
|
| | | private final Date bootDate;
|
| | | private final AtomicLong rejectedConnectionCount;
|
| | | private final AtomicInteger peakConnectionCount;
|
| | |
| | | this.host = host;
|
| | | this.port = port;
|
| | | this.name = name;
|
| | | |
| | |
|
| | | connections = new ConcurrentHashMap<String, FanoutServiceConnection>();
|
| | | subscriptions = new ConcurrentHashMap<String, Set<FanoutServiceConnection>>();
|
| | | subscriptions.put(FanoutConstants.CH_ALL, new ConcurrentSkipListSet<FanoutServiceConnection>());
|
| | | |
| | |
|
| | | isRunning = new AtomicBoolean(false);
|
| | | strictRequestTermination = new AtomicBoolean(false);
|
| | | allowAllChannelAnnouncements = new AtomicBoolean(false);
|
| | | concurrentConnectionLimit = new AtomicInteger(0);
|
| | | |
| | |
|
| | | bootDate = new Date();
|
| | | rejectedConnectionCount = new AtomicLong(0);
|
| | | peakConnectionCount = new AtomicInteger(0);
|
| | |
| | | /*
|
| | | * Abstract methods
|
| | | */
|
| | | |
| | |
|
| | | protected abstract boolean isConnected();
|
| | | |
| | |
|
| | | protected abstract boolean connect();
|
| | | |
| | |
|
| | | protected abstract void listen() throws IOException;
|
| | | |
| | |
|
| | | protected abstract void disconnect();
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the service requires \n request termination.
|
| | | * |
| | | *
|
| | | * @return true if request requires \n termination
|
| | | */
|
| | | public boolean isStrictRequestTermination() {
|
| | |
| | | * Control the termination of fanout requests. If true, fanout requests must
|
| | | * be terminated with \n. If false, fanout requests may be terminated with
|
| | | * \n, \r, \r\n, or \n\r. This is useful for debugging with a telnet client.
|
| | | * |
| | | *
|
| | | * @param isStrictTermination
|
| | | */
|
| | | public void setStrictRequestTermination(boolean isStrictTermination) {
|
| | | strictRequestTermination.set(isStrictTermination);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the maximum allowable concurrent fanout connections.
|
| | | * |
| | | *
|
| | | * @return the maximum allowable concurrent connection count
|
| | | */
|
| | | public int getConcurrentConnectionLimit() {
|
| | | return concurrentConnectionLimit.get();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Sets the maximum allowable concurrent fanout connection count.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | */
|
| | | public void setConcurrentConnectionLimit(int value) {
|
| | | concurrentConnectionLimit.set(value);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if connections are allowed to announce on the all channel.
|
| | | * |
| | | *
|
| | | * @return true if connections are allowed to announce on the all channel
|
| | | */
|
| | | public boolean allowAllChannelAnnouncements() {
|
| | | return allowAllChannelAnnouncements.get();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Allows/prohibits connections from announcing on the ALL channel.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | */
|
| | | public void setAllowAllChannelAnnouncements(boolean value) {
|
| | | allowAllChannelAnnouncements.set(value);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the current connections
|
| | | * |
| | | *
|
| | | * @param channel
|
| | | * @return map of current connections keyed by their id
|
| | | */
|
| | | public Map<String, FanoutServiceConnection> getCurrentConnections() {
|
| | | return connections;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns all subscriptions
|
| | | * |
| | | *
|
| | | * @return map of current subscriptions keyed by channel name
|
| | | */
|
| | | public Map<String, Set<FanoutServiceConnection>> getCurrentSubscriptions() {
|
| | |
| | |
|
| | | /**
|
| | | * Returns the subscriptions for the specified channel
|
| | | * |
| | | *
|
| | | * @param channel
|
| | | * @return set of subscribed connections for the specified channel
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the runtime statistics object for this service.
|
| | | * |
| | | *
|
| | | * @return stats
|
| | | */
|
| | | public FanoutStats getStatistics() {
|
| | | FanoutStats stats = new FanoutStats();
|
| | | |
| | |
|
| | | // settings
|
| | | stats.allowAllChannelAnnouncements = allowAllChannelAnnouncements();
|
| | | stats.concurrentConnectionLimit = getConcurrentConnectionLimit();
|
| | | stats.strictRequestTermination = isStrictRequestTermination();
|
| | | |
| | |
|
| | | // runtime stats
|
| | | stats.bootDate = bootDate;
|
| | | stats.rejectedConnectionCount = rejectedConnectionCount.get();
|
| | |
| | | stats.totalMessages = totalMessages.get();
|
| | | stats.totalSubscribes = totalSubscribes.get();
|
| | | stats.totalUnsubscribes = totalUnsubscribes.get();
|
| | | stats.totalPings = totalPings.get(); |
| | | stats.totalPings = totalPings.get();
|
| | | stats.currentConnections = connections.size();
|
| | | stats.currentChannels = subscriptions.size();
|
| | | stats.currentSubscriptions = subscriptions.size() * connections.size();
|
| | | return stats;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the service is ready.
|
| | | * |
| | | *
|
| | | * @return true, if the service is ready
|
| | | */
|
| | | public boolean isReady() {
|
| | |
| | |
|
| | | /**
|
| | | * Start the Fanout service thread and immediatel return.
|
| | | * |
| | | *
|
| | | */
|
| | | public void start() {
|
| | | if (isRunning.get()) {
|
| | |
| | | serviceThread.setName(MessageFormat.format("{0} {1}:{2,number,0}", name, host == null ? "all" : host, port));
|
| | | serviceThread.start();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Start the Fanout service thread and wait until it is accepting connections.
|
| | | * |
| | | *
|
| | | */
|
| | | public void startSynchronously() {
|
| | | start();
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Stop the Fanout service. This method returns when the service has been
|
| | | * completely shutdown.
|
| | |
| | | }
|
| | | logger.info(MessageFormat.format("stopped {0}", name));
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Main execution method of the service
|
| | | */
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | disconnect(); |
| | | disconnect();
|
| | | resetState();
|
| | | }
|
| | | |
| | |
|
| | | protected void resetState() {
|
| | | // reset state data
|
| | | connections.clear();
|
| | |
| | |
|
| | | /**
|
| | | * Configure the client connection socket.
|
| | | * |
| | | *
|
| | | * @param socket
|
| | | * @throws SocketException
|
| | | */
|
| | | protected void configureClientSocket(Socket socket) throws SocketException {
|
| | | socket.setKeepAlive(true); |
| | | socket.setKeepAlive(true);
|
| | | socket.setSoLinger(true, 0); // immediately discard any remaining data
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Add the connection to the connections map.
|
| | | * |
| | | *
|
| | | * @param connection
|
| | | * @return false if the connection was rejected due to too many concurrent
|
| | | * connections
|
| | | */
|
| | | protected boolean addConnection(FanoutServiceConnection connection) { |
| | | protected boolean addConnection(FanoutServiceConnection connection) {
|
| | | int limit = getConcurrentConnectionLimit();
|
| | | if (limit > 0 && connections.size() > limit) {
|
| | | logger.info(MessageFormat.format("hit {0,number,0} connection limit, rejecting fanout connection", concurrentConnectionLimit));
|
| | |
| | | connection.busy();
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | // add the connection to our map
|
| | | connections.put(connection.id, connection);
|
| | |
|
| | |
| | | connection.connected();
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Remove the connection from the connections list and from subscriptions.
|
| | | * |
| | | *
|
| | | * @param connection
|
| | | */
|
| | | protected void removeConnection(FanoutServiceConnection connection) {
|
| | |
| | | }
|
| | | logger.info(MessageFormat.format("fanout connection {0} removed", connection.id));
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Tests to see if the connection is being monitored by the service.
|
| | | * |
| | | *
|
| | | * @param connection
|
| | | * @return true if the service is monitoring the connection
|
| | | */
|
| | | protected boolean hasConnection(FanoutServiceConnection connection) {
|
| | | return connections.containsKey(connection.id);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Reply to a connection on the specified channel.
|
| | | * |
| | | *
|
| | | * @param connection
|
| | | * @param channel
|
| | | * @param message
|
| | | * @return the reply
|
| | | */
|
| | | protected String reply(FanoutServiceConnection connection, String channel, String message) { |
| | | protected String reply(FanoutServiceConnection connection, String channel, String message) {
|
| | | if (channel != null && channel.length() > 0) {
|
| | | increment(totalMessages);
|
| | | }
|
| | | return connection.reply(channel, message);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Service method to broadcast a message to all connections.
|
| | | * |
| | | *
|
| | | * @param message
|
| | | */
|
| | | public void broadcastAll(String message) {
|
| | | broadcast(connections.values(), FanoutConstants.CH_ALL, message);
|
| | | increment(totalAnnouncements);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Service method to broadcast a message to connections subscribed to the
|
| | | * channel.
|
| | | * |
| | | *
|
| | | * @param message
|
| | | */
|
| | | public void broadcast(String channel, String message) {
|
| | |
| | | broadcast(connections, channel, message);
|
| | | increment(totalAnnouncements);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Broadcast a message to connections subscribed to the specified channel.
|
| | | * |
| | | *
|
| | | * @param connections
|
| | | * @param channel
|
| | | * @param message
|
| | |
| | | reply(connection, channel, message);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Process an incoming Fanout request.
|
| | | * |
| | | *
|
| | | * @param connection
|
| | | * @param req
|
| | | * @return the reply to the request, may be null
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Process the Fanout request.
|
| | | * |
| | | *
|
| | | * @param connection
|
| | | * @param action
|
| | | * @param channel
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | private String asHexArray(String req) {
|
| | | StringBuilder sb = new StringBuilder();
|
| | | for (char c : req.toCharArray()) {
|
| | |
| | | }
|
| | | return "[ " + sb.toString().trim() + " ]";
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Increment a long and prevent negative rollover.
|
| | | * |
| | | *
|
| | | * @param counter
|
| | | */
|
| | | private void increment(AtomicLong counter) {
|
| | |
| | | counter.set(0);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name;
|
| | |
| | | /**
|
| | | * FanoutServiceConnection handles reading/writing messages from a remote fanout
|
| | | * connection.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class FanoutServiceConnection implements Comparable<FanoutServiceConnection> {
|
| | | |
| | |
|
| | | private static final Logger logger = LoggerFactory.getLogger(FanoutServiceConnection.class);
|
| | | |
| | |
|
| | | public final String id;
|
| | |
|
| | | protected FanoutServiceConnection(Socket socket) {
|
| | |
| | |
|
| | | /**
|
| | | * Send the connection a debug channel connected message.
|
| | | * |
| | | *
|
| | | * @param message
|
| | | */
|
| | | protected void connected() {
|
| | | reply(FanoutConstants.CH_DEBUG, FanoutConstants.MSG_CONNECTED);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Send the connection a debug channel busy message.
|
| | | * |
| | | *
|
| | | * @param message
|
| | | */
|
| | | protected void busy() {
|
| | | reply(FanoutConstants.CH_DEBUG, FanoutConstants.MSG_BUSY);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Send the connection a message for the specified channel.
|
| | | * |
| | | *
|
| | | * @param channel
|
| | | * @param message
|
| | | * @return the reply
|
| | |
| | | * This implementation creates a master acceptor thread which accepts incoming
|
| | | * fanout connections and then spawns a daemon thread for each accepted connection.
|
| | | * If there are 100 concurrent fanout connections, there are 101 threads.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | |
| | | pubsub.setAllowAllChannelAnnouncements(false);
|
| | | pubsub.start();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Create a multi-threaded fanout service.
|
| | | * |
| | | *
|
| | | * @param port
|
| | | * the port for running the fanout PubSub service
|
| | | * @throws IOException
|
| | |
| | |
|
| | | /**
|
| | | * Create a multi-threaded fanout service.
|
| | | * |
| | | *
|
| | | * @param bindInterface
|
| | | * the ip address to bind for the service, may be null
|
| | | * @param port
|
| | |
| | | public FanoutSocketService(String bindInterface, int port) {
|
| | | super(bindInterface, port, "Fanout socket service");
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected boolean isConnected() {
|
| | | return serviceSocket != null;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected boolean connect() {
|
| | | if (serviceSocket == null) {
|
| | |
| | | serviceSocket.setReuseAddress(true);
|
| | | serviceSocket.setSoTimeout(serviceTimeout);
|
| | | serviceSocket.bind(host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port));
|
| | | logger.info(MessageFormat.format("{0} is ready on {1}:{2,number,0}", |
| | | logger.info(MessageFormat.format("{0} is ready on {1}:{2,number,0}",
|
| | | name, host == null ? "0.0.0.0" : host, serviceSocket.getLocalPort()));
|
| | | } catch (IOException e) {
|
| | | logger.error(MessageFormat.format("failed to open {0} on {1}:{2,number,0}",
|
| | |
| | | // ignore accept timeout exceptions
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * FanoutSocketConnection handles reading/writing messages from a remote fanout
|
| | | * connection.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | class FanoutSocketConnection extends FanoutServiceConnection implements Runnable {
|
| | | Socket socket;
|
| | | |
| | |
|
| | | FanoutSocketConnection(Socket socket) {
|
| | | super(socket);
|
| | | this.socket = socket;
|
| | |
| | |
|
| | | logger.info(MessageFormat.format("thread for fanout connection {0} is finished", id));
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void reply(String content) throws IOException {
|
| | | // synchronously send reply
|
| | |
| | | }
|
| | | os.flush();
|
| | | }
|
| | | |
| | |
|
| | | protected void closeConnection() {
|
| | | // close the connection socket
|
| | | try {
|
| | |
| | | } catch (IOException e) {
|
| | | }
|
| | | socket = null;
|
| | | |
| | |
|
| | | // remove this connection from the service
|
| | | removeConnection(this);
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Encapsulates the runtime stats of a fanout service.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | public class FanoutStats implements Serializable {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | public long concurrentConnectionLimit;
|
| | | public boolean allowAllChannelAnnouncements;
|
| | | public boolean strictRequestTermination;
|
| | | |
| | |
|
| | | public Date bootDate;
|
| | | public long rejectedConnectionCount;
|
| | | public int peakConnectionCount;
|
| | |
| | | public long totalSubscribes;
|
| | | public long totalUnsubscribes;
|
| | | public long totalPings;
|
| | | |
| | |
|
| | | public String info() {
|
| | | int i = 0;
|
| | | StringBuilder sb = new StringBuilder();
|
| | |
| | | sb.append(infoInt(i++, "total pings"));
|
| | | String template = sb.toString();
|
| | |
|
| | | String info = MessageFormat.format(template, |
| | | String info = MessageFormat.format(template,
|
| | | bootDate.toString(),
|
| | | Boolean.toString(strictRequestTermination),
|
| | | Boolean.toString(allowAllChannelAnnouncements),
|
| | | concurrentConnectionLimit,
|
| | | rejectedConnectionCount,
|
| | | peakConnectionCount,
|
| | | currentConnections, |
| | | currentConnections,
|
| | | currentChannels,
|
| | | currentSubscriptions,
|
| | | currentSubscriptions == 0 ? 0 : (currentSubscriptions - currentConnections),
|
| | |
| | | totalPings);
|
| | | return info;
|
| | | }
|
| | | |
| | |
|
| | | private String infoStr(int index, String label) {
|
| | | return label + ": {" + index + "}\n";
|
| | | }
|
| | | |
| | |
|
| | | private String infoInt(int index, String label) {
|
| | | return label + ": {" + index + ",number,0}\n";
|
| | | }
|
| | |
| | | /**
|
| | | * Gitblit's Git Daemon ignores any and all per-repository daemon settings and
|
| | | * integrates into Gitblit's security model.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitDaemon {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Construct the Gitblit Git daemon.
|
| | | * |
| | | *
|
| | | * @param bindInterface
|
| | | * the ip address of the interface to bind
|
| | | * @param port
|
| | |
| | | // set the repository resolver and pack factories
|
| | | repositoryResolver = new RepositoryResolver<GitDaemonClient>(folder);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Configure a new daemon for the specified network address.
|
| | | * |
| | | *
|
| | | * @param addr
|
| | | * address to listen for connections on. If null, any available
|
| | | * port will be chosen on all network interfaces.
|
| | |
| | | }
|
| | | } };
|
| | | }
|
| | | |
| | |
|
| | | public int getPort() {
|
| | | return myAddress.getPort();
|
| | | }
|
| | | |
| | |
|
| | | public String formatUrl(String servername, String repository) {
|
| | | if (getPort() == 9418) {
|
| | | // standard port
|
| | |
| | |
|
| | | /**
|
| | | * Set the timeout before willing to abort an IO call.
|
| | | * |
| | | *
|
| | | * @param seconds
|
| | | * number of seconds to wait (with no data transfer occurring)
|
| | | * before aborting an IO read or write operation with the
|
| | |
| | |
|
| | | /**
|
| | | * Start this daemon on a background thread.
|
| | | * |
| | | *
|
| | | * @throws IOException
|
| | | * the server socket could not be opened.
|
| | | * @throws IllegalStateException
|
| | |
| | | run.set(true);
|
| | | acceptSocket = listenSock;
|
| | | acceptThread = new Thread(processors, "Git-Daemon-Accept") {
|
| | | @Override
|
| | | public void run() {
|
| | | while (isRunning()) {
|
| | | try {
|
| | |
| | | }
|
| | | };
|
| | | acceptThread.start();
|
| | | |
| | |
|
| | | logger.info(MessageFormat.format("Git Daemon is listening on {0}:{1,number,0}", myAddress.getAddress().getHostAddress(), myAddress.getPort()));
|
| | | }
|
| | |
|
| | |
| | | dc.setRemoteAddress(((InetSocketAddress) peer).getAddress());
|
| | |
|
| | | new Thread(processors, "Git-Daemon-Client " + peer.toString()) {
|
| | | @Override
|
| | | public void run() {
|
| | | try {
|
| | | dc.execute(s);
|
| | |
| | | private InputStream rawIn;
|
| | |
|
| | | private OutputStream rawOut;
|
| | | |
| | |
|
| | | private String repositoryName;
|
| | |
|
| | | GitDaemonClient(final GitDaemon d) {
|
| | |
| | | public OutputStream getOutputStream() {
|
| | | return rawOut;
|
| | | }
|
| | | |
| | |
|
| | | public void setRepositoryName(String repositoryName) {
|
| | | this.repositoryName = repositoryName;
|
| | | }
|
| | | |
| | |
|
| | | /** @return the name of the requested repository. */
|
| | | public String getRepositoryName() {
|
| | | return repositoryName;
|
| | |
| | | GitDaemonService(final String cmdName, final String cfgName) {
|
| | | command = cmdName.startsWith("git-") ? cmdName : "git-" + cmdName; //$NON-NLS-1$ //$NON-NLS-2$
|
| | | configKey = new SectionParser<ServiceConfig>() {
|
| | | @Override
|
| | | public ServiceConfig parse(final Config cfg) {
|
| | | return new ServiceConfig(GitDaemonService.this, cfg, cfgName);
|
| | | }
|
| | |
| | | /**
|
| | | * The GitServlet provides http/https access to Git repositories.
|
| | | * Access to this servlet is protected by the GitFilter.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitServlet extends org.eclipse.jgit.http.server.GitServlet {
|
| | |
|
| | |
| | | setAllowCreates(user.canCreateRef(repository));
|
| | | setAllowDeletes(user.canDeleteRef(repository));
|
| | | setAllowNonFastForwards(user.canRewindRef(repository));
|
| | | |
| | |
|
| | | // setup pre and post receive hook
|
| | | setPreReceiveHook(this);
|
| | | setPostReceiveHook(this);
|
| | |
| | | /** |
| | | * The upload pack factory creates an upload pack which controls what refs are |
| | | * advertised to cloning/pulling clients. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | * |
| | | * @param <X> the connection type |
| | | */ |
| | | public class GitblitUploadPackFactory<X> implements UploadPackFactory<X> { |
| | |
| | | int timeout = 0; |
| | | |
| | | if (req instanceof HttpServletRequest) { |
| | | // http/https request may or may not be authenticated |
| | | // http/https request may or may not be authenticated |
| | | user = GitBlit.self().authenticate((HttpServletRequest) req); |
| | | if (user == null) { |
| | | user = UserModel.ANONYMOUS; |
| | |
| | | UploadPack up = new UploadPack(db); |
| | | up.setRefFilter(refFilter); |
| | | up.setTimeout(timeout); |
| | | |
| | | |
| | | return up; |
| | | } |
| | | |
| | |
| | | * requesting user. |
| | | */ |
| | | public static class UserRefFilter implements RefFilter { |
| | | |
| | | |
| | | final UserModel user; |
| | | |
| | | |
| | | public UserRefFilter(UserModel user) { |
| | | this.user = user; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public Map<String, Ref> filter(Map<String, Ref> refs) { |
| | | if (user.canAdmin()) { |
| | |
| | | |
| | | /** |
| | | * Resolves repositories and grants export access. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class RepositoryResolver<X> extends FileResolver<X> { |
| | | |
| | | private final Logger logger = LoggerFactory.getLogger(RepositoryResolver.class); |
| | | |
| | | |
| | | public RepositoryResolver(File repositoriesFolder) { |
| | | super(repositoriesFolder, true); |
| | | } |
| | |
| | | public Repository open(final X req, final String name) |
| | | throws RepositoryNotFoundException, ServiceNotEnabledException { |
| | | Repository repo = super.open(req, name); |
| | | |
| | | |
| | | // Set repository name for the pack factories |
| | | // We do this because the JGit API does not have a consistent way to |
| | | // retrieve the repository name from the pack factories or the hooks. |
| | |
| | | } |
| | | return repo; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if this repository can be served by the requested client connection. |
| | | */ |
| | |
| | | String scheme = null; |
| | | UserModel user = null; |
| | | String origin = null; |
| | | |
| | | |
| | | if (req instanceof GitDaemonClient) { |
| | | // git daemon request |
| | | // this is an anonymous/unauthenticated protocol |
| | |
| | | } else if (req instanceof HttpServletRequest) { |
| | | // http/https request |
| | | HttpServletRequest httpRequest = (HttpServletRequest) req; |
| | | scheme = httpRequest.getScheme(); |
| | | scheme = httpRequest.getScheme(); |
| | | origin = httpRequest.getRemoteAddr(); |
| | | user = GitBlit.self().authenticate(httpRequest); |
| | | if (user == null) { |
| | |
| | | scheme, repositoryName, user.username, origin)); |
| | | return true; |
| | | } |
| | | |
| | | |
| | | // user can not access this git repo |
| | | logger.warn(MessageFormat.format("{0}:// access of {1} by {2} from {3} DENIED", |
| | | scheme, repositoryName, user.username, origin)); |
| | |
| | | /**
|
| | | * Model class to represent the commit activity across many repositories. This
|
| | | * class is used by the Activity page.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class Activity implements Serializable, Comparable<Activity> {
|
| | |
| | | public final Date startDate;
|
| | |
|
| | | public final Date endDate;
|
| | | |
| | |
|
| | | private final Set<RepositoryCommit> commits;
|
| | |
|
| | | private final Map<String, Metric> authorMetrics;
|
| | |
| | |
|
| | | /**
|
| | | * Constructor for one day of activity.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | */
|
| | | public Activity(Date date) {
|
| | |
| | |
|
| | | /**
|
| | | * Constructor for specified duration of activity from start date.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * the start date of the activity
|
| | | * @param duration
|
| | |
| | | repositoryMetrics = new HashMap<String, Metric>();
|
| | | authorExclusions = new TreeSet<String>();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Exclude the specified authors from the metrics.
|
| | | * |
| | | *
|
| | | * @param authors
|
| | | */
|
| | | public void excludeAuthors(Collection<String> authors) {
|
| | |
| | | /**
|
| | | * Adds a commit to the activity object as long as the commit is not a
|
| | | * duplicate.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param branch
|
| | | * @param commit
|
| | |
| | | /**
|
| | | * Adds a commit to the activity object as long as the commit is not a
|
| | | * duplicate.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param branch
|
| | | * @param commit
|
| | |
| | | public int getCommitCount() {
|
| | | return commits.size();
|
| | | }
|
| | | |
| | |
|
| | | public List<RepositoryCommit> getCommits() {
|
| | | List<RepositoryCommit> list = new ArrayList<RepositoryCommit>(commits);
|
| | | Collections.sort(list);
|
| | |
| | | /**
|
| | | * AnnotatedLine is a serializable model class that represents a the most recent
|
| | | * author, date, and commit id of a line in a source file.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class AnnotatedLine implements Serializable {
|
| | |
|
| | |
| | | * Model class to simulate a push for presentation in the push log news feed |
| | | * for a repository that does not have a Gitblit push log. Commits are grouped |
| | | * by date and may be additionally split by ref. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | */ |
| | | public class DailyLogEntry extends RefLogEntry implements Serializable { |
| | |
| | | if (getAuthorCount() == 1) { |
| | | return getCommits().get(0).getCommitterIdent(); |
| | | } |
| | | |
| | | |
| | | return super.getCommitterIdent(); |
| | | } |
| | | |
| | |
| | | if (getAuthorCount() == 1) { |
| | | return getCommits().get(0).getAuthorIdent(); |
| | | } |
| | | |
| | | |
| | | return super.getAuthorIdent(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Tracks the change type for the specified ref. |
| | | * |
| | | * |
| | | * @param ref |
| | | * @param type |
| | | * @param oldId |
| | | * @param newId |
| | | */ |
| | | @Override |
| | | public void updateRef(String ref, ReceiveCommand.Type type, String oldId, String newId) { |
| | | // daily digests are filled from most recent to oldest |
| | | // daily digests are filled from most recent to oldest |
| | | String preservedNewId = getNewId(ref); |
| | | if (preservedNewId == null) { |
| | | // no preserved new id, this is newest commit |
| | |
| | | * Gitblit instance to pull the repositories and configuration from another
|
| | | * Gitblit instance. This is a backup operation and can be considered something
|
| | | * like svn-sync.
|
| | | * |
| | | *
|
| | | */
|
| | | public class FederationModel implements Serializable, Comparable<FederationModel> {
|
| | |
|
| | |
| | | public String frequency;
|
| | |
|
| | | public String folder;
|
| | | |
| | |
|
| | | public boolean bare;
|
| | |
|
| | | public boolean mirror;
|
| | |
| | |
|
| | | /**
|
| | | * The constructor for a remote server configuration.
|
| | | * |
| | | *
|
| | | * @param serverName
|
| | | */
|
| | | public FederationModel(String serverName) {
|
| | |
| | | /**
|
| | | * Updates the pull status of a particular repository in this federation
|
| | | * registration.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param status
|
| | | */
|
| | |
| | | /**
|
| | | * Iterates over the current pull results and returns the lowest pull
|
| | | * status.
|
| | | * |
| | | *
|
| | | * @return the lowest pull status of the registration
|
| | | */
|
| | | public FederationPullStatus getLowestStatus() {
|
| | |
| | | /**
|
| | | * Returns true if this registration represents the result data sent by a
|
| | | * pulling Gitblit instance.
|
| | | * |
| | | *
|
| | | * @return true, if this is result data
|
| | | */
|
| | | public boolean isResultData() {
|
| | |
| | |
|
| | | /**
|
| | | * Class that encapsulates a point-in-time pull result.
|
| | | * |
| | | *
|
| | | */
|
| | | public static class RepositoryStatus implements Serializable, Comparable<RepositoryStatus> {
|
| | |
|
| | |
| | | public FederationToken tokenType;
|
| | |
|
| | | public String token;
|
| | | |
| | |
|
| | | public String message;
|
| | |
|
| | | public Map<String, RepositoryModel> repositories;
|
| | |
|
| | | /**
|
| | | * The constructor for a federation proposal.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * the url of the source Gitblit instance
|
| | | * @param tokenType
|
| | |
| | |
|
| | | /**
|
| | | * The constructor for a federation set.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * the name of this federation set
|
| | | * @param tokenType
|
| | |
| | |
|
| | | /**
|
| | | * FeedEntryModel represents an entry in a syndication (RSS) feed.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class FeedEntryModel implements Serializable, Comparable<FeedEntryModel> {
|
| | |
| | |
|
| | | /**
|
| | | * FeedModel represents a syndication (RSS) feed.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class FeedModel implements Serializable, Comparable<FeedModel> {
|
| | |
| | | |
| | | /** |
| | | * A ForkModel represents a repository, its direct descendants, and its origin. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class ForkModel implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | |
| | | public final RepositoryModel repository; |
| | | |
| | | |
| | | public final List<ForkModel> forks; |
| | | |
| | | |
| | | public ForkModel(RepositoryModel repository) { |
| | | this.repository = repository; |
| | | this.forks = new ArrayList<ForkModel>(); |
| | | } |
| | | |
| | | |
| | | public boolean isRoot() { |
| | | return StringUtils.isEmpty(repository.originRepository); |
| | | } |
| | | |
| | | |
| | | public boolean isNode() { |
| | | return !ArrayUtils.isEmpty(forks); |
| | | } |
| | | |
| | | |
| | | public boolean isLeaf() { |
| | | return ArrayUtils.isEmpty(forks); |
| | | } |
| | | |
| | | |
| | | public boolean isPersonalRepository() { |
| | | return repository.isPersonalRepository(); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public int hashCode() { |
| | | return repository.hashCode(); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public boolean equals(Object o) { |
| | | if (o instanceof ForkModel) { |
| | |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return repository.toString(); |
| | |
| | |
|
| | | /**
|
| | | * Model class to represent a git client application.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | public boolean supportsTransport(String transportOrUrl) {
|
| | | if (ArrayUtils.isEmpty(transports)) {
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | String scheme = transportOrUrl;
|
| | | if (transportOrUrl.indexOf(':') > -1) {
|
| | | // strip scheme
|
| | | scheme = transportOrUrl.substring(0, transportOrUrl.indexOf(':'));
|
| | | }
|
| | | |
| | |
|
| | | for (String transport : transports) {
|
| | | if (transport.equalsIgnoreCase(scheme)) {
|
| | | return true;
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return StringUtils.isEmpty(title) ? name : title;
|
| | |
| | | * GitNote is a serializable model class that represents a git note. This class
|
| | | * retains an instance of the RefModel which contains the commit in which this
|
| | | * git note was created.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitNote implements Serializable {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Represents a Gravatar profile.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GravatarProfile implements Serializable {
|
| | |
|
| | |
| | | public String preferredUsername;
|
| | | public String currentLocation;
|
| | | public String aboutMe;
|
| | | |
| | |
|
| | | public String profileUrl;
|
| | | public String thumbnailUrl;
|
| | | public List<ProfileObject> photos;
|
| | |
| | | /**
|
| | | * Metric is a serializable model class that encapsulates metrics for some given
|
| | | * type.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class Metric implements Serializable, Comparable<Metric> {
|
| | |
|
| | |
| | | /**
|
| | | * PathModel is a serializable model class that represents a file or a folder,
|
| | | * including all its metadata and associated commit id.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class PathModel implements Serializable, Comparable<PathModel> {
|
| | |
|
| | |
| | | public boolean isSubmodule() {
|
| | | return FileMode.GITLINK.equals(mode);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isTree() {
|
| | | return FileMode.TREE.equals(mode);
|
| | | }
|
| | |
| | | /**
|
| | | * PathChangeModel is a serializable class that represents a file changed in
|
| | | * a commit.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public static class PathChangeModel extends PathModel {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | public ChangeType changeType;
|
| | | |
| | |
|
| | | public int insertions;
|
| | | |
| | |
|
| | | public int deletions;
|
| | | |
| | |
|
| | | public PathChangeModel(String name, String path, long size, int mode, String objectId,
|
| | | String commitId, ChangeType type) {
|
| | | super(name, path, size, mode, objectId, commitId);
|
| | | this.changeType = type;
|
| | | }
|
| | | |
| | |
|
| | | public void update(char op) {
|
| | | switch (op) {
|
| | | case '+':
|
| | |
| | |
|
| | | /**
|
| | | * ProjectModel is a serializable model class.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class ProjectModel implements Serializable, Comparable<ProjectModel> {
|
| | |
|
| | |
| | | public String title;
|
| | | public String description;
|
| | | public final Set<String> repositories = new HashSet<String>();
|
| | | |
| | |
|
| | | public String projectMarkdown;
|
| | | public String repositoriesMarkdown;
|
| | | public Date lastChange;
|
| | |
| | | public ProjectModel(String name) {
|
| | | this(name, false);
|
| | | }
|
| | | |
| | |
|
| | | public ProjectModel(String name, boolean isRoot) {
|
| | | this.name = name;
|
| | | this.isRoot = isRoot;
|
| | |
| | | this.title = "";
|
| | | this.description = "";
|
| | | }
|
| | | |
| | |
|
| | | public boolean isUserProject() {
|
| | | return ModelUtils.isPersonalRepository(name);
|
| | | }
|
| | |
| | | for (String name:names) {
|
| | | repositories.add(name.toLowerCase());
|
| | | }
|
| | | } |
| | | }
|
| | |
|
| | | public void removeRepository(String name) {
|
| | | repositories.remove(name.toLowerCase());
|
| | | }
|
| | | |
| | |
|
| | | public String getDisplayName() {
|
| | | return StringUtils.isEmpty(title) ? name : title;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name;
|
| | |
| | |
|
| | | /**
|
| | | * Model class to represent a push into a repository.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class RefLogEntry implements Serializable, Comparable<RefLogEntry> {
|
| | |
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | public final String repository;
|
| | | |
| | |
|
| | | public final Date date;
|
| | | |
| | |
|
| | | public final UserModel user;
|
| | |
|
| | | private final Set<RepositoryCommit> commits;
|
| | | |
| | |
|
| | | protected final Map<String, ReceiveCommand.Type> refUpdates;
|
| | | |
| | |
|
| | | protected final Map<String, String> refIdChanges;
|
| | | |
| | |
|
| | | private int authorCount;
|
| | |
|
| | | /**
|
| | | * Constructor for specified duration of push from start date.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * the repository that received the push
|
| | | * @param date
|
| | |
| | | this.refIdChanges = new HashMap<String, String>();
|
| | | this.authorCount = -1;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Tracks the change type for the specified ref.
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @param type
|
| | | */
|
| | |
| | | refUpdates.put(ref, type);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Tracks the change type for the specified ref.
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @param type
|
| | | * @param oldId
|
| | |
| | | refIdChanges.put(ref, oldId + "-" + newId);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the old id of a ref.
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @return the old id
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the new id of a ref
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @return the new id
|
| | | */
|
| | |
| | | }
|
| | | return change.split("-")[1];
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the change type of the ref change.
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @return the change type for the ref
|
| | | */
|
| | |
| | | /**
|
| | | * Adds a commit to the push entry object as long as the commit is not a
|
| | | * duplicate.
|
| | | * |
| | | *
|
| | | * @param branch
|
| | | * @param commit
|
| | | * @return a RepositoryCommit, if one was added. Null if this is duplicate
|
| | |
| | | /**
|
| | | * Adds a commit to the push entry object as long as the commit is not a
|
| | | * duplicate.
|
| | | * |
| | | *
|
| | | * @param branch
|
| | | * @param commit
|
| | | * @return a RepositoryCommit, if one was added. Null if this is duplicate
|
| | |
| | | /**
|
| | | * Adds a a list of repository commits. This is used to construct discrete
|
| | | * ref push log entries
|
| | | * |
| | | *
|
| | | * @param commits
|
| | | */
|
| | | public void addCommits(List<RepositoryCommit> list) {
|
| | | commits.addAll(list);
|
| | | authorCount = -1;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if this push contains a non-fastforward ref update.
|
| | | * |
| | | *
|
| | | * @return true if this is a non-fastforward push
|
| | | */
|
| | | public boolean isNonFastForward() {
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if this ref has been rewound.
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @return true if this is a non-fastforward ref update
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns true if this ref has been deleted.
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @return true if this is a delete ref update
|
| | | */
|
| | |
| | | }
|
| | | return ReceiveCommand.Type.DELETE.equals(type);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of refs changed by the push.
|
| | | * |
| | | *
|
| | | * @return a list of refs
|
| | | */
|
| | | public List<String> getChangedRefs() {
|
| | | return new ArrayList<String>(refUpdates.keySet());
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of branches changed by the push.
|
| | | * |
| | | *
|
| | | * @return a list of branches
|
| | | */
|
| | | public List<String> getChangedBranches() {
|
| | | return getChangedRefs(Constants.R_HEADS);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of tags changed by the push.
|
| | | * |
| | | *
|
| | | * @return a list of tags
|
| | | */
|
| | | public List<String> getChangedTags() {
|
| | |
| | |
|
| | | /**
|
| | | * Gets the changed refs in the push.
|
| | | * |
| | | *
|
| | | * @param baseRef
|
| | | * @return the changed refs
|
| | | */
|
| | |
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | public int getAuthorCount() {
|
| | | if (authorCount == -1) {
|
| | | Set<String> authors = new HashSet<String>();
|
| | |
| | | }
|
| | | return authorCount;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * The total number of commits in the push.
|
| | | * |
| | | *
|
| | | * @return the number of commits in the push
|
| | | */
|
| | | public int getCommitCount() {
|
| | | return commits.size();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns all commits in the push.
|
| | | * |
| | | *
|
| | | * @return a list of commits
|
| | | */
|
| | | public List<RepositoryCommit> getCommits() {
|
| | |
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns all commits that belong to a particular ref
|
| | | * |
| | | *
|
| | | * @param ref
|
| | | * @return a list of commits
|
| | | */
|
| | |
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | public PersonIdent getCommitterIdent() {
|
| | | return new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.username : user.emailAddress);
|
| | | }
|
| | |
| | | // reverse chronological order
|
| | | return o.date.compareTo(date);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | StringBuilder sb = new StringBuilder();
|
| | |
| | | /**
|
| | | * RefModel is a serializable model class that represents a tag or branch and
|
| | | * includes the referenced object.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RefModel implements Serializable, Comparable<RefModel> {
|
| | |
|
| | |
| | | |
| | | /** |
| | | * Represents a Registrant-AccessPermission tuple. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | */ |
| | | public class RegistrantAccessPermission implements Serializable, Comparable<RegistrantAccessPermission> { |
| | |
| | | public PermissionType permissionType; |
| | | public boolean mutable; |
| | | public String source; |
| | | |
| | | |
| | | public RegistrantAccessPermission() { |
| | | } |
| | | |
| | |
| | | this.permissionType = PermissionType.EXPLICIT; |
| | | this.mutable = true; |
| | | } |
| | | |
| | | |
| | | public RegistrantAccessPermission(String registrant, AccessPermission permission, PermissionType permissionType, RegistrantType registrantType, String source, boolean mutable) { |
| | | this.registrant = registrant; |
| | | this.permission = permission; |
| | |
| | | this.source = source; |
| | | this.mutable = mutable; |
| | | } |
| | | |
| | | |
| | | public boolean isAdmin() { |
| | | return PermissionType.ADMINISTRATOR.equals(permissionType); |
| | | } |
| | |
| | | public boolean isOwner() { |
| | | return PermissionType.OWNER.equals(permissionType); |
| | | } |
| | | |
| | | |
| | | public boolean isExplicit() { |
| | | return PermissionType.EXPLICIT.equals(permissionType); |
| | | } |
| | |
| | | public boolean isMissing() { |
| | | return PermissionType.MISSING.equals(permissionType); |
| | | } |
| | | |
| | | |
| | | public int getScore() { |
| | | switch (registrantType) { |
| | | case REPOSITORY: |
| | |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public int compareTo(RegistrantAccessPermission p) { |
| | | switch (registrantType) { |
| | |
| | | int score2 = p.getScore(); |
| | | if (score1 <= 2 && score2 <= 2) { |
| | | // group admin, owner, and explicit together |
| | | return StringUtils.compareRepositoryNames(registrant, p.registrant); |
| | | return StringUtils.compareRepositoryNames(registrant, p.registrant); |
| | | } |
| | | if (score1 < score2) { |
| | | return -1; |
| | |
| | | return registrant.toLowerCase().compareTo(p.registrant.toLowerCase()); |
| | | } |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public int hashCode() { |
| | | return registrant.hashCode(); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public boolean equals(Object o) { |
| | | if (o instanceof RegistrantAccessPermission) { |
| | | RegistrantAccessPermission p = (RegistrantAccessPermission) o; |
| | | return registrant.equals(p.registrant); |
| | | } |
| | | |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return permission.asRole(registrant); |
| | |
| | | /** |
| | | * Model class to represent a RevCommit, it's source repository, and the branch. |
| | | * This class is used by the activity page. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | */ |
| | | public class RepositoryCommit implements Serializable, Comparable<RepositoryCommit> { |
| | |
| | | public String getShortMessage() { |
| | | return commit.getShortMessage(); |
| | | } |
| | | |
| | | |
| | | public Date getCommitDate() { |
| | | return new Date(commit.getCommitTime() * 1000L); |
| | | } |
| | |
| | | public int getParentCount() { |
| | | return commit.getParentCount(); |
| | | } |
| | | |
| | | |
| | | public RevCommit [] getParents() { |
| | | return commit.getParents(); |
| | | } |
| | |
| | | public PersonIdent getCommitterIdent() { |
| | | return commit.getCommitterIdent(); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public boolean equals(Object o) { |
| | | if (o instanceof RepositoryCommit) { |
| | |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | public RepositoryCommit clone(String withRef) { |
| | | return new RepositoryCommit(repository, withRef, commit); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return MessageFormat.format("{0} {1} {2,date,yyyy-MM-dd HH:mm} {3} {4}", |
| | | return MessageFormat.format("{0} {1} {2,date,yyyy-MM-dd HH:mm} {3} {4}", |
| | | getShortName(), branch, getCommitterIdent().getWhen(), getAuthorIdent().getName(), |
| | | getShortMessage()); |
| | | } |
| | |
| | | /**
|
| | | * RepositoryModel is a serializable model class that represents a Gitblit
|
| | | * repository including its configuration settings and access restriction.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RepositoryModel implements Serializable, Comparable<RepositoryModel> {
|
| | |
|
| | |
| | | public boolean verifyCommitter;
|
| | | public String gcThreshold;
|
| | | public int gcPeriod;
|
| | | public int maxActivityCommits; |
| | | public int maxActivityCommits;
|
| | | public List<String> metricAuthorExclusions;
|
| | | public CommitMessageRenderer commitMessageRenderer;
|
| | | |
| | |
|
| | | public transient boolean isCollectingGarbage;
|
| | | public Date lastGC;
|
| | | public String sparkleshareId;
|
| | | |
| | |
|
| | | public RepositoryModel() {
|
| | | this("", "", "", new Date(0));
|
| | | }
|
| | |
| | | this.accessRestriction = AccessRestrictionType.NONE;
|
| | | this.authorizationControl = AuthorizationControl.NAMED;
|
| | | this.federationSets = new ArrayList<String>();
|
| | | this.federationStrategy = FederationStrategy.FEDERATE_THIS; |
| | | this.federationStrategy = FederationStrategy.FEDERATE_THIS;
|
| | | this.projectPath = StringUtils.getFirstPathElement(name);
|
| | | this.owners = new ArrayList<String>();
|
| | | this.isBare = true;
|
| | | |
| | |
|
| | | addOwner(owner);
|
| | | }
|
| | | |
| | |
|
| | | public List<String> getLocalBranches() {
|
| | | if (ArrayUtils.isEmpty(availableRefs)) {
|
| | | return new ArrayList<String>();
|
| | |
| | | }
|
| | | return localBranches;
|
| | | }
|
| | | |
| | |
|
| | | public void addFork(String repository) {
|
| | | if (forks == null) {
|
| | | forks = new TreeSet<String>();
|
| | | }
|
| | | forks.add(repository);
|
| | | }
|
| | | |
| | |
|
| | | public void removeFork(String repository) {
|
| | | if (forks == null) {
|
| | | return;
|
| | | }
|
| | | forks.remove(repository);
|
| | | }
|
| | | |
| | |
|
| | | public void resetDisplayName() {
|
| | | displayName = null;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public int hashCode() {
|
| | | return name.hashCode();
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean equals(Object o) {
|
| | | if (o instanceof RepositoryModel) {
|
| | |
| | | public int compareTo(RepositoryModel o) {
|
| | | return StringUtils.compareRepositoryNames(name, o.name);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isFork() {
|
| | | return !StringUtils.isEmpty(originRepository);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isOwner(String username) {
|
| | | if (StringUtils.isEmpty(username) || ArrayUtils.isEmpty(owners)) {
|
| | | return false;
|
| | | }
|
| | | return owners.contains(username.toLowerCase());
|
| | | }
|
| | | |
| | |
|
| | | public boolean isPersonalRepository() {
|
| | | return !StringUtils.isEmpty(projectPath) && ModelUtils.isPersonalRepository(projectPath);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isUsersPersonalRepository(String username) {
|
| | | return !StringUtils.isEmpty(projectPath) && ModelUtils.isUsersPersonalRepository(username, projectPath);
|
| | | }
|
| | | |
| | |
|
| | | public boolean allowAnonymousView() {
|
| | | return !accessRestriction.atLeast(AccessRestrictionType.VIEW);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isShowActivity() {
|
| | | return maxActivityCommits > -1;
|
| | | }
|
| | | |
| | |
|
| | | public boolean isSparkleshared() {
|
| | | return !StringUtils.isEmpty(sparkleshareId);
|
| | | }
|
| | | |
| | |
|
| | | public RepositoryModel cloneAs(String cloneName) {
|
| | | RepositoryModel clone = new RepositoryModel();
|
| | | clone.originRepository = name;
|
| | |
| | | clone.useTickets = useTickets;
|
| | | clone.skipSizeCalculation = skipSizeCalculation;
|
| | | clone.skipSummaryMetrics = skipSummaryMetrics;
|
| | | clone.sparkleshareId = sparkleshareId; |
| | | clone.sparkleshareId = sparkleshareId;
|
| | | return clone;
|
| | | }
|
| | |
|
| | |
| | | /**
|
| | | * Represents a git repository url and it's associated access permission for the
|
| | | * current user.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | |
| | | this.url = url;
|
| | | this.permission = permission;
|
| | | }
|
| | | |
| | |
|
| | | public boolean isExternal() {
|
| | | return permission == null;
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Model class that represents a search result.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SearchResult implements Serializable {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | public int hitId;
|
| | | |
| | |
|
| | | public int totalHits;
|
| | |
|
| | | public float score;
|
| | |
| | | public String committer;
|
| | |
|
| | | public String summary;
|
| | | |
| | |
|
| | | public String fragment;
|
| | | |
| | |
|
| | | public String repository;
|
| | | |
| | |
|
| | | public String branch;
|
| | |
|
| | | public String commitId;
|
| | | |
| | |
|
| | | public String path;
|
| | | |
| | |
|
| | | public List<String> tags;
|
| | | |
| | |
|
| | | public SearchObjectType type;
|
| | |
|
| | | public SearchResult() {
|
| | | }
|
| | | |
| | |
|
| | | public String getId() {
|
| | | switch (type) {
|
| | | case blob:
|
| | |
| | | * setting metadata such as name, current value, default value, description, and
|
| | | * directives. It is a model class for serialization and presentation, but not
|
| | | * for persistence.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class ServerSettings implements Serializable {
|
| | |
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | public List<String> pushScripts;
|
| | | |
| | |
|
| | | public boolean supportsCredentialChanges;
|
| | | |
| | |
|
| | | public boolean supportsDisplayNameChanges;
|
| | | |
| | |
|
| | | public boolean supportsEmailAddressChanges;
|
| | | |
| | |
|
| | | public boolean supportsTeamMembershipChanges;
|
| | |
|
| | | public ServerSettings() {
|
| | |
| | | public SettingModel get(String key) {
|
| | | return settings.get(key);
|
| | | }
|
| | | |
| | |
|
| | | public boolean hasKey(String key) {
|
| | | return settings.containsKey(key);
|
| | | }
|
| | |
| | | /**
|
| | | * ServerStatus encapsulates runtime status information about the server
|
| | | * including some information about the system environment.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class ServerStatus implements Serializable {
|
| | |
|
| | |
| | | /**
|
| | | * SettingModel represents a setting and all its metadata: name, current value,
|
| | | * default value, description, and directives.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | public class SettingModel implements Serializable {
|
| | |
| | |
|
| | | /**
|
| | | * Returns true if the current value is the default value.
|
| | | * |
| | | *
|
| | | * @return true if current value is the default value
|
| | | */
|
| | | public boolean isDefaultValue() {
|
| | |
| | | /**
|
| | | * Returns the boolean value for the currentValue. If the currentValue can
|
| | | * not be interpreted as a boolean, the defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the integer value for the currentValue. If the currentValue can
|
| | | * not be interpreted as an integer, the defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the char value for currentValue. If the currentValue can not be
|
| | | * interpreted as a char, the defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the string value for currentValue. If the currentValue is null,
|
| | | * the defaultValue is returned.
|
| | | * |
| | | *
|
| | | * @param defaultValue
|
| | | * @return key value or defaultValue
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns a list of space-separated strings from the specified key.
|
| | | * |
| | | *
|
| | | * @return list of strings
|
| | | */
|
| | | public List<String> getStrings() {
|
| | |
| | | /**
|
| | | * Returns a list of strings from the currentValue using the specified
|
| | | * string separator.
|
| | | * |
| | | *
|
| | | * @param separator
|
| | | * @return list of strings
|
| | | */
|
| | |
| | | strings = StringUtils.getStringsFromValue(currentValue, separator);
|
| | | return strings;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a map of strings from the current value.
|
| | | * |
| | | *
|
| | | * @return map of string, string
|
| | | */
|
| | | public Map<String, String> getMap() {
|
| | |
| | | for (String string : getStrings()) {
|
| | | String[] kvp = string.split("=", 2);
|
| | | String key = kvp[0];
|
| | | String value = kvp[1]; |
| | | String value = kvp[1];
|
| | | map.put(key, value);
|
| | | }
|
| | | return map;
|
| | |
| | | /**
|
| | | * SubmoduleModel is a serializable model class that represents a git submodule
|
| | | * definition.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SubmoduleModel implements Serializable {
|
| | |
|
| | |
| | | this.path = path;
|
| | | this.url = url;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return path + "=" + url;
|
| | | }
|
| | |
| | | /**
|
| | | * TeamModel is a serializable model class that represents a group of users and
|
| | | * a list of accessible repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class TeamModel implements Serializable, Comparable<TeamModel> {
|
| | |
|
| | |
| | | public void addRepository(String name) {
|
| | | addRepositoryPermission(name);
|
| | | }
|
| | | |
| | |
|
| | | @Deprecated
|
| | | @Unused
|
| | | public void addRepositories(Collection<String> names) {
|
| | |
| | | removeRepositoryPermission(name);
|
| | | }
|
| | |
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a list of repository permissions for this team.
|
| | | * |
| | | *
|
| | | * @return the team's list of permissions
|
| | | */
|
| | | public List<RegistrantAccessPermission> getRepositoryPermissions() {
|
| | |
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the team has any type of specified access permission for
|
| | | * this repository.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return true if team has a specified access permission for the repository
|
| | | */
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the team has an explicitly specified access permission for
|
| | | * this repository.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return if the team has an explicitly specified access permission
|
| | | */
|
| | |
| | | String repository = AccessPermission.repositoryFromRole(name).toLowerCase();
|
| | | return permissions.containsKey(repository);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Adds a repository permission to the team.
|
| | | * <p>
|
| | |
| | | addRepositoryPermission(role);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public AccessPermission removeRepositoryPermission(String name) {
|
| | | String repository = AccessPermission.repositoryFromRole(name).toLowerCase();
|
| | | repositories.remove(repository);
|
| | | return permissions.remove(repository);
|
| | | }
|
| | | |
| | |
|
| | | public void setRepositoryPermission(String repository, AccessPermission permission) {
|
| | | if (permission == null) {
|
| | | // remove the permission
|
| | |
| | | repositories.add(repository.toLowerCase());
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public RegistrantAccessPermission getRepositoryPermission(RepositoryModel repository) {
|
| | | RegistrantAccessPermission ap = new RegistrantAccessPermission();
|
| | | ap.registrant = name;
|
| | | ap.registrantType = RegistrantType.TEAM;
|
| | | ap.permission = AccessPermission.NONE;
|
| | | ap.mutable = false;
|
| | | |
| | |
|
| | | // determine maximum permission for the repository
|
| | | final AccessPermission maxPermission = |
| | | final AccessPermission maxPermission =
|
| | | (repository.isFrozen || !repository.isBare) ?
|
| | | AccessPermission.CLONE : AccessPermission.REWIND;
|
| | |
|
| | |
| | | }
|
| | | return ap;
|
| | | }
|
| | | |
| | |
|
| | | if (canAdmin) {
|
| | | ap.permissionType = PermissionType.ADMINISTRATOR;
|
| | | if (AccessPermission.REWIND.atMost(maxPermission)) {
|
| | |
| | | }
|
| | | return ap;
|
| | | }
|
| | | |
| | |
|
| | | if (permissions.containsKey(repository.name.toLowerCase())) {
|
| | | // exact repository permission specified
|
| | | AccessPermission p = permissions.get(repository.name.toLowerCase());
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // still no explicit or regex, check for implicit permissions
|
| | | if (AccessPermission.NONE == ap.permission) {
|
| | | switch (repository.accessRestriction) {
|
| | |
| | |
|
| | | return ap;
|
| | | }
|
| | | |
| | |
|
| | | protected boolean canAccess(RepositoryModel repository, AccessRestrictionType ifRestriction, AccessPermission requirePermission) {
|
| | | if (repository.accessRestriction.atLeast(ifRestriction)) {
|
| | | RegistrantAccessPermission ap = getRepositoryPermission(repository);
|
| | |
| | | }
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | public boolean canView(RepositoryModel repository) {
|
| | | return canAccess(repository, AccessRestrictionType.VIEW, AccessPermission.VIEW);
|
| | | }
|
| | |
| | | * UserModel is a serializable model class that represents a user and the user's
|
| | | * restricted repository memberships. Instances of UserModels are also used as
|
| | | * servlet user principals.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class UserModel implements Principal, Serializable, Comparable<UserModel> {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | public static final UserModel ANONYMOUS = new UserModel();
|
| | | |
| | |
|
| | | // field names are reflectively mapped in EditUser page
|
| | | public String username;
|
| | | public String password;
|
| | |
| | | public AccountType accountType;
|
| | |
|
| | | public UserPreferences userPreferences;
|
| | | |
| | |
|
| | | public UserModel(String username) {
|
| | | this.username = username;
|
| | | this.isAuthenticated = true;
|
| | |
| | | this.accountType = AccountType.LOCAL;
|
| | | this.userPreferences = new UserPreferences(this.username);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isLocalAccount() {
|
| | | return accountType.isLocal();
|
| | | }
|
| | |
| | | /**
|
| | | * This method does not take into consideration Ownership where the
|
| | | * administrator has not explicitly granted access to the owner.
|
| | | * |
| | | *
|
| | | * @param repositoryName
|
| | | * @return
|
| | | */
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | @Deprecated
|
| | | @Unused
|
| | | public boolean hasRepository(String name) {
|
| | |
| | | public void removeRepository(String name) {
|
| | | removeRepositoryPermission(name);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a list of repository permissions for this user exclusive of
|
| | | * permissions inherited from team memberships.
|
| | | * |
| | | *
|
| | | * @return the user's list of permissions
|
| | | */
|
| | | public List<RegistrantAccessPermission> getRepositoryPermissions() {
|
| | |
| | | list.add(new RegistrantAccessPermission(registrant, ap, pType, RegistrantType.REPOSITORY, source, mutable));
|
| | | }
|
| | | Collections.sort(list);
|
| | | |
| | |
|
| | | // include immutable team permissions, being careful to preserve order
|
| | | Set<RegistrantAccessPermission> set = new LinkedHashSet<RegistrantAccessPermission>(list);
|
| | | for (TeamModel team : teams) {
|
| | |
| | | }
|
| | | return new ArrayList<RegistrantAccessPermission>(set);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the user has any type of specified access permission for
|
| | | * this repository.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return true if user has a specified access permission for the repository
|
| | | */
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the user has an explicitly specified access permission for
|
| | | * this repository.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return if the user has an explicitly specified access permission
|
| | | */
|
| | |
| | | String repository = AccessPermission.repositoryFromRole(name).toLowerCase();
|
| | | return permissions.containsKey(repository);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the user's team memberships specify an access permission for
|
| | | * this repository.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return if the user's team memberships specifi an access permission
|
| | | */
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Adds a repository permission to the team.
|
| | | * <p>
|
| | |
| | | repositories.add(repository);
|
| | | permissions.put(repository, permission);
|
| | | }
|
| | | |
| | |
|
| | | public AccessPermission removeRepositoryPermission(String name) {
|
| | | String repository = AccessPermission.repositoryFromRole(name).toLowerCase();
|
| | | repositories.remove(repository);
|
| | | return permissions.remove(repository);
|
| | | }
|
| | | |
| | |
|
| | | public void setRepositoryPermission(String repository, AccessPermission permission) {
|
| | | if (permission == null) {
|
| | | // remove the permission
|
| | |
| | | ap.registrantType = RegistrantType.USER;
|
| | | ap.permission = AccessPermission.NONE;
|
| | | ap.mutable = false;
|
| | | |
| | |
|
| | | // determine maximum permission for the repository
|
| | | final AccessPermission maxPermission = |
| | | final AccessPermission maxPermission =
|
| | | (repository.isFrozen || !repository.isBare) ?
|
| | | AccessPermission.CLONE : AccessPermission.REWIND;
|
| | |
|
| | |
| | | }
|
| | | return ap;
|
| | | }
|
| | | |
| | |
|
| | | // repository owner - either specified owner or personal repository
|
| | | if (repository.isOwner(username) || repository.isUsersPersonalRepository(username)) {
|
| | | ap.permissionType = PermissionType.OWNER;
|
| | |
| | | }
|
| | | return ap;
|
| | | }
|
| | | |
| | |
|
| | | if (AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl) && isAuthenticated) {
|
| | | // AUTHENTICATED is a shortcut for authorizing all logged-in users RW+ access
|
| | | if (AccessPermission.REWIND.atMost(maxPermission)) {
|
| | |
| | | }
|
| | | return ap;
|
| | | }
|
| | | |
| | |
|
| | | // explicit user permission OR user regex match is used
|
| | | // if that fails, then the best team permission is used
|
| | | if (permissions.containsKey(repository.name.toLowerCase())) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // try to find a team match
|
| | | for (TeamModel team : teams) {
|
| | | RegistrantAccessPermission p = team.getRepositoryPermission(repository);
|
| | |
| | | ap.permissionType = PermissionType.TEAM;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // still no explicit, regex, or team match, check for implicit permissions
|
| | | if (AccessPermission.NONE == ap.permission) {
|
| | | switch (repository.accessRestriction) {
|
| | |
| | | break;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | return ap;
|
| | | }
|
| | | |
| | |
|
| | | protected boolean canAccess(RepositoryModel repository, AccessRestrictionType ifRestriction, AccessPermission requirePermission) {
|
| | | if (repository.accessRestriction.atLeast(ifRestriction)) {
|
| | | RegistrantAccessPermission ap = getRepositoryPermission(repository);
|
| | |
| | | }
|
| | | return true;
|
| | | }
|
| | | |
| | |
|
| | | public boolean canView(RepositoryModel repository) {
|
| | | return canAccess(repository, AccessRestrictionType.VIEW, AccessPermission.VIEW);
|
| | | }
|
| | | |
| | |
|
| | | public boolean canView(RepositoryModel repository, String ref) {
|
| | | // Default UserModel doesn't implement ref-level security.
|
| | | // Other Realms (i.e. Gerrit) may override this method.
|
| | |
| | | }
|
| | | return canClone(repository);
|
| | | }
|
| | | |
| | |
|
| | | public boolean canDelete(RepositoryModel model) {
|
| | | return canAdmin() || model.isUsersPersonalRepository(username);
|
| | | }
|
| | | |
| | |
|
| | | public boolean canEdit(RepositoryModel model) {
|
| | | return canAdmin() || model.isUsersPersonalRepository(username) || model.isOwner(username);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * This returns true if the user has fork privileges or the user has fork
|
| | | * privileges because of a team membership.
|
| | | * |
| | | *
|
| | | * @return true if the user can fork
|
| | | */
|
| | | public boolean canFork() {
|
| | |
| | | /**
|
| | | * This returns true if the user has admin privileges or the user has admin
|
| | | * privileges because of a team membership.
|
| | | * |
| | | *
|
| | | * @return true if the user can admin
|
| | | */
|
| | | public boolean canAdmin() {
|
| | |
| | | /**
|
| | | * This returns true if the user has create privileges or the user has create
|
| | | * privileges because of a team membership.
|
| | | * |
| | | *
|
| | | * @return true if the user can admin
|
| | | */
|
| | | public boolean canCreate() {
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the user is allowed to create the specified repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return true if the user can create the repository
|
| | | */
|
| | |
| | | public String getName() {
|
| | | return username;
|
| | | }
|
| | | |
| | |
|
| | | public String getDisplayName() {
|
| | | if (StringUtils.isEmpty(displayName)) {
|
| | | return username;
|
| | | }
|
| | | return displayName;
|
| | | }
|
| | | |
| | |
|
| | | public String getPersonalPath() {
|
| | | return ModelUtils.getPersonalPath(username);
|
| | | }
|
| | | |
| | |
|
| | | public UserPreferences getPreferences() {
|
| | | return userPreferences;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public int hashCode() {
|
| | | return username.hashCode();
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public boolean equals(Object o) {
|
| | | if (o instanceof UserModel) {
|
| | |
| | | public int compareTo(UserModel o) {
|
| | | return username.compareTo(o.username);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the name/email pair match this user account.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @param email
|
| | | * @return true, if the name and email address match this account
|
| | |
| | | }
|
| | | return nameVerified && emailVerified;
|
| | | }
|
| | | |
| | |
|
| | | @Deprecated
|
| | | public boolean hasBranchPermission(String repositoryName, String branch) {
|
| | | // Default UserModel doesn't implement branch-level security. Other Realms (i.e. Gerrit) may override this method.
|
| | | return hasRepositoryPermission(repositoryName) || hasTeamRepositoryPermission(repositoryName);
|
| | | }
|
| | | |
| | |
|
| | | public boolean isMyPersonalRepository(String repository) {
|
| | | String projectPath = StringUtils.getFirstPathElement(repository);
|
| | | return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase(getPersonalPath());
|
| | |
| | | |
| | | /** |
| | | * User preferences. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | |
| | | public final String username; |
| | | |
| | | public String locale; |
| | | |
| | | |
| | | private final Map<String, UserRepositoryPreferences> repositoryPreferences = new TreeMap<String, UserRepositoryPreferences>(); |
| | | |
| | | public UserPreferences(String username) { |
| | | this.username = username; |
| | | } |
| | | |
| | | |
| | | public Locale getLocale() { |
| | | if (StringUtils.isEmpty(locale)) { |
| | | return null; |
| | | } |
| | | return new Locale(locale); |
| | | } |
| | | |
| | | |
| | | public UserRepositoryPreferences getRepositoryPreferences(String repositoryName) { |
| | | String key = repositoryName.toLowerCase(); |
| | | if (!repositoryPreferences.containsKey(key)) { |
| | |
| | | } |
| | | return repositoryPreferences.get(key); |
| | | } |
| | | |
| | | |
| | | public void setRepositoryPreferences(UserRepositoryPreferences pref) { |
| | | repositoryPreferences.put(pref.repositoryName.toLowerCase(), pref); |
| | | } |
| | | |
| | | |
| | | public boolean isStarredRepository(String repository) { |
| | | if (repositoryPreferences == null) { |
| | | return false; |
| | |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | |
| | | public List<String> getStarredRepositories() { |
| | | List<String> list = new ArrayList<String>(); |
| | | for (UserRepositoryPreferences prefs : repositoryPreferences.values()) { |
| | |
| | | |
| | | /** |
| | | * User repository preferences. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public String username; |
| | | |
| | | |
| | | public String repositoryName; |
| | | |
| | | |
| | | public boolean starred; |
| | | |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return username + ":" + repositoryName; |
| | |
| | |
|
| | | /**
|
| | | * Utility class for building activity information from repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class ActivityUtils {
|
| | |
|
| | | /**
|
| | | * Gets the recent activity from the repositories for the last daysBack days
|
| | | * on the specified branch.
|
| | | * |
| | | *
|
| | | * @param models
|
| | | * the list of repositories to query
|
| | | * @param daysBack
|
| | |
| | | df.setTimeZone(timezone);
|
| | | Calendar cal = Calendar.getInstance();
|
| | | cal.setTimeZone(timezone);
|
| | | |
| | |
|
| | | // aggregate author exclusions
|
| | | Set<String> authorExclusions = new TreeSet<String>();
|
| | | authorExclusions.addAll(GitBlit.getStrings(Keys.web.metricAuthorExclusions));
|
| | |
| | | // trim commits to maximum count
|
| | | commits = commits.subList(0, model.maxActivityCommits);
|
| | | }
|
| | | for (RepositoryCommit commit : commits) { |
| | | for (RepositoryCommit commit : commits) {
|
| | | Date date = commit.getCommitDate();
|
| | | String dateStr = df.format(date);
|
| | | if (!activity.containsKey(dateStr)) {
|
| | |
| | | activity.get(dateStr).addCommit(commit);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // close the repository
|
| | | repository.close();
|
| | | }
|
| | |
| | | /**
|
| | | * Returns the Gravatar profile, if available, for the specified email
|
| | | * address.
|
| | | * |
| | | *
|
| | | * @param emailaddress
|
| | | * @return a Gravatar Profile
|
| | | * @throws IOException
|
| | |
| | |
|
| | | /**
|
| | | * Creates a Gravatar thumbnail url from the specified email address.
|
| | | * |
| | | *
|
| | | * @param email
|
| | | * address to query Gravatar
|
| | | * @param width
|
| | |
| | | "https://www.gravatar.com/avatar/{0}?s={1,number,0}&d=identicon", emailHash, width);
|
| | | return url;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Creates a Gravatar thumbnail url from the specified email address.
|
| | | * |
| | | *
|
| | | * @param email
|
| | | * address to query Gravatar
|
| | | * @param width
|
| | |
| | | /**
|
| | | * Returns the Gravatar profile, if available, for the specified hashcode.
|
| | | * address.
|
| | | * |
| | | *
|
| | | * @param hash
|
| | | * the hash of the email address
|
| | | * @return a Gravatar Profile
|
| | |
| | |
|
| | | /**
|
| | | * Utility class for arrays and collections.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class ArrayUtils {
|
| | |
|
| | |
| | | public static boolean isEmpty(Object [] array) {
|
| | | return array == null || array.length == 0;
|
| | | }
|
| | | |
| | |
|
| | | public static boolean isEmpty(Collection<?> collection) {
|
| | | return collection == null || collection.size() == 0;
|
| | | }
|
| | | |
| | |
|
| | | public static String toString(Collection<?> collection) {
|
| | | if (isEmpty(collection)) {
|
| | | return "";
|
| | |
| | | sb.setLength(sb.length() - 2);
|
| | | return sb.toString();
|
| | | }
|
| | | |
| | |
|
| | | public static Collection<String> fromString(String value) {
|
| | | if (StringUtils.isEmpty(value)) {
|
| | | value = "";
|
| | |
| | | * href="http://iharder.net/base64">http://iharder.net/base64</a> periodically
|
| | | * to check for updates or to contribute improvements.
|
| | | * </p>
|
| | | * |
| | | *
|
| | | * @author Robert Harder
|
| | | * @author rob@iharder.net
|
| | | * @version 2.1, stripped to minimum feature set used by JGit.
|
| | |
| | | * <var>destOffset</var> + 4 for the <var>destination</var> array. The
|
| | | * actual number of significant bytes in your array is given by
|
| | | * <var>numSigBytes</var>.
|
| | | * |
| | | *
|
| | | * @param source
|
| | | * the array to convert
|
| | | * @param srcOffset
|
| | |
| | |
|
| | | /**
|
| | | * Encodes a byte array into Base64 notation.
|
| | | * |
| | | *
|
| | | * @param source
|
| | | * The data to convert
|
| | | * @return encoded base64 representation of source.
|
| | |
| | |
|
| | | /**
|
| | | * Encodes a byte array into Base64 notation.
|
| | | * |
| | | *
|
| | | * @param source
|
| | | * The data to convert
|
| | | * @param off
|
| | |
| | | * <var>destOffset</var> + 3 for the <var>destination</var> array. This
|
| | | * method returns the actual number of bytes that were converted from the
|
| | | * Base64 encoding.
|
| | | * |
| | | *
|
| | | * @param source
|
| | | * the array to convert
|
| | | * @param srcOffset
|
| | |
| | |
|
| | | /**
|
| | | * Low-level decoding ASCII characters from a byte array.
|
| | | * |
| | | *
|
| | | * @param source
|
| | | * The Base64 encoded data
|
| | | * @param off
|
| | |
| | |
|
| | | /**
|
| | | * Decodes data from Base64 notation.
|
| | | * |
| | | *
|
| | | * @param s
|
| | | * the string to decode
|
| | | * @return the decoded data
|
| | |
| | | /** |
| | | * ByteFormat is a formatter which takes numbers and returns filesizes in bytes, |
| | | * kilobytes, megabytes, or gigabytes. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | * |
| | | */ |
| | | public class ByteFormat extends Format { |
| | | |
| | |
| | | return format(Long.valueOf(value)); |
| | | } |
| | | |
| | | @Override |
| | | public StringBuffer format(Object obj, StringBuffer buf, FieldPosition pos) { |
| | | if (obj instanceof Number) { |
| | | long numBytes = ((Number) obj).longValue(); |
| | |
| | | buf.append(formatter.format((double) numBytes)).append(" b"); |
| | | } else if (numBytes < 1024 * 1024) { |
| | | DecimalFormat formatter = new DecimalFormat("#,##0"); |
| | | buf.append(formatter.format((double) numBytes / 1024.0)).append(" KB"); |
| | | buf.append(formatter.format(numBytes / 1024.0)).append(" KB"); |
| | | } else if (numBytes < 1024 * 1024 * 1024) { |
| | | DecimalFormat formatter = new DecimalFormat("#,##0.0"); |
| | | buf.append(formatter.format((double) numBytes / (1024.0 * 1024.0))).append(" MB"); |
| | | buf.append(formatter.format(numBytes / (1024.0 * 1024.0))).append(" MB"); |
| | | } else { |
| | | DecimalFormat formatter = new DecimalFormat("#,##0.0"); |
| | | buf.append(formatter.format((double) numBytes / (1024.0 * 1024.0 * 1024.0))) |
| | | buf.append(formatter.format(numBytes / (1024.0 * 1024.0 * 1024.0))) |
| | | .append(" GB"); |
| | | } |
| | | } |
| | | return buf; |
| | | } |
| | | |
| | | @Override |
| | | public Object parseObject(String source, ParsePosition pos) { |
| | | return null; |
| | | } |
| | |
| | | /** |
| | | * Class to log messages to the pushing Git client. Intended to be used by the |
| | | * Groovy Hooks. |
| | | * |
| | | * |
| | | * @author John Crygier |
| | | * |
| | | * |
| | | */ |
| | | public class ClientLogger { |
| | | |
| | |
| | | |
| | | /** |
| | | * Sends an info/warning message to the git client. |
| | | * |
| | | * |
| | | * @param message |
| | | */ |
| | | public void info(String message) { |
| | |
| | | |
| | | /** |
| | | * Sends an error message to the git client. |
| | | * |
| | | * |
| | | * @param message |
| | | */ |
| | | public void error(String message) { |
| | |
| | | |
| | | /** |
| | | * Sends an error message to the git client with an exception. |
| | | * |
| | | * |
| | | * @param message |
| | | * @param t |
| | | * an exception |
| | |
| | | |
| | | /** |
| | | * Caches repository commits for re-use in the dashboard and activity pages. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class CommitCache { |
| | | |
| | | |
| | | private static final CommitCache instance; |
| | | |
| | | |
| | | protected final Logger logger = LoggerFactory.getLogger(getClass()); |
| | | |
| | | |
| | | protected final Map<String, ObjectCache<List<RepositoryCommit>>> cache; |
| | | |
| | | |
| | | protected int cacheDays = -1; |
| | | |
| | | |
| | | public static CommitCache instance() { |
| | | return instance; |
| | | } |
| | | |
| | | |
| | | static { |
| | | instance = new CommitCache(); |
| | | } |
| | | |
| | | |
| | | protected CommitCache() { |
| | | cache = new ConcurrentHashMap<String, ObjectCache<List<RepositoryCommit>>>(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the cutoff date for the cache. Commits after this date are cached. |
| | | * Commits before this date are not cached. |
| | | * |
| | | * |
| | | * @return |
| | | */ |
| | | public Date getCutoffDate() { |
| | |
| | | cal.add(Calendar.DATE, -1*cacheDays); |
| | | return cal.getTime(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Sets the number of days to cache. |
| | | * |
| | | * |
| | | * @param days |
| | | */ |
| | | public synchronized void setCacheDays(int days) { |
| | | this.cacheDays = days; |
| | | clear(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Clears the entire commit cache. |
| | | * |
| | | * |
| | | */ |
| | | public void clear() { |
| | | cache.clear(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Clears the commit cache for a specific repository. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | */ |
| | | public void clear(String repositoryName) { |
| | |
| | | logger.info(MessageFormat.format("{0} commit cache cleared", repositoryName)); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Clears the commit cache for a specific branch of a specific repository. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param branch |
| | | */ |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get all commits for the specified repository:branch that are in the cache. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param branch |
| | |
| | | public List<RepositoryCommit> getCommits(String repositoryName, Repository repository, String branch) { |
| | | return getCommits(repositoryName, repository, branch, getCutoffDate()); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get all commits for the specified repository:branch since a specific date. |
| | | * These commits may be retrieved from the cache if the sinceDate is after |
| | | * the cacheCutoffDate. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param branch |
| | |
| | | if (!cache.containsKey(repoKey)) { |
| | | cache.put(repoKey, new ObjectCache<List<RepositoryCommit>>()); |
| | | } |
| | | |
| | | |
| | | ObjectCache<List<RepositoryCommit>> repoCache = cache.get(repoKey); |
| | | String branchKey = branch.toLowerCase(); |
| | | |
| | | |
| | | RevCommit tip = JGitUtils.getCommit(repository, branch); |
| | | Date tipDate = JGitUtils.getCommitDate(tip); |
| | | |
| | | |
| | | List<RepositoryCommit> commits; |
| | | if (!repoCache.hasCurrent(branchKey, tipDate)) { |
| | | commits = repoCache.getObject(branchKey); |
| | |
| | | // update cache |
| | | repoCache.updateObject(branchKey, tipDate, commits); |
| | | } |
| | | |
| | | |
| | | if (sinceDate.equals(cacheCutoffDate)) { |
| | | list = commits; |
| | | } else { |
| | |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns a list of commits for the specified repository branch. |
| | | * |
| | | * Returns a list of commits for the specified repository branch. |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param branch |
| | |
| | | } |
| | | return commits; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns a list of commits for the specified repository branch since the specified commit. |
| | | * |
| | | * Returns a list of commits for the specified repository branch since the specified commit. |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param branch |
| | |
| | | } |
| | | return commits; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Reduces the list of commits to those since the specified date. |
| | | * |
| | | * |
| | | * @param commits |
| | | * @param sinceDate |
| | | * @return a list of commits |
| | |
| | |
|
| | | /**
|
| | | * Collection of static methods for retrieving information from a repository.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class CompressionUtils {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Log an error message and exception.
|
| | | * |
| | | *
|
| | | * @param t
|
| | | * @param repository
|
| | | * if repository is not null it MUST be the {0} parameter in the
|
| | |
| | | /**
|
| | | * Zips the contents of the tree at the (optionally) specified revision and
|
| | | * the (optionally) specified basepath to the supplied outputstream.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param basePath
|
| | | * if unspecified, entire repository is assumed.
|
| | |
| | | }
|
| | | return success;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * tar the contents of the tree at the (optionally) specified revision and
|
| | | * the (optionally) specified basepath to the supplied outputstream.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param basePath
|
| | | * if unspecified, entire repository is assumed.
|
| | |
| | | OutputStream os) {
|
| | | return tar(null, repository, basePath, objectId, os);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * tar.gz the contents of the tree at the (optionally) specified revision and
|
| | | * the (optionally) specified basepath to the supplied outputstream.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param basePath
|
| | | * if unspecified, entire repository is assumed.
|
| | |
| | | OutputStream os) {
|
| | | return tar(CompressorStreamFactory.GZIP, repository, basePath, objectId, os);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * tar.xz the contents of the tree at the (optionally) specified revision and
|
| | | * the (optionally) specified basepath to the supplied outputstream.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param basePath
|
| | | * if unspecified, entire repository is assumed.
|
| | |
| | | OutputStream os) {
|
| | | return tar(CompressorStreamFactory.XZ, repository, basePath, objectId, os);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * tar.bzip2 the contents of the tree at the (optionally) specified revision and
|
| | | * the (optionally) specified basepath to the supplied outputstream.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param basePath
|
| | | * if unspecified, entire repository is assumed.
|
| | |
| | | */
|
| | | public static boolean bzip2(Repository repository, String basePath, String objectId,
|
| | | OutputStream os) {
|
| | | |
| | |
|
| | | return tar(CompressorStreamFactory.BZIP2, repository, basePath, objectId, os);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Compresses/archives the contents of the tree at the (optionally)
|
| | | * specified revision and the (optionally) specified basepath to the
|
| | | * supplied outputstream.
|
| | | * |
| | | *
|
| | | * @param algorithm
|
| | | * compression algorithm for tar (optional)
|
| | | * @param repository
|
| | |
| | | if (commit == null) {
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | OutputStream cos = os;
|
| | | if (!StringUtils.isEmpty(algorithm)) {
|
| | | try {
|
| | |
| | | continue;
|
| | | }
|
| | | tw.getObjectId(id, 0);
|
| | | |
| | |
|
| | | ObjectLoader loader = repository.open(id);
|
| | | if (FileMode.SYMLINK == mode) {
|
| | | TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString(),TarArchiveEntry.LF_SYMLINK);
|
| | |
| | | entry.setMode(mode.getBits());
|
| | | entry.setModTime(modified);
|
| | | entry.setSize(loader.getSize());
|
| | | tos.putArchiveEntry(entry); |
| | | tos.putArchiveEntry(entry);
|
| | | loader.copyTo(tos);
|
| | | tos.closeArchiveEntry();
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Utility class for establishing HTTP/HTTPS connections.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class ConnectionUtils {
|
| | |
|
| | |
| | | SSL_CONTEXT = context;
|
| | | HOSTNAME_VERIFIER = new DummyHostnameVerifier();
|
| | | CHARSET = "UTF-8";
|
| | | |
| | |
|
| | | // Disable Java 7 SNI checks
|
| | | // http://stackoverflow.com/questions/7615645/ssl-handshake-alert-unrecognized-name-error-since-upgrade-to-java-1-7-0
|
| | | System.setProperty("jsse.enableSNIExtension", "false");
|
| | |
| | | }
|
| | | return conn;
|
| | | }
|
| | | |
| | |
|
| | | // Copyright (C) 2009 The Android Open Source Project
|
| | | //
|
| | | // Licensed under the Apache License, Version 2.0 (the "License");
|
| | |
| | |
|
| | | /**
|
| | | * DummyTrustManager trusts all certificates.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | private static class DummyTrustManager implements X509TrustManager {
|
| | |
| | |
|
| | | /**
|
| | | * Trusts all hostnames from a certificate, including self-signed certs.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | */
|
| | | private static class DummyHostnameVerifier implements HostnameVerifier {
|
| | |
| | |
|
| | | /**
|
| | | * This is the support class for all container specific code.
|
| | | * |
| | | *
|
| | | * @author jpyeron
|
| | | */
|
| | | public class ContainerUtils
|
| | |
| | | /**
|
| | | * The support class for managing and evaluating the environment with
|
| | | * regards to CVE-2007-0405.
|
| | | * |
| | | *
|
| | | * @see http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0450
|
| | | * @author jpyeron
|
| | | */
|
| | |
| | | * blocked from use in certain URL s. It will emit a warning to the
|
| | | * logger if the configuration of Tomcat causes the URL processing to
|
| | | * fail on %2F.
|
| | | * |
| | | *
|
| | | * @return true if it recognizes Tomcat, false if it does not recognize
|
| | | * Tomcat
|
| | | */
|
| | |
| | |
|
| | | // mb.setBytes(test, 0, test.length);
|
| | | Method mByteChunck_setBytes = cByteChunk.getMethod("setBytes", byte[].class, int.class, int.class);
|
| | | mByteChunck_setBytes.invoke(mb, test, (int) 0, test.length);
|
| | | mByteChunck_setBytes.invoke(mb, test, 0, test.length);
|
| | |
|
| | | // UDecoder ud=new UDecoder();
|
| | | Class<?> cUDecoder = Class.forName("org.apache.tomcat.util.buf.UDecoder");
|
| | |
| | | * very large objects. The current thread is used for serializing the
|
| | | * original object in order to respect any synchronization the caller may
|
| | | * have around it, and a new thread is used for deserializing the copy.
|
| | | * |
| | | *
|
| | | */
|
| | | public static <T> T copyParallel(T original) {
|
| | | try {
|
| | |
| | | start();
|
| | | }
|
| | |
|
| | | @Override
|
| | | @SuppressWarnings("unchecked")
|
| | | public void run() {
|
| | |
|
| | |
| | | return null;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Encapsulates the output of a diff. |
| | | * Encapsulates the output of a diff.
|
| | | */
|
| | | public static class DiffOutput implements Serializable {
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | public final DiffOutputType type;
|
| | | public final String content;
|
| | | public final DiffStat stat;
|
| | | |
| | |
|
| | | DiffOutput(DiffOutputType type, String content, DiffStat stat) {
|
| | | this.type = type;
|
| | | this.content = content;
|
| | | this.stat = stat;
|
| | | }
|
| | | |
| | |
|
| | | public PathChangeModel getPath(String path) {
|
| | | if (stat == null) {
|
| | | return null;
|
| | |
| | | public static class DiffStat implements Serializable {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | public final List<PathChangeModel> paths = new ArrayList<PathChangeModel>();
|
| | | |
| | |
|
| | | private final String commitId;
|
| | | |
| | |
|
| | | public DiffStat(String commitId) {
|
| | | this.commitId = commitId;
|
| | | }
|
| | | |
| | |
|
| | | public PathChangeModel addPath(DiffEntry entry) {
|
| | | PathChangeModel pcm = PathChangeModel.from(entry, commitId);
|
| | | paths.add(pcm);
|
| | |
| | | }
|
| | | return val;
|
| | | }
|
| | | |
| | |
|
| | | public PathChangeModel getPath(String path) {
|
| | | PathChangeModel stat = null;
|
| | | for (PathChangeModel p : paths) {
|
| | |
| | | }
|
| | | }
|
| | | return stat;
|
| | | } |
| | | }
|
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | |
| | | return sb.toString();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static class NormalizedDiffStat implements Serializable {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | public final int insertions;
|
| | | public final int deletions;
|
| | | public final int blanks;
|
| | | |
| | |
|
| | | NormalizedDiffStat(int insertions, int deletions, int blanks) {
|
| | | this.insertions = insertions;
|
| | | this.deletions = deletions;
|
| | |
| | | } catch (Throwable t) {
|
| | | LOGGER.error("failed to generate commit diff!", t);
|
| | | }
|
| | | |
| | |
|
| | | return new DiffOutput(outputType, diff, stat);
|
| | | }
|
| | |
|
| | |
| | | }
|
| | | return lines;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Normalizes a diffstat to an N-segment display.
|
| | | * |
| | | *
|
| | | * @params segments
|
| | | * @param insertions
|
| | | * @param deletions
|
| | |
| | | sd = segments - si;
|
| | | sb = 0;
|
| | | }
|
| | | |
| | |
|
| | | return new NormalizedDiffStat(si, sd, sb);
|
| | | }
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Utility methods for federation functions.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class FederationUtils {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns an url to this servlet for the specified parameters.
|
| | | * |
| | | *
|
| | | * @param sourceURL
|
| | | * the url of the source gitblit instance
|
| | | * @param token
|
| | |
| | | }
|
| | |
|
| | | /**
|
| | | * |
| | | *
|
| | | * @param remoteURL
|
| | | * the url of the remote gitblit instance
|
| | | * @param tokenType
|
| | |
| | | /**
|
| | | * Returns the list of federated gitblit instances that this instance will
|
| | | * try to pull.
|
| | | * |
| | | *
|
| | | * @return list of registered gitblit instances
|
| | | */
|
| | | public static List<FederationModel> getFederationRegistrations(IStoredSettings settings) {
|
| | |
| | | * sent by an pulling Gitblit instance to an origin Gitblit instance as part
|
| | | * of the proposal process. This is to ensure that the pulling Gitblit
|
| | | * instance has an IP route to the origin instance.
|
| | | * |
| | | *
|
| | | * @param remoteUrl
|
| | | * the remote Gitblit instance to send a federation proposal to
|
| | | * @param proposal
|
| | |
| | |
|
| | | /**
|
| | | * Sends a federation proposal to the Gitblit instance at remoteUrl
|
| | | * |
| | | *
|
| | | * @param remoteUrl
|
| | | * the remote Gitblit instance to send a federation proposal to
|
| | | * @param proposal
|
| | |
| | | /**
|
| | | * Retrieves a map of the repositories at the remote gitblit instance keyed
|
| | | * by the repository clone url.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | * @param checkExclusions
|
| | | * should returned repositories remove registration exclusions
|
| | |
| | |
|
| | | /**
|
| | | * Tries to pull the gitblit user accounts from the remote gitblit instance.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | * @return a collection of UserModel objects
|
| | | * @throws Exception
|
| | |
| | | /**
|
| | | * Tries to pull the gitblit team definitions from the remote gitblit
|
| | | * instance.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | * @return a collection of TeamModel objects
|
| | | * @throws Exception
|
| | |
| | | /**
|
| | | * Tries to pull the gitblit server settings from the remote gitblit
|
| | | * instance.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | * @return a map of the remote gitblit settings
|
| | | * @throws Exception
|
| | |
| | |
|
| | | /**
|
| | | * Tries to pull the referenced scripts from the remote gitblit instance.
|
| | | * |
| | | *
|
| | | * @param registration
|
| | | * @return a map of the remote gitblit scripts by script name
|
| | | * @throws Exception
|
| | |
| | |
|
| | | /**
|
| | | * Send an status acknowledgment to the remote Gitblit server.
|
| | | * |
| | | *
|
| | | * @param identification
|
| | | * identification of this pulling instance
|
| | | * @param registration
|
| | |
| | |
|
| | | /**
|
| | | * Common file utilities.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class FileUtils {
|
| | | |
| | |
|
| | | /** 1024 (number of bytes in one kilobyte) */
|
| | | public static final int KB = 1024;
|
| | |
|
| | |
| | | /**
|
| | | * Returns an int from a string representation of a file size.
|
| | | * e.g. 50m = 50 megabytes
|
| | | * |
| | | *
|
| | | * @param aString
|
| | | * @param defaultValue
|
| | | * @return an int value or the defaultValue if aString can not be parsed
|
| | |
| | | public static int convertSizeToInt(String aString, int defaultValue) {
|
| | | return (int) convertSizeToLong(aString, defaultValue);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a long from a string representation of a file size.
|
| | | * e.g. 50m = 50 megabytes
|
| | | * |
| | | *
|
| | | * @param aString
|
| | | * @param defaultValue
|
| | | * @return a long value or the defaultValue if aString can not be parsed
|
| | | */
|
| | | public static long convertSizeToLong(String aString, long defaultValue) {
|
| | | // trim string and remove all spaces |
| | | // trim string and remove all spaces
|
| | | aString = aString.toLowerCase().trim();
|
| | | StringBuilder sb = new StringBuilder();
|
| | | for (String a : aString.split(" ")) {
|
| | | sb.append(a);
|
| | | }
|
| | | aString = sb.toString();
|
| | | |
| | |
|
| | | // identify value and unit
|
| | | int idx = 0;
|
| | | int len = aString.length();
|
| | |
| | | }
|
| | | return defaultValue;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the byte [] content of the specified file.
|
| | | * |
| | | *
|
| | | * @param file
|
| | | * @return the byte content of the file
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the string content of the specified file.
|
| | | * |
| | | *
|
| | | * @param file
|
| | | * @param lineEnding
|
| | | * @return the string content of the file
|
| | |
| | |
|
| | | /**
|
| | | * Writes the string content to the file.
|
| | | * |
| | | *
|
| | | * @param file
|
| | | * @param content
|
| | | */
|
| | |
| | | /**
|
| | | * Recursively traverses a folder and its subfolders to calculate the total
|
| | | * size in bytes.
|
| | | * |
| | | *
|
| | | * @param directory
|
| | | * @return folder size in bytes
|
| | | */
|
| | | public static long folderSize(File directory) {
|
| | | if (directory == null || !directory.exists()) {
|
| | | return -1;
|
| | | } |
| | | }
|
| | | if (directory.isDirectory()) {
|
| | | long length = 0;
|
| | | for (File file : directory.listFiles()) {
|
| | |
| | |
|
| | | /**
|
| | | * Copies a file or folder (recursively) to a destination folder.
|
| | | * |
| | | *
|
| | | * @param destinationFolder
|
| | | * @param filesOrFolders
|
| | | * @return
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Determine the relative path between two files. Takes into account
|
| | | * canonical paths, if possible.
|
| | | * |
| | | *
|
| | | * @param basePath
|
| | | * @param path
|
| | | * @return a relative path from basePath to path
|
| | |
| | | // no relative relationship
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the exact path for a file. This path will be the canonical path
|
| | | * unless an exception is thrown in which case it will be the absolute path.
|
| | | * |
| | | *
|
| | | * @param path
|
| | | * @return the exact file
|
| | | */
|
| | |
| | |
|
| | | public static File resolveParameter(String parameter, File aFolder, String path) {
|
| | | if (aFolder == null) {
|
| | | // strip any parameter reference |
| | | // strip any parameter reference
|
| | | path = path.replace(parameter, "").trim();
|
| | | if (path.length() > 0 && path.charAt(0) == '/') {
|
| | | // strip leading /
|
| | |
| | | /**
|
| | | * Generates an html snippet of a diff in Gitblit's style, tracks changed paths,
|
| | | * and calculates diff stats.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitBlitDiffFormatter extends DiffFormatter {
|
| | |
|
| | |
| | | private PathChangeModel currentPath;
|
| | |
|
| | | private int left, right;
|
| | | |
| | |
|
| | | public GitBlitDiffFormatter(OutputStream os, String commitId) {
|
| | | super(os);
|
| | | this.os = os;
|
| | | this.diffStat = new DiffStat(commitId);
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | public void format(DiffEntry ent) throws IOException {
|
| | | currentPath = diffStat.addPath(ent);
|
| | | super.format(ent);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Output a hunk header
|
| | | * |
| | | *
|
| | | * @param aStartLine
|
| | | * within first source
|
| | | * @param aEndLine
|
| | |
| | | left = aStartLine + 1;
|
| | | right = bStartLine + 1;
|
| | | }
|
| | | |
| | |
|
| | | protected void writeRange(final char prefix, final int begin, final int cnt) throws IOException {
|
| | | os.write(' ');
|
| | | os.write(prefix);
|
| | |
| | | throws IOException {
|
| | | // update entry diffstat
|
| | | currentPath.update(prefix);
|
| | | |
| | |
|
| | | // output diff
|
| | | os.write("<tr>".getBytes());
|
| | | switch (prefix) {
|
| | |
| | | /**
|
| | | * Workaround function for complex private methods in DiffFormatter. This
|
| | | * sets the html for the diff headers.
|
| | | * |
| | | *
|
| | | * @return
|
| | | */
|
| | | public String getHtml() {
|
| | |
| | | } else {
|
| | | // use a
|
| | | line = line.substring("diff --git ".length()).trim();
|
| | | line = line.substring(line.startsWith("\"a/") ? 3 : 2); |
| | | line = line.substring(line.startsWith("\"a/") ? 3 : 2);
|
| | | line = line.substring(0, line.indexOf(" b/") > -1 ? line.indexOf(" b/") : line.indexOf("\"b/")).trim();
|
| | | }
|
| | | |
| | |
|
| | | if (line.charAt(0) == '"') {
|
| | | line = line.substring(1);
|
| | | }
|
| | |
| | | sb.append("</tbody></table></div>\n");
|
| | | inFile = false;
|
| | | }
|
| | | |
| | |
|
| | | sb.append(MessageFormat.format("<div class='header'><div class=\"diffHeader\" id=\"{0}\"><i class=\"icon-file\"></i> ", line)).append(line).append("</div></div>");
|
| | | sb.append("<div class=\"diff\">");
|
| | | sb.append("<table><tbody>");
|
| | |
| | | sb.append("</table></div>");
|
| | | return sb.toString();
|
| | | }
|
| | | |
| | |
|
| | | public DiffStat getDiffStat() {
|
| | | return diffStat;
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Collection of utility methods for http requests.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class HttpUtils {
|
| | |
|
| | | /**
|
| | | * Returns the Gitblit URL based on the request.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @return the host url
|
| | | */
|
| | |
| | | } catch (Throwable t) {
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // try to use reverse-proxy server's scheme
|
| | | String forwardedScheme = request.getHeader("X-Forwarded-Proto");
|
| | | if (StringUtils.isEmpty(forwardedScheme)) {
|
| | |
| | | if (!StringUtils.isEmpty(forwardedScheme)) {
|
| | | // reverse-proxy server has supplied the original scheme
|
| | | scheme = forwardedScheme;
|
| | | |
| | |
|
| | | if ("https".equals(scheme) && port == 80) {
|
| | | // proxy server is https, inside server is 80
|
| | | // this is likely because the proxy server has not supplied
|
| | |
| | | port = 443;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | String context = request.getContextPath();
|
| | | String forwardedContext = request.getHeader("X-Forwarded-Context");
|
| | | if (forwardedContext != null) {
|
| | |
| | | if (!StringUtils.isEmpty(forwardedContext)) {
|
| | | context = forwardedContext;
|
| | | }
|
| | | |
| | |
|
| | | // trim any trailing slash
|
| | | if (context.length() > 0 && context.charAt(context.length() - 1) == '/') {
|
| | | context = context.substring(1);
|
| | | }
|
| | | |
| | |
|
| | | StringBuilder sb = new StringBuilder();
|
| | | sb.append(scheme);
|
| | | sb.append("://");
|
| | |
| | | sb.append(context);
|
| | | return sb.toString();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns a user model object built from attributes in the SSL certificate.
|
| | | * This model is not retrieved from the user service.
|
| | | * |
| | | *
|
| | | * @param httpRequest
|
| | | * @param checkValidity ensure certificate can be used now
|
| | | * @param usernameOIDs if unspecified, CN is used as the username
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Creates a UserModel from a certificate
|
| | | * @param cert
|
| | |
| | | */
|
| | | public static UserModel getUserModelFromCertificate(X509Certificate cert, String... usernameOIDs) {
|
| | | X509Metadata metadata = X509Utils.getMetadata(cert);
|
| | | |
| | |
|
| | | UserModel user = new UserModel(metadata.commonName);
|
| | | user.emailAddress = metadata.emailAddress;
|
| | | user.isAuthenticated = false;
|
| | | |
| | |
|
| | | if (usernameOIDs == null || usernameOIDs.length == 0) {
|
| | | // use default usename<->CN mapping
|
| | | usernameOIDs = new String [] { "CN" };
|
| | | }
|
| | | |
| | |
|
| | | // determine username from OID fingerprint
|
| | | StringBuilder an = new StringBuilder();
|
| | | for (String oid : usernameOIDs) {
|
| | |
| | | an.append(val).append(' ');
|
| | | }
|
| | | }
|
| | | user.username = an.toString().trim(); |
| | | user.username = an.toString().trim();
|
| | | return user;
|
| | | }
|
| | | |
| | |
|
| | | public static X509Metadata getCertificateMetadata(HttpServletRequest httpRequest) {
|
| | | if (httpRequest.getAttribute("javax.servlet.request.X509Certificate") != null) {
|
| | | X509Certificate[] certChain = (X509Certificate[]) httpRequest
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | public static boolean isIpAddress(String address) {
|
| | | if (StringUtils.isEmpty(address)) {
|
| | | return false;
|
| | |
| | |
|
| | | /**
|
| | | * Collection of static methods for retrieving information from a repository.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class JGitUtils {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Log an error message and exception.
|
| | | * |
| | | *
|
| | | * @param t
|
| | | * @param repository
|
| | | * if repository is not null it MUST be the {0} parameter in the
|
| | |
| | | /**
|
| | | * Returns the displayable name of the person in the form "Real Name <email
|
| | | * address>". If the email address is empty, just "Real Name" is returned.
|
| | | * |
| | | *
|
| | | * @param person
|
| | | * @return "Real Name <email address>" or "Real Name"
|
| | | */
|
| | |
| | | * Clone or Fetch a repository. If the local repository does not exist,
|
| | | * clone is called. If the repository does exist, fetch is called. By
|
| | | * default the clone/fetch retrieves the remote heads, tags, and notes.
|
| | | * |
| | | *
|
| | | * @param repositoriesFolder
|
| | | * @param name
|
| | | * @param fromUrl
|
| | |
| | | * Clone or Fetch a repository. If the local repository does not exist,
|
| | | * clone is called. If the repository does exist, fetch is called. By
|
| | | * default the clone/fetch retrieves the remote heads, tags, and notes.
|
| | | * |
| | | *
|
| | | * @param repositoriesFolder
|
| | | * @param name
|
| | | * @param fromUrl
|
| | |
| | | clone.setCredentialsProvider(credentialsProvider);
|
| | | }
|
| | | Repository repository = clone.call().getRepository();
|
| | | |
| | |
|
| | | // Now we have to fetch because CloneCommand doesn't fetch
|
| | | // refs/notes nor does it allow manual RefSpec.
|
| | | result.createdRepository = true;
|
| | |
| | | /**
|
| | | * Fetch updates from the remote repository. If refSpecs is unspecifed,
|
| | | * remote heads, tags, and notes are retrieved.
|
| | | * |
| | | *
|
| | | * @param credentialsProvider
|
| | | * @param repository
|
| | | * @param refSpecs
|
| | |
| | |
|
| | | /**
|
| | | * Creates a bare repository.
|
| | | * |
| | | *
|
| | | * @param repositoriesFolder
|
| | | * @param name
|
| | | * @return Repository
|
| | |
| | |
|
| | | /**
|
| | | * Returns a list of repository names in the specified folder.
|
| | | * |
| | | *
|
| | | * @param repositoriesFolder
|
| | | * @param onlyBare
|
| | | * if true, only bare repositories repositories are listed. If
|
| | |
| | |
|
| | | /**
|
| | | * Recursive function to find git repositories.
|
| | | * |
| | | *
|
| | | * @param basePath
|
| | | * basePath is stripped from the repository name as repositories
|
| | | * are relative to this path
|
| | |
| | | if (depth == 0) {
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | int nextDepth = (depth == -1) ? -1 : depth - 1;
|
| | | for (File file : searchFolder.listFiles()) {
|
| | | if (file.isDirectory()) {
|
| | |
| | | /**
|
| | | * Returns the first commit on a branch. If the repository does not exist or
|
| | | * is empty, null is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param branch
|
| | | * if unspecified, HEAD is assumed.
|
| | |
| | | * Returns the date of the first commit on a branch. If the repository does
|
| | | * not exist, Date(0) is returned. If the repository does exist bit is
|
| | | * empty, the last modified date of the repository folder is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param branch
|
| | | * if unspecified, HEAD is assumed.
|
| | |
| | | /**
|
| | | * Determine if a repository has any commits. This is determined by checking
|
| | | * the for loose and packed objects.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return true if the repository has commits
|
| | | */
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Encapsulates the result of cloning or pulling from a repository.
|
| | | */
|
| | | public static class LastChange {
|
| | | public Date when;
|
| | | public String who;
|
| | | |
| | |
|
| | | LastChange() {
|
| | | when = new Date(0); |
| | | when = new Date(0);
|
| | | }
|
| | | |
| | |
|
| | | LastChange(long lastModified) {
|
| | | this.when = new Date(lastModified);
|
| | | }
|
| | |
| | | * Returns the date and author of the most recent commit on a branch. If the
|
| | | * repository does not exist Date(0) is returned. If it does exist but is
|
| | | * empty, the last modified date of the repository folder is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return a LastChange object
|
| | | */
|
| | |
| | | List<RefModel> branchModels = getLocalBranches(repository, true, -1);
|
| | | if (branchModels.size() > 0) {
|
| | | // find most recent branch update
|
| | | LastChange lastChange = new LastChange(); |
| | | LastChange lastChange = new LastChange();
|
| | | for (RefModel branchModel : branchModels) {
|
| | | if (branchModel.getDate().after(lastChange.when)) {
|
| | | lastChange.when = branchModel.getDate();
|
| | |
| | | }
|
| | | return lastChange;
|
| | | }
|
| | | |
| | |
|
| | | // default to the repository folder modification date
|
| | | return new LastChange(repository.getDirectory().lastModified());
|
| | | }
|
| | |
|
| | | /**
|
| | | * Retrieves a Java Date from a Git commit.
|
| | | * |
| | | *
|
| | | * @param commit
|
| | | * @return date of the commit or Date(0) if the commit is null
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves a Java Date from a Git commit.
|
| | | * |
| | | *
|
| | | * @param commit
|
| | | * @return date of the commit or Date(0) if the commit is null
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the specified commit from the repository. If the repository does
|
| | | * not exist or is empty, null is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * if unspecified, HEAD is assumed.
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves the raw byte content of a file in the specified tree.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param tree
|
| | | * if null, the RevTree from HEAD is assumed.
|
| | |
| | |
|
| | | /**
|
| | | * Returns the UTF-8 string content of a file in the specified tree.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param tree
|
| | | * if null, the RevTree from HEAD is assumed.
|
| | |
| | |
|
| | | /**
|
| | | * Gets the raw byte content of the specified blob object.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * @return byte [] blob content
|
| | |
| | |
|
| | | /**
|
| | | * Gets the UTF-8 string content of the blob specified by objectId.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * @param charsets optional
|
| | |
| | | * Returns the list of files in the specified folder at the specified
|
| | | * commit. If the repository does not exist or is empty, an empty list is
|
| | | * returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param path
|
| | | * if unspecified, root folder is assumed.
|
| | |
| | | Collections.sort(list);
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of files changed in a specified commit. If the
|
| | | * repository does not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param commit
|
| | | * if null, HEAD is assumed.
|
| | |
| | | /**
|
| | | * Returns the list of files changed in a specified commit. If the
|
| | | * repository does not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param commit
|
| | | * if null, HEAD is assumed.
|
| | |
| | | for (DiffEntry diff : diffs) {
|
| | | // create the path change model
|
| | | PathChangeModel pcm = PathChangeModel.from(diff, commit.getName());
|
| | | |
| | |
|
| | | if (calculateDiffStat) {
|
| | | // update file diffstats
|
| | | df.format(diff);
|
| | |
| | | /**
|
| | | * Returns the list of files changed in a specified commit. If the
|
| | | * repository does not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param startCommit
|
| | | * earliest commit
|
| | |
| | | for (DiffEntry diff : diffEntries) {
|
| | | PathChangeModel pcm = PathChangeModel.from(diff, null);
|
| | | list.add(pcm);
|
| | | } |
| | | }
|
| | | Collections.sort(list);
|
| | | } catch (Throwable t) {
|
| | | error(t, repository, "{0} failed to determine files in range {1}..{2}!", startCommit, endCommit);
|
| | |
| | | * Returns the list of files in the repository on the default branch that
|
| | | * match one of the specified extensions. This is a CASE-SENSITIVE search.
|
| | | * If the repository does not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param extensions
|
| | | * @return list of files in repository with a matching extension
|
| | |
| | | * Returns the list of files in the repository in the specified commit that
|
| | | * match one of the specified extensions. This is a CASE-SENSITIVE search.
|
| | | * If the repository does not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param extensions
|
| | | * @param objectId
|
| | |
| | |
|
| | | /**
|
| | | * Returns a path model of the current file in the treewalk.
|
| | | * |
| | | *
|
| | | * @param tw
|
| | | * @param basePath
|
| | | * @param commit
|
| | |
| | |
|
| | | /**
|
| | | * Returns a permissions representation of the mode bits.
|
| | | * |
| | | *
|
| | | * @param mode
|
| | | * @return string representation of the mode bits
|
| | | */
|
| | |
| | | /**
|
| | | * Returns a list of commits since the minimum date starting from the
|
| | | * specified object id.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * if unspecified, HEAD is assumed.
|
| | |
| | |
|
| | | /**
|
| | | * Returns a list of commits starting from HEAD and working backwards.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param maxCount
|
| | | * if < 0, all commits for the repository are returned.
|
| | |
| | | * offset and maxCount for paging. This is similar to LIMIT n OFFSET p in
|
| | | * SQL. If the repository does not exist or is empty, an empty list is
|
| | | * returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * if unspecified, HEAD is assumed.
|
| | |
| | | * repository. Caller may specify ending revision with objectId. Caller may
|
| | | * specify offset and maxCount to achieve pagination of results. If the
|
| | | * repository does not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * if unspecified, HEAD is assumed.
|
| | |
| | | RevWalk rw = new RevWalk(repository);
|
| | | rw.markStart(rw.parseCommit(endRange));
|
| | | if (startRange != null) {
|
| | | rw.markUninteresting(rw.parseCommit(startRange)); |
| | | rw.markUninteresting(rw.parseCommit(startRange));
|
| | | }
|
| | | if (!StringUtils.isEmpty(path)) {
|
| | | TreeFilter filter = AndTreeFilter.create(
|
| | |
| | | * Returns a list of commits for the repository within the range specified
|
| | | * by startRangeId and endRangeId. If the repository does not exist or is
|
| | | * empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param startRangeId
|
| | | * the first commit (not included in results)
|
| | |
| | | * Search results require a specified SearchType of AUTHOR, COMMITTER, or
|
| | | * COMMIT. Results may be paginated using offset and maxCount. If the
|
| | | * repository does not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * if unspecified, HEAD is assumed.
|
| | |
| | | * Returns the default branch to use for a repository. Normally returns
|
| | | * whatever branch HEAD points to, but if HEAD points to nothing it returns
|
| | | * the most recently updated branch.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return the objectid of a branch
|
| | | * @throws Exception
|
| | |
| | | }
|
| | | return target;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Sets the symbolic ref HEAD to the specified target ref. The
|
| | | * HEAD will be detached if the target ref is not a branch.
|
| | |
| | | case FORCED:
|
| | | case NO_CHANGE:
|
| | | case FAST_FORWARD:
|
| | | return true; |
| | | return true;
|
| | | default:
|
| | | LOGGER.error(MessageFormat.format("{0} HEAD update to {1} returned result {2}",
|
| | | repository.getDirectory().getAbsolutePath(), targetRef, result));
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Sets the local branch ref to point to the specified commit id.
|
| | | *
|
| | |
| | | case FORCED:
|
| | | case NO_CHANGE:
|
| | | case FAST_FORWARD:
|
| | | return true; |
| | | return true;
|
| | | default:
|
| | | LOGGER.error(MessageFormat.format("{0} {1} update to {2} returned result {3}",
|
| | | repository.getDirectory().getAbsolutePath(), branchName, commitId, result));
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Deletes the specified branch ref.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param branch
|
| | | * @return true if successful
|
| | |
| | | case FORCED:
|
| | | case NO_CHANGE:
|
| | | case FAST_FORWARD:
|
| | | return true; |
| | | return true;
|
| | | default:
|
| | | LOGGER.error(MessageFormat.format("{0} failed to delete to {1} returned result {2}",
|
| | | repository.getDirectory().getAbsolutePath(), branchName, result));
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Get the full branch and tag ref names for any potential HEAD targets.
|
| | | *
|
| | |
| | |
|
| | | /**
|
| | | * Returns all refs grouped by their associated object id.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return all refs grouped by their referenced object id
|
| | | */
|
| | | public static Map<ObjectId, List<RefModel>> getAllRefs(Repository repository) {
|
| | | return getAllRefs(repository, true);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns all refs grouped by their associated object id.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param includeRemoteRefs
|
| | | * @return all refs grouped by their referenced object id
|
| | |
| | | /**
|
| | | * Returns the list of tags in the repository. If repository does not exist
|
| | | * or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param fullName
|
| | | * if true, /refs/tags/yadayadayada is returned. If false,
|
| | |
| | | /**
|
| | | * Returns the list of local branches in the repository. If repository does
|
| | | * not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param fullName
|
| | | * if true, /refs/heads/yadayadayada is returned. If false,
|
| | |
| | | /**
|
| | | * Returns the list of remote branches in the repository. If repository does
|
| | | * not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param fullName
|
| | | * if true, /refs/remotes/yadayadayada is returned. If false,
|
| | |
| | | /**
|
| | | * Returns the list of note branches. If repository does not exist or is
|
| | | * empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param fullName
|
| | | * if true, /refs/notes/yadayadayada is returned. If false,
|
| | |
| | | int maxCount) {
|
| | | return getRefs(repository, Constants.R_NOTES, fullName, maxCount);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of refs in the specified base ref. If repository does |
| | | * Returns the list of refs in the specified base ref. If repository does
|
| | | * not exist or is empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param fullName
|
| | | * if true, /refs/yadayadayada is returned. If false,
|
| | |
| | | /**
|
| | | * Returns a list of references in the repository matching "refs". If the
|
| | | * repository is null or empty, an empty list is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param refs
|
| | | * if unspecified, all refs are returned
|
| | |
| | | /**
|
| | | * Returns a RefModel for the gh-pages branch in the repository. If the
|
| | | * branch can not be found, null is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return a refmodel for the gh-pages branch or null
|
| | | */
|
| | |
| | | /**
|
| | | * Returns a RefModel for a specific branch name in the repository. If the
|
| | | * branch can not be found, null is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return a refmodel for the branch or null
|
| | | */
|
| | |
| | | }
|
| | | return branch;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of submodules for this repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param commit
|
| | | * @return list of submodules
|
| | |
| | | RevCommit commit = getCommit(repository, commitId);
|
| | | return getSubmodules(repository, commit.getTree());
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the list of submodules for this repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param commit
|
| | | * @return list of submodules
|
| | |
| | | }
|
| | | return list;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the submodule definition for the specified path at the specified
|
| | | * commit. If no module is defined for the path, null is returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param commit
|
| | | * @param path
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | public static String getSubmoduleCommitId(Repository repository, String path, RevCommit commit) {
|
| | | String commitId = null;
|
| | | RevWalk rw = new RevWalk(repository);
|
| | |
| | | * Returns the list of notes entered about the commit from the refs/notes
|
| | | * namespace. If the repository does not exist or is empty, an empty list is
|
| | | * returned.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param commit
|
| | | * @return list of notes
|
| | |
| | | list.add(gitNote);
|
| | | continue;
|
| | | }
|
| | | |
| | |
|
| | | // folder structure
|
| | | StringBuilder sb = new StringBuilder(commit.getName());
|
| | | sb.insert(2, '/');
|
| | |
| | | /**
|
| | | * this method creates an incremental revision number as a tag according to
|
| | | * the amount of already existing tags, which start with a defined prefix.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * @param tagger
|
| | |
| | |
|
| | | /**
|
| | | * creates a tag in a repository
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId, the ref the tag points towards
|
| | | * @param tagger, the person tagging the object
|
| | |
| | | * @return boolean, true if operation was successful, otherwise false
|
| | | */
|
| | | public static boolean createTag(Repository repository, String objectId, PersonIdent tagger, String tag, String message) {
|
| | | try { |
| | | try {
|
| | | Git gitClient = Git.open(repository.getDirectory());
|
| | | TagCommand tagCommand = gitClient.tag();
|
| | | tagCommand.setTagger(tagger);
|
| | |
| | | tagCommand.setObjectId(revObj);
|
| | | }
|
| | | tagCommand.setName(tag);
|
| | | Ref call = tagCommand.call(); |
| | | Ref call = tagCommand.call();
|
| | | return call != null ? true : false;
|
| | | } catch (Exception e) {
|
| | | error(e, repository, "Failed to create tag {1} in repository {0}", objectId, tag);
|
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Create an orphaned branch in a repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param branchName
|
| | | * @param author
|
| | |
| | | }
|
| | | return success;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Reads the sparkleshare id, if present, from the repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @return an id or null
|
| | | */
|
| | |
| | | */ |
| | | package com.gitblit.utils; |
| | | |
| | | import com.sun.jna.Library; |
| | | import com.sun.jna.Native; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import com.sun.jna.Library; |
| | | import com.sun.jna.Native; |
| | | |
| | | /** |
| | | * Collection of static methods to access native OS library functionality. |
| | | * |
| | |
| | |
|
| | | /**
|
| | | * Utility methods for json calls to a Gitblit server.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class JsonUtils {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Creates JSON from the specified object.
|
| | | * |
| | | *
|
| | | * @param o
|
| | | * @return json
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Convert a json string to an object of the specified type.
|
| | | * |
| | | *
|
| | | * @param json
|
| | | * @param clazz
|
| | | * @return an object
|
| | |
| | |
|
| | | /**
|
| | | * Convert a json string to an object of the specified type.
|
| | | * |
| | | *
|
| | | * @param json
|
| | | * @param clazz
|
| | | * @return an object
|
| | |
| | |
|
| | | /**
|
| | | * Reads a gson object from the specified url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @param type
|
| | | * @return the deserialized object
|
| | |
| | |
|
| | | /**
|
| | | * Reads a gson object from the specified url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @param type
|
| | | * @return the deserialized object
|
| | |
| | |
|
| | | /**
|
| | | * Reads a gson object from the specified url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @param type
|
| | | * @param username
|
| | |
| | |
|
| | | /**
|
| | | * Reads a gson object from the specified url.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @param clazz
|
| | | * @param username
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves a JSON message.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * @return the JSON message as a string
|
| | | * @throws {@link IOException}
|
| | |
| | |
|
| | | /**
|
| | | * Sends a JSON message.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * the url to write to
|
| | | * @param json
|
| | |
| | |
|
| | | /**
|
| | | * Sends a JSON message.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * the url to write to
|
| | | * @param json
|
| | |
| | | JsonDeserializationContext jsonDeserializationContext) {
|
| | | try {
|
| | | synchronized (dateFormat) {
|
| | | Date date = dateFormat.parse(jsonElement.getAsString()); |
| | | Date date = dateFormat.parse(jsonElement.getAsString());
|
| | | return new Date((date.getTime() / 1000) * 1000);
|
| | | }
|
| | | } catch (ParseException e) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private static class AccessPermissionTypeAdapter implements JsonSerializer<AccessPermission>, JsonDeserializer<AccessPermission> {
|
| | |
|
| | | private AccessPermissionTypeAdapter() {
|
| | |
| | | @Override
|
| | | public synchronized AccessPermission deserialize(JsonElement jsonElement, Type type,
|
| | | JsonDeserializationContext jsonDeserializationContext) {
|
| | | return AccessPermission.fromCode(jsonElement.getAsString()); |
| | | return AccessPermission.fromCode(jsonElement.getAsString());
|
| | | }
|
| | | }
|
| | |
|
| | |
| | | this.fieldName = fqfn.substring(fqfn.lastIndexOf(".") + 1);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public boolean shouldSkipClass(Class<?> arg0) {
|
| | | return false;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public boolean shouldSkipField(FieldAttributes f) {
|
| | | return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Utility methods for transforming raw markdown text to html.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class MarkdownUtils {
|
| | |
|
| | | /**
|
| | | * Returns the html version of the markdown source text.
|
| | | * |
| | | *
|
| | | * @param markdown
|
| | | * @return html version of markdown text
|
| | | * @throws java.text.ParseException
|
| | |
| | | /**
|
| | | * Returns the html version of the markdown source reader. The reader is
|
| | | * closed regardless of success or failure.
|
| | | * |
| | | *
|
| | | * @param markdownReader
|
| | | * @return html version of the markdown text
|
| | | * @throws java.text.ParseException
|
| | |
| | | /**
|
| | | * Utility class for collecting metrics on a branch, tag, or other ref within
|
| | | * the repository.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class MetricUtils {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Log an error message and exception.
|
| | | * |
| | | *
|
| | | * @param t
|
| | | * @param repository
|
| | | * if repository is not null it MUST be the {0} parameter in the
|
| | |
| | | * Returns the list of metrics for the specified commit reference, branch,
|
| | | * or tag within the repository. If includeTotal is true, the total of all
|
| | | * the metrics will be included as the first element in the returned list.
|
| | | * |
| | | *
|
| | | * If the dateformat is unspecified an attempt is made to determine an
|
| | | * appropriate date format by determining the time difference between the
|
| | | * first commit on the branch and the most recent commit. This assumes that
|
| | | * the commits are linear.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * if null or empty, HEAD is assumed.
|
| | |
| | |
|
| | | /**
|
| | | * Returns a list of author metrics for the specified repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param objectId
|
| | | * if null or empty, HEAD is assumed.
|
| | |
| | | userRepoPrefix = Constants.DEFAULT_USER_REPOSITORY_PREFIX; |
| | | return; |
| | | } |
| | | |
| | | |
| | | String newPrefix = prefix.replace('\\', '/'); |
| | | if (prefix.charAt(0) == '/') { |
| | | newPrefix = prefix.substring(1); |
| | |
| | | * milliseconds and in fast, concurrent systems this cache is too simplistic.
|
| | | * However, for the cases where its being used in Gitblit this cache technique
|
| | | * is just fine.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class ObjectCache<X> implements Serializable {
|
| | |
|
| | |
| | | }
|
| | | return null;
|
| | | }
|
| | | |
| | |
|
| | | public int size() {
|
| | | return cache.size();
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * A diff formatter that outputs standard patch content.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class PatchFormatter extends DiffFormatter {
|
| | |
|
| | |
| | | this.os = os;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void format(DiffEntry entry) throws IOException {
|
| | | currentTouple = new PatchTouple();
|
| | | changes.put(entry.getNewPath(), currentTouple);
|
| | |
| | | /** |
| | | * Utility class for maintaining a reflog within a git repository on an |
| | | * orphan branch. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class RefLogUtils { |
| | | |
| | | |
| | | private static final String GB_REFLOG = "refs/gitblit/reflog"; |
| | | |
| | | private static final Logger LOGGER = LoggerFactory.getLogger(RefLogUtils.class); |
| | | |
| | | /** |
| | | * Log an error message and exception. |
| | | * |
| | | * |
| | | * @param t |
| | | * @param repository |
| | | * if repository is not null it MUST be the {0} parameter in the |
| | |
| | | } |
| | | LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns true if the repository has a reflog branch. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @return true if the repository has a reflog branch |
| | | */ |
| | |
| | | /** |
| | | * Returns a RefModel for the reflog branch in the repository. If the |
| | | * branch can not be found, null is returned. |
| | | * |
| | | * |
| | | * @param repository |
| | | * @return a refmodel for the reflog branch or null |
| | | */ |
| | |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | |
| | | private static UserModel newUserModelFrom(PersonIdent ident) { |
| | | String name = ident.getName(); |
| | | String username; |
| | |
| | | displayname = name; |
| | | username = ident.getEmailAddress(); |
| | | } |
| | | |
| | | |
| | | UserModel user = new UserModel(username); |
| | | user.displayName = displayname; |
| | | user.emailAddress = ident.getEmailAddress(); |
| | | return user; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Logs a ref deletion. |
| | | * |
| | | * |
| | | * @param user |
| | | * @param repository |
| | | * @param ref |
| | |
| | | if (reflogBranch == null) { |
| | | return false; |
| | | } |
| | | |
| | | |
| | | List<RevCommit> log = JGitUtils.getRevLog(repository, reflogBranch.getName(), ref.getName(), 0, 1); |
| | | if (log.isEmpty()) { |
| | | // this ref is not in the reflog branch |
| | |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Updates the reflog with the received commands. |
| | | * |
| | | * |
| | | * @param user |
| | | * @param repository |
| | | * @param commands |
| | |
| | | if (reflogBranch == null) { |
| | | JGitUtils.createOrphanBranch(repository, GB_REFLOG, null); |
| | | } |
| | | |
| | | |
| | | boolean success = false; |
| | | String message = "push"; |
| | | |
| | | |
| | | try { |
| | | ObjectId headId = repository.resolve(GB_REFLOG + "^{commit}"); |
| | | ObjectInserter odi = repository.newObjectInserter(); |
| | |
| | | } |
| | | return success; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Creates an in-memory index of the push log entry. |
| | | * |
| | | * |
| | | * @param repo |
| | | * @param headId |
| | | * @param commands |
| | | * @return an in-memory index |
| | | * @throws IOException |
| | | */ |
| | | private static DirCache createIndex(Repository repo, ObjectId headId, |
| | | private static DirCache createIndex(Repository repo, ObjectId headId, |
| | | Collection<ReceiveCommand> commands) throws IOException { |
| | | |
| | | DirCache inCoreIndex = DirCache.newInCore(); |
| | |
| | | continue; |
| | | } |
| | | String content = change.toString(); |
| | | |
| | | |
| | | // create an index entry for this attachment |
| | | final DirCacheEntry dcEntry = new DirCacheEntry(path); |
| | | dcEntry.setLength(content.length()); |
| | |
| | | } |
| | | return inCoreIndex; |
| | | } |
| | | |
| | | |
| | | public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository) { |
| | | return getRefLog(repositoryName, repository, null, 0, -1); |
| | | } |
| | |
| | | public static List<RefLogEntry> getRefLog(String repositoryName, Repository repository, Date minimumDate) { |
| | | return getRefLog(repositoryName, repository, minimumDate, 0, -1); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of reflog entries as they were recorded by Gitblit. |
| | | * Each RefLogEntry may represent multiple ref updates. |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param minimumDate |
| | |
| | | if (maxCount == 0) { |
| | | return list; |
| | | } |
| | | |
| | | |
| | | Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository); |
| | | List<RevCommit> pushes; |
| | | if (minimumDate == null) { |
| | |
| | | |
| | | UserModel user = newUserModelFrom(push.getAuthorIdent()); |
| | | Date date = push.getAuthorIdent().getWhen(); |
| | | |
| | | RefLogEntry log = new RefLogEntry(repositoryName, date, user); |
| | | |
| | | RefLogEntry log = new RefLogEntry(repositoryName, date, user); |
| | | List<PathChangeModel> changedRefs = JGitUtils.getFilesInCommit(repository, push); |
| | | if (changedRefs.isEmpty()) { |
| | | // skip empty commits |
| | |
| | | /** |
| | | * Returns the list of pushes separated by ref (e.g. each ref has it's own |
| | | * PushLogEntry object). |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param maxCount |
| | |
| | | public static List<RefLogEntry> getLogByRef(String repositoryName, Repository repository, int maxCount) { |
| | | return getLogByRef(repositoryName, repository, 0, maxCount); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of pushes separated by ref (e.g. each ref has it's own |
| | | * PushLogEntry object). |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param offset |
| | |
| | | if (!refMap.containsKey(ref)) { |
| | | refMap.put(ref, new ArrayList<RefLogEntry>()); |
| | | } |
| | | |
| | | |
| | | // construct new ref-specific ref change entry |
| | | RefLogEntry refChange; |
| | | if (entry instanceof DailyLogEntry) { |
| | |
| | | refMap.get(ref).add(refChange); |
| | | } |
| | | } |
| | | |
| | | |
| | | // merge individual ref changes into master list |
| | | List<RefLogEntry> mergedRefLog = new ArrayList<RefLogEntry>(); |
| | | for (List<RefLogEntry> refPush : refMap.values()) { |
| | | mergedRefLog.addAll(refPush); |
| | | } |
| | | |
| | | |
| | | // sort ref log |
| | | Collections.sort(mergedRefLog); |
| | | |
| | | |
| | | return mergedRefLog; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of ref changes separated by ref (e.g. each ref has it's own |
| | | * RefLogEntry object). |
| | | * |
| | | * |
| | | * @param repositoryName |
| | | * @param repository |
| | | * @param minimumDate |
| | |
| | | refMap.get(ref).add(refPush); |
| | | } |
| | | } |
| | | |
| | | |
| | | // merge individual ref pushes into master list |
| | | List<RefLogEntry> refPushLog = new ArrayList<RefLogEntry>(); |
| | | for (List<RefLogEntry> refPush : refMap.values()) { |
| | | refPushLog.addAll(refPush); |
| | | } |
| | | |
| | | |
| | | // sort ref push log |
| | | Collections.sort(refPushLog); |
| | | |
| | | |
| | | return refPushLog; |
| | | } |
| | | |
| | |
| | | linearParent = commit.getParents()[0].getId().getName(); |
| | | digest.updateRef(branch, ReceiveCommand.Type.UPDATE, linearParent, commit.getName()); |
| | | } |
| | | |
| | | |
| | | RepositoryCommit repoCommit = digest.addCommit(commit); |
| | | if (repoCommit != null) { |
| | | List<RefModel> matchedRefs = allRefs.get(commit.getId()); |
| | | repoCommit.setRefs(matchedRefs); |
| | | |
| | | |
| | | if (!ArrayUtils.isEmpty(matchedRefs)) { |
| | | for (RefModel ref : matchedRefs) { |
| | | if (ref.getName().startsWith(Constants.R_TAGS)) { |
| | |
| | |
|
| | | /**
|
| | | * Utility methods for rpc calls.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class RpcUtils {
|
| | |
|
| | |
| | | }.getType();
|
| | |
|
| | | /**
|
| | | * |
| | | *
|
| | | * @param remoteURL
|
| | | * the url of the remote gitblit instance
|
| | | * @param req
|
| | |
| | | }
|
| | |
|
| | | /**
|
| | | * |
| | | *
|
| | | * @param remoteURL
|
| | | * the url of the remote gitblit instance
|
| | | * @param req
|
| | |
| | |
|
| | | /**
|
| | | * Returns the version of the RPC protocol on the server.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | | try {
|
| | | protocol = JsonUtils.retrieveJson(url, Integer.class, account, password);
|
| | | } catch (UnknownRequestException e) {
|
| | | // v0.7.0 (protocol 1) did not have this request type |
| | | // v0.7.0 (protocol 1) did not have this request type
|
| | | }
|
| | | return protocol;
|
| | | }
|
| | |
| | | /**
|
| | | * Retrieves a map of the repositories at the remote gitblit instance keyed
|
| | | * by the repository clone url.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Tries to pull the gitblit user accounts from the remote gitblit instance.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | | /**
|
| | | * Tries to pull the gitblit team definitions from the remote gitblit
|
| | | * instance.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Create a repository on the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Send a revised version of the repository model to the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Delete a repository from the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | | password);
|
| | |
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Clears the repository cache on the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | | * @return true if the action succeeded
|
| | | * @throws IOException
|
| | | */
|
| | | public static boolean clearRepositoryCache(String serverUrl, String account, |
| | | public static boolean clearRepositoryCache(String serverUrl, String account,
|
| | | char[] password) throws IOException {
|
| | | return doAction(RpcRequest.CLEAR_REPOSITORY_CACHE, null, null, serverUrl, account,
|
| | | password);
|
| | |
| | |
|
| | | /**
|
| | | * Create a user on the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Send a revised version of the user model to the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Deletes a user from the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param user
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | | char[] password) throws IOException {
|
| | | return doAction(RpcRequest.DELETE_USER, null, user, serverUrl, account, password);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Tries to get the specified gitblit user account from the remote gitblit instance.
|
| | | * If the username is null or empty, the current user is returned.
|
| | | * |
| | | *
|
| | | * @param username
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Create a team on the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param team
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Send a revised version of the team model to the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param team
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Deletes a team from the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param team
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves the list of users that can access the specified repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | | Collection<String> list = JsonUtils.retrieveJson(url, NAMES_TYPE, account, password);
|
| | | return new ArrayList<String>(list);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieves the list of user access permissions for the specified repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | | * @return list of User-AccessPermission tuples
|
| | | * @throws IOException
|
| | | */
|
| | | public static List<RegistrantAccessPermission> getRepositoryMemberPermissions(RepositoryModel repository, |
| | | public static List<RegistrantAccessPermission> getRepositoryMemberPermissions(RepositoryModel repository,
|
| | | String serverUrl, String account, char [] password) throws IOException {
|
| | | String url = asLink(serverUrl, RpcRequest.LIST_REPOSITORY_MEMBER_PERMISSIONS, repository.name);
|
| | | Collection<RegistrantAccessPermission> list = JsonUtils.retrieveJson(url, REGISTRANT_PERMISSIONS_TYPE, account, password);
|
| | |
| | |
|
| | | /**
|
| | | * Sets the repository user access permissions
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param permissions
|
| | | * @param serverUrl
|
| | |
| | | return doAction(RpcRequest.SET_REPOSITORY_MEMBER_PERMISSIONS, repository.name, permissions, serverUrl,
|
| | | account, password);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieves the list of teams that can access the specified repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | | Collection<String> list = JsonUtils.retrieveJson(url, NAMES_TYPE, account, password);
|
| | | return new ArrayList<String>(list);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieves the list of team access permissions for the specified repository.
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param serverUrl
|
| | | * @param account
|
| | |
| | | * @return list of Team-AccessPermission tuples
|
| | | * @throws IOException
|
| | | */
|
| | | public static List<RegistrantAccessPermission> getRepositoryTeamPermissions(RepositoryModel repository, |
| | | public static List<RegistrantAccessPermission> getRepositoryTeamPermissions(RepositoryModel repository,
|
| | | String serverUrl, String account, char [] password) throws IOException {
|
| | | String url = asLink(serverUrl, RpcRequest.LIST_REPOSITORY_TEAM_PERMISSIONS, repository.name);
|
| | | Collection<RegistrantAccessPermission> list = JsonUtils.retrieveJson(url, REGISTRANT_PERMISSIONS_TYPE, account, password);
|
| | |
| | |
|
| | | /**
|
| | | * Sets the repository team access permissions
|
| | | * |
| | | *
|
| | | * @param repository
|
| | | * @param permissions
|
| | | * @param serverUrl
|
| | |
| | | return doAction(RpcRequest.SET_REPOSITORY_TEAM_PERMISSIONS, repository.name, permissions, serverUrl,
|
| | | account, password);
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieves the list of federation registrations. These are the list of
|
| | | * registrations that this Gitblit instance is pulling from.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | | /**
|
| | | * Retrieves the list of federation result registrations. These are the
|
| | | * results reported back to this Gitblit instance from a federation client.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves the list of federation proposals.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves the list of federation repository sets.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves the settings of the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Update the settings on the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param settings
|
| | | * the settings to update
|
| | | * @param serverUrl
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves the server status object.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | | /**
|
| | | * Retrieves a map of local branches in the Gitblit server keyed by
|
| | | * repository.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Retrieves a list of available branch feeds in the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param serverUrl
|
| | | * @param account
|
| | | * @param password
|
| | |
| | |
|
| | | /**
|
| | | * Do the specified administrative action on the Gitblit server.
|
| | | * |
| | | *
|
| | | * @param request
|
| | | * @param name
|
| | | * the name of the object (may be null)
|
| | |
| | |
|
| | | /**
|
| | | * Utility class of string functions.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class StringUtils {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Returns true if the string is null or empty.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @return true if string is null or empty
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Replaces carriage returns and line feeds with html line breaks.
|
| | | * |
| | | *
|
| | | * @param string
|
| | | * @return plain text with html line breaks
|
| | | */
|
| | |
| | | /**
|
| | | * Prepare text for html presentation. Replace sensitive characters with
|
| | | * html entities.
|
| | | * |
| | | *
|
| | | * @param inStr
|
| | | * @param changeSpace
|
| | | * @return plain text escaped for html
|
| | |
| | |
|
| | | /**
|
| | | * Decode html entities back into plain text characters.
|
| | | * |
| | | *
|
| | | * @param inStr
|
| | | * @return returns plain text from html
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Encodes a url parameter by escaping troublesome characters.
|
| | | * |
| | | *
|
| | | * @param inStr
|
| | | * @return properly escaped url
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Flatten the list of strings into a single string with a space separator.
|
| | | * |
| | | *
|
| | | * @param values
|
| | | * @return flattened list
|
| | | */
|
| | |
| | | /**
|
| | | * Flatten the list of strings into a single string with the specified
|
| | | * separator.
|
| | | * |
| | | *
|
| | | * @param values
|
| | | * @param separator
|
| | | * @return flattened list
|
| | |
| | | * Returns a string trimmed to a maximum length with trailing ellipses. If
|
| | | * the string length is shorter than the max, the original string is
|
| | | * returned.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @param max
|
| | | * @return trimmed string
|
| | |
| | | /**
|
| | | * Left pad a string with the specified character, if the string length is
|
| | | * less than the specified length.
|
| | | * |
| | | *
|
| | | * @param input
|
| | | * @param length
|
| | | * @param pad
|
| | |
| | | /**
|
| | | * Right pad a string with the specified character, if the string length is
|
| | | * less then the specified length.
|
| | | * |
| | | *
|
| | | * @param input
|
| | | * @param length
|
| | | * @param pad
|
| | |
| | |
|
| | | /**
|
| | | * Calculates the SHA1 of the string.
|
| | | * |
| | | *
|
| | | * @param text
|
| | | * @return sha1 of the string
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Calculates the SHA1 of the byte array.
|
| | | * |
| | | *
|
| | | * @param bytes
|
| | | * @return sha1 of the byte array
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Calculates the MD5 of the string.
|
| | | * |
| | | *
|
| | | * @param string
|
| | | * @return md5 of the string
|
| | | */
|
| | |
| | | throw new RuntimeException(u);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Calculates the MD5 of the string.
|
| | | * |
| | | *
|
| | | * @param string
|
| | | * @return md5 of the string
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the hex representation of the byte array.
|
| | | * |
| | | *
|
| | | * @param bytes
|
| | | * @return byte array as hex string
|
| | | */
|
| | | private static String toHex(byte[] bytes) {
|
| | | StringBuilder sb = new StringBuilder(bytes.length * 2);
|
| | | for (int i = 0; i < bytes.length; i++) {
|
| | | if (((int) bytes[i] & 0xff) < 0x10) {
|
| | | if ((bytes[i] & 0xff) < 0x10) {
|
| | | sb.append('0');
|
| | | }
|
| | | sb.append(Long.toString((int) bytes[i] & 0xff, 16));
|
| | | sb.append(Long.toString(bytes[i] & 0xff, 16));
|
| | | }
|
| | | return sb.toString();
|
| | | }
|
| | |
| | | /**
|
| | | * Returns the root path of the specified path. Returns a blank string if
|
| | | * there is no root path.
|
| | | * |
| | | *
|
| | | * @param path
|
| | | * @return root path or blank
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the path remainder after subtracting the basePath from the
|
| | | * fullPath.
|
| | | * |
| | | *
|
| | | * @param basePath
|
| | | * @param fullPath
|
| | | * @return the relative path
|
| | |
| | |
|
| | | /**
|
| | | * Splits the space-separated string into a list of strings.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @return list of strings
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Splits the string into a list of string by the specified separator.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @param separator
|
| | | * @return list of strings
|
| | |
| | | public static List<String> getStringsFromValue(String value, String separator) {
|
| | | List<String> strings = new ArrayList<String>();
|
| | | try {
|
| | | String[] chunks = value.split(separator + "(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); |
| | | String[] chunks = value.split(separator + "(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
|
| | | for (String chunk : chunks) {
|
| | | chunk = chunk.trim();
|
| | | if (chunk.length() > 0) {
|
| | |
| | | /**
|
| | | * Validates that a name is composed of letters, digits, or limited other
|
| | | * characters.
|
| | | * |
| | | *
|
| | | * @param name
|
| | | * @return the first invalid character found or null if string is acceptable
|
| | | */
|
| | |
| | | /**
|
| | | * Simple fuzzy string comparison. This is a case-insensitive check. A
|
| | | * single wildcard * value is supported.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @param pattern
|
| | | * @return true if the value matches the pattern
|
| | |
| | |
|
| | | /**
|
| | | * Compare two repository names for proper group sorting.
|
| | | * |
| | | *
|
| | | * @param r1
|
| | | * @param r2
|
| | | * @return
|
| | |
| | |
|
| | | /**
|
| | | * Sort grouped repository names.
|
| | | * |
| | | *
|
| | | * @param list
|
| | | */
|
| | | public static void sortRepositorynames(List<String> list) {
|
| | |
| | | for (char c : getMD5(value.toLowerCase()).toCharArray()) {
|
| | | cs += c;
|
| | | }
|
| | | int n = (cs % 360); |
| | | int n = (cs % 360);
|
| | | float hue = ((float) n) / 360;
|
| | | return hsvToRgb(hue, 0.90f, 0.65f);
|
| | | }
|
| | |
| | | String bs = Integer.toHexString((int) (b * 256));
|
| | | return "#" + rs + gs + bs;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Strips a trailing ".git" from the value.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @return a stripped value or the original value if .git is not found
|
| | | */
|
| | |
| | | }
|
| | | return value;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Count the number of lines in a string.
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @return the line count
|
| | | */
|
| | |
| | | }
|
| | | return value.split("\n").length;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the file extension of a path.
|
| | | * |
| | | *
|
| | | * @param path
|
| | | * @return a blank string or a file extension
|
| | | */
|
| | |
| | | }
|
| | | return "";
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Replace all occurences of a substring within a string with
|
| | | * another string.
|
| | | * |
| | | *
|
| | | * From Spring StringUtils.
|
| | | * |
| | | *
|
| | | * @param inString String to examine
|
| | | * @param oldPattern String to replace
|
| | | * @param newPattern String to insert
|
| | |
| | | // remember to append any characters to the right of a match
|
| | | return sb.toString();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Decodes a string by trying several charsets until one does not throw a
|
| | | * coding exception. Last resort is to interpret as UTF-8 with illegal
|
| | | * character substitution.
|
| | | * |
| | | *
|
| | | * @param content
|
| | | * @param charsets optional
|
| | | * @return a string
|
| | |
| | | }
|
| | | return value;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Attempt to extract a repository name from a given url using regular
|
| | | * expressions. If no match is made, then return whatever trails after
|
| | | * the final / character.
|
| | | * |
| | | *
|
| | | * @param regexUrls
|
| | | * @return a repository path
|
| | | */
|
| | |
| | | }
|
| | | return url;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Converts a string with \nnn sequences into a UTF-8 encoded string.
|
| | | * @param input
|
| | |
| | | // strip leading \ character
|
| | | String oct = m.group().substring(1);
|
| | | bytes.write(Integer.parseInt(oct, 8));
|
| | | i = m.end(); |
| | | i = m.end();
|
| | | }
|
| | | if (bytes.size() == 0) {
|
| | | // no octal matches
|
| | |
| | | }
|
| | | return input;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the first path element of a path string. If no path separator is
|
| | | * found in the path, an empty string is returned. |
| | | * |
| | | * found in the path, an empty string is returned.
|
| | | *
|
| | | * @param path
|
| | | * @return the first element in the path
|
| | | */
|
| | |
| | | }
|
| | | return "";
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns the last path element of a path string
|
| | | * |
| | | *
|
| | | * @param path
|
| | | * @return the last element in the path
|
| | | */
|
| | |
| | | }
|
| | | return path;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Variation of String.matches() which disregards case issues.
|
| | | * |
| | | *
|
| | | * @param regex
|
| | | * @param input
|
| | | * @return true if the pattern matches
|
| | |
| | | Matcher m = p.matcher(input);
|
| | | return m.matches();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Removes new line and carriage return chars from a string.
|
| | | * If input value is null an empty string is returned.
|
| | | * |
| | | *
|
| | | * @param input
|
| | | * @return a sanitized or empty string
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Utility class for RSS feeds.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class SyndicationUtils {
|
| | |
|
| | | /**
|
| | | * Outputs an RSS feed of the list of entries to the outputstream.
|
| | | * |
| | | *
|
| | | * @param hostUrl
|
| | | * @param feedLink
|
| | | * @param title
|
| | |
| | |
|
| | | /**
|
| | | * Reads a Gitblit RSS feed.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * the url of the Gitblit server
|
| | | * @param repository
|
| | |
| | |
|
| | | /**
|
| | | * Reads a Gitblit RSS search feed.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * the url of the Gitblit server
|
| | | * @param repository
|
| | |
| | |
|
| | | /**
|
| | | * Reads a Gitblit RSS feed.
|
| | | * |
| | | *
|
| | | * @param url
|
| | | * the url of the Gitblit server
|
| | | * @param parameters
|
| | |
| | |
|
| | | /**
|
| | | * Utility class of time functions.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class TimeUtils {
|
| | | public static final long MIN = 1000 * 60L;
|
| | |
| | | public static final long ONEDAY = ONEHOUR * 24L;
|
| | |
|
| | | public static final long ONEYEAR = ONEDAY * 365L;
|
| | | |
| | |
|
| | | private final ResourceBundle translation;
|
| | | |
| | |
|
| | | private final TimeZone timezone;
|
| | | |
| | |
|
| | | public TimeUtils() {
|
| | | this(null, null);
|
| | | }
|
| | | |
| | |
|
| | | public TimeUtils(ResourceBundle translation, TimeZone timezone) {
|
| | | this.translation = translation;
|
| | | this.timezone = timezone;
|
| | |
| | |
|
| | | /**
|
| | | * Returns true if date is today.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @return true if date is today
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns true if date is yesterday.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @return true if date is yesterday
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the string representation of the duration as days, months and/or
|
| | | * years.
|
| | | * |
| | | *
|
| | | * @param days
|
| | | * @return duration as string in days, months, and/or years
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the number of minutes ago between the start time and the end
|
| | | * time.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @param endTime
|
| | | * @param roundup
|
| | |
| | |
|
| | | /**
|
| | | * Return the difference in minutes between now and the date.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @param roundup
|
| | | * @return minutes ago
|
| | |
| | |
|
| | | /**
|
| | | * Return the difference in hours between now and the date.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @param roundup
|
| | | * @return hours ago
|
| | |
| | |
|
| | | /**
|
| | | * Return the difference in days between now and the date.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @return days ago
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the string representation of the duration between now and the
|
| | | * date.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @return duration as a string
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Returns the CSS class for the date based on its age from Now.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @return the css class
|
| | | */
|
| | |
| | | /**
|
| | | * Returns the string representation of the duration OR the css class for
|
| | | * the duration.
|
| | | * |
| | | *
|
| | | * @param date
|
| | | * @param css
|
| | | * @return the string representation of the duration OR the css class
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public String inFuture(Date date) {
|
| | | long diff = date.getTime() - System.currentTimeMillis();
|
| | | if (diff > ONEDAY) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | private String translate(String key, String defaultValue) {
|
| | | String value = defaultValue;
|
| | | if (translation != null && translation.containsKey(key)) {
|
| | |
| | | }
|
| | | return value;
|
| | | }
|
| | | |
| | |
|
| | | private String translate(int val, String key, String defaultPattern) {
|
| | | String pattern = defaultPattern;
|
| | | if (translation != null && translation.containsKey(key)) {
|
| | |
| | |
|
| | | /**
|
| | | * Convert a frequency string into minutes.
|
| | | * |
| | | *
|
| | | * @param frequency
|
| | | * @return minutes
|
| | | */
|
| | |
| | |
|
| | | /**
|
| | | * Utility class to generate X509 certificates, keystores, and truststores.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class X509Utils {
|
| | | |
| | |
|
| | | public static final String SERVER_KEY_STORE = "serverKeyStore.jks";
|
| | | |
| | |
|
| | | public static final String SERVER_TRUST_STORE = "serverTrustStore.jks";
|
| | |
|
| | | public static final String CERTS = "certs";
|
| | | |
| | |
|
| | | public static final String CA_KEY_STORE = "certs/caKeyStore.p12";
|
| | |
|
| | | public static final String CA_REVOCATION_LIST = "certs/caRevocationList.crl";
|
| | | |
| | |
|
| | | public static final String CA_CONFIG = "certs/authority.conf";
|
| | |
|
| | | public static final String CA_CN = "Gitblit Certificate Authority";
|
| | | |
| | |
|
| | | public static final String CA_ALIAS = CA_CN;
|
| | |
|
| | | private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
|
| | | |
| | |
|
| | | private static final int KEY_LENGTH = 2048;
|
| | | |
| | |
|
| | | private static final String KEY_ALGORITHM = "RSA";
|
| | | |
| | |
|
| | | private static final String SIGNING_ALGORITHM = "SHA512withRSA";
|
| | | |
| | |
|
| | | public static final boolean unlimitedStrength;
|
| | | |
| | |
|
| | | private static final Logger logger = LoggerFactory.getLogger(X509Utils.class);
|
| | | |
| | |
|
| | | static {
|
| | | Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
| | | |
| | |
|
| | | // check for JCE Unlimited Strength
|
| | | int maxKeyLen = 0;
|
| | | try {
|
| | |
| | | logger.info("Using JCE Standard Encryption Policy files, encryption key lengths will be limited");
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static enum RevocationReason {
|
| | | // https://en.wikipedia.org/wiki/Revocation_list
|
| | | unspecified, keyCompromise, caCompromise, affiliationChanged, superseded,
|
| | | cessationOfOperation, certificateHold, unused, removeFromCRL, privilegeWithdrawn,
|
| | | ACompromise;
|
| | | |
| | |
|
| | | public static RevocationReason [] reasons = {
|
| | | unspecified, keyCompromise, caCompromise, |
| | | affiliationChanged, superseded, cessationOfOperation, |
| | | unspecified, keyCompromise, caCompromise,
|
| | | affiliationChanged, superseded, cessationOfOperation,
|
| | | privilegeWithdrawn };
|
| | | |
| | |
|
| | | @Override
|
| | | public String toString() {
|
| | | return name() + " (" + ordinal() + ")";
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public interface X509Log {
|
| | | void log(String message);
|
| | | }
|
| | | |
| | |
|
| | | public static class X509Metadata {
|
| | |
|
| | | // map for distinguished name OIDs
|
| | |
| | |
|
| | | // CN in distingiushed name
|
| | | public final String commonName;
|
| | | |
| | |
|
| | | // password for store
|
| | | public final String password;
|
| | | |
| | |
|
| | | // password hint for README in bundle
|
| | | public String passwordHint;
|
| | |
|
| | |
| | |
|
| | | // start date of generated certificate
|
| | | public Date notBefore;
|
| | | |
| | |
|
| | | // expiraiton date of generated certificate
|
| | | public Date notAfter;
|
| | | |
| | |
|
| | | // hostname of server for which certificate is generated
|
| | | public String serverHostname;
|
| | | |
| | |
|
| | | // displayname of user for README in bundle
|
| | | public String userDisplayname;
|
| | |
|
| | |
| | | notAfter = c.getTime();
|
| | | oids = new HashMap<String, String>();
|
| | | }
|
| | | |
| | |
|
| | | public X509Metadata clone(String commonName, String password) {
|
| | | X509Metadata clone = new X509Metadata(commonName, password);
|
| | | clone.emailAddress = emailAddress;
|
| | |
| | | clone.userDisplayname = userDisplayname;
|
| | | return clone;
|
| | | }
|
| | | |
| | |
|
| | | public String getOID(String oid, String defaultValue) {
|
| | | if (oids.containsKey(oid)) {
|
| | | return oids.get(oid);
|
| | | }
|
| | | return defaultValue;
|
| | | }
|
| | | |
| | |
|
| | | public void setOID(String oid, String value) {
|
| | | if (StringUtils.isEmpty(value)) {
|
| | | oids.remove(oid);
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Prepare all the certificates and stores necessary for a Gitblit GO server.
|
| | | * |
| | | *
|
| | | * @param metadata
|
| | | * @param folder
|
| | | * @param x509log
|
| | |
| | | public static void prepareX509Infrastructure(X509Metadata metadata, File folder, X509Log x509log) {
|
| | | // make the specified folder, if necessary
|
| | | folder.mkdirs();
|
| | | |
| | |
|
| | | // Gitblit CA certificate
|
| | | File caKeyStore = new File(folder, CA_KEY_STORE); |
| | | File caKeyStore = new File(folder, CA_KEY_STORE);
|
| | | if (!caKeyStore.exists()) {
|
| | | logger.info(MessageFormat.format("Generating {0} ({1})", CA_CN, caKeyStore.getAbsolutePath()));
|
| | | X509Certificate caCert = newCertificateAuthority(metadata, caKeyStore, x509log);
|
| | |
| | | }
|
| | |
|
| | | // Gitblit CRL
|
| | | File caRevocationList = new File(folder, CA_REVOCATION_LIST); |
| | | File caRevocationList = new File(folder, CA_REVOCATION_LIST);
|
| | | if (!caRevocationList.exists()) {
|
| | | logger.info(MessageFormat.format("Generating {0} CRL ({1})", CA_CN, caRevocationList.getAbsolutePath()));
|
| | | newCertificateRevocationList(caRevocationList, caKeyStore, metadata.password);
|
| | |
| | | File oldKeyStore = new File(folder, "keystore");
|
| | | if (oldKeyStore.exists()) {
|
| | | oldKeyStore.renameTo(new File(folder, SERVER_KEY_STORE));
|
| | | logger.info(MessageFormat.format("Renaming {0} to {1}", oldKeyStore.getName(), SERVER_KEY_STORE)); |
| | | logger.info(MessageFormat.format("Renaming {0} to {1}", oldKeyStore.getName(), SERVER_KEY_STORE));
|
| | | }
|
| | |
|
| | | // create web SSL certificate signed by CA
|
| | |
| | | logger.info(MessageFormat.format("Generating SSL certificate for {0} signed by {1} ({2})", metadata.commonName, CA_CN, serverKeyStore.getAbsolutePath()));
|
| | | PrivateKey caPrivateKey = getPrivateKey(CA_ALIAS, caKeyStore, metadata.password);
|
| | | X509Certificate caCert = getCertificate(CA_ALIAS, caKeyStore, metadata.password);
|
| | | newSSLCertificate(metadata, caPrivateKey, caCert, serverKeyStore, x509log); |
| | | newSSLCertificate(metadata, caPrivateKey, caCert, serverKeyStore, x509log);
|
| | | }
|
| | |
|
| | | // server certificate trust store holds trusted public certificates
|
| | |
| | | addTrustedCertificate(CA_ALIAS, caCert, serverTrustStore, metadata.password);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Open a keystore. Store type is determined by file extension of name. If
|
| | | * undetermined, JKS is assumed. The keystore does not need to exist.
|
| | | * |
| | | *
|
| | | * @param storeFile
|
| | | * @param storePassword
|
| | | * @return a KeyStore
|
| | |
| | | throw new RuntimeException("Could not open keystore " + storeFile, e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Saves the keystore to the specified file.
|
| | | * |
| | | *
|
| | | * @param targetStoreFile
|
| | | * @param store
|
| | | * @param password
|
| | |
| | | } catch (IOException e) {
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | if (tmpFile.exists()) {
|
| | | tmpFile.delete();
|
| | | }
|
| | | }
|
| | | } |
| | | }
|
| | |
|
| | | /**
|
| | | * Retrieves the X509 certificate with the specified alias from the certificate
|
| | | * store.
|
| | | * |
| | | *
|
| | | * @param alias
|
| | | * @param storeFile
|
| | | * @param storePassword
|
| | |
| | | throw new RuntimeException(e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Retrieves the private key for the specified alias from the certificate
|
| | | * store.
|
| | | * |
| | | *
|
| | | * @param alias
|
| | | * @param storeFile
|
| | | * @param storePassword
|
| | |
| | | * Saves the certificate to the file system. If the destination filename
|
| | | * ends with the pem extension, the certificate is written in the PEM format,
|
| | | * otherwise the certificate is written in the DER format.
|
| | | * |
| | | *
|
| | | * @param cert
|
| | | * @param targetFile
|
| | | */
|
| | |
| | | try {
|
| | | pemWriter = new PEMWriter(new FileWriter(tmpFile));
|
| | | pemWriter.writeObject(cert);
|
| | | pemWriter.flush(); |
| | | pemWriter.flush();
|
| | | } finally {
|
| | | if (pemWriter != null) {
|
| | | pemWriter.close();
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // rename tmp file to target
|
| | | if (targetFile.exists()) { |
| | | if (targetFile.exists()) {
|
| | | targetFile.delete();
|
| | | }
|
| | | tmpFile.renameTo(targetFile);
|
| | |
| | | throw new RuntimeException("Failed to save certificate " + cert.getSubjectX500Principal().getName(), e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Generate a new keypair.
|
| | | * |
| | | *
|
| | | * @return a keypair
|
| | | * @throws Exception
|
| | | */
|
| | |
| | | kpGen.initialize(KEY_LENGTH, new SecureRandom());
|
| | | return kpGen.generateKeyPair();
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Builds a distinguished name from the X509Metadata.
|
| | | * |
| | | *
|
| | | * @return a DN
|
| | | */
|
| | | private static X500Name buildDistinguishedName(X509Metadata metadata) {
|
| | |
| | | setOID(dnBuilder, metadata, "O", Constants.NAME);
|
| | | setOID(dnBuilder, metadata, "OU", Constants.NAME);
|
| | | setOID(dnBuilder, metadata, "E", metadata.emailAddress);
|
| | | setOID(dnBuilder, metadata, "CN", metadata.commonName); |
| | | setOID(dnBuilder, metadata, "CN", metadata.commonName);
|
| | | X500Name dn = dnBuilder.build();
|
| | | return dn;
|
| | | }
|
| | | |
| | |
|
| | | private static void setOID(X500NameBuilder dnBuilder, X509Metadata metadata,
|
| | | String oid, String defaultValue) {
|
| | | |
| | |
|
| | | String value = null;
|
| | | if (metadata.oids != null && metadata.oids.containsKey(oid)) {
|
| | | value = metadata.oids.get(oid);
|
| | |
| | | if (StringUtils.isEmpty(value)) {
|
| | | value = defaultValue;
|
| | | }
|
| | | |
| | |
|
| | | if (!StringUtils.isEmpty(value)) {
|
| | | try {
|
| | | Field field = BCStyle.class.getField(oid);
|
| | |
| | | /**
|
| | | * Creates a new SSL certificate signed by the CA private key and stored in
|
| | | * keyStore.
|
| | | * |
| | | *
|
| | | * @param sslMetadata
|
| | | * @param caPrivateKey
|
| | | * @param caCert
|
| | |
| | |
|
| | | X500Name webDN = buildDistinguishedName(sslMetadata);
|
| | | X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(caCert).getName());
|
| | | |
| | |
|
| | | X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
| | | issuerDN,
|
| | | BigInteger.valueOf(System.currentTimeMillis()), |
| | | BigInteger.valueOf(System.currentTimeMillis()),
|
| | | sslMetadata.notBefore,
|
| | | sslMetadata.notAfter,
|
| | | webDN,
|
| | | pair.getPublic());
|
| | | |
| | |
|
| | | JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
| | | certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
|
| | | certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
|
| | |
| | | // support alternateSubjectNames for SSL certificates
|
| | | List<GeneralName> altNames = new ArrayList<GeneralName>();
|
| | | if (HttpUtils.isIpAddress(sslMetadata.commonName)) {
|
| | | altNames.add(new GeneralName(GeneralName.iPAddress, sslMetadata.commonName)); |
| | | altNames.add(new GeneralName(GeneralName.iPAddress, sslMetadata.commonName));
|
| | | }
|
| | | if (altNames.size() > 0) {
|
| | | GeneralNames subjectAltName = new GeneralNames(altNames.toArray(new GeneralName [altNames.size()]));
|
| | |
| | | .setProvider(BC).build(caPrivateKey);
|
| | | X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
|
| | | .getCertificate(certBuilder.build(caSigner));
|
| | | |
| | |
|
| | | cert.checkValidity(new Date());
|
| | | cert.verify(caCert.getPublicKey());
|
| | |
|
| | |
| | | serverStore.setKeyEntry(sslMetadata.commonName, pair.getPrivate(), sslMetadata.password.toCharArray(),
|
| | | new Certificate[] { cert, caCert });
|
| | | saveKeyStore(targetStoreFile, serverStore, sslMetadata.password);
|
| | | |
| | |
|
| | | x509log.log(MessageFormat.format("New SSL certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName()));
|
| | | |
| | |
|
| | | // update serial number in metadata object
|
| | | sslMetadata.serialNumber = cert.getSerialNumber().toString();
|
| | |
|
| | |
| | | /**
|
| | | * Creates a new certificate authority PKCS#12 store. This function will
|
| | | * destroy any existing CA store.
|
| | | * |
| | | *
|
| | | * @param metadata
|
| | | * @param storeFile
|
| | | * @param keystorePassword
|
| | |
| | | public static X509Certificate newCertificateAuthority(X509Metadata metadata, File storeFile, X509Log x509log) {
|
| | | try {
|
| | | KeyPair caPair = newKeyPair();
|
| | | |
| | |
|
| | | ContentSigner caSigner = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPair.getPrivate());
|
| | | |
| | |
|
| | | // clone metadata
|
| | | X509Metadata caMetadata = metadata.clone(CA_CN, metadata.password);
|
| | | X500Name issuerDN = buildDistinguishedName(caMetadata);
|
| | | |
| | |
|
| | | // Generate self-signed certificate
|
| | | X509v3CertificateBuilder caBuilder = new JcaX509v3CertificateBuilder(
|
| | | issuerDN,
|
| | |
| | | caMetadata.notAfter,
|
| | | issuerDN,
|
| | | caPair.getPublic());
|
| | | |
| | |
|
| | | JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
| | | caBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(caPair.getPublic()));
|
| | | caBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caPair.getPublic()));
|
| | | caBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(true));
|
| | | caBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign));
|
| | | |
| | |
|
| | | JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC);
|
| | | X509Certificate cert = converter.getCertificate(caBuilder.build(caSigner));
|
| | | |
| | |
|
| | | // confirm the validity of the CA certificate
|
| | | cert.checkValidity(new Date());
|
| | | cert.verify(cert.getPublicKey());
|
| | |
| | | if (storeFile.exists()) {
|
| | | storeFile.delete();
|
| | | }
|
| | | |
| | |
|
| | | // Save private key and certificate to new keystore
|
| | | KeyStore store = openKeyStore(storeFile, caMetadata.password);
|
| | | store.setKeyEntry(CA_ALIAS, caPair.getPrivate(), caMetadata.password.toCharArray(),
|
| | | new Certificate[] { cert });
|
| | | saveKeyStore(storeFile, store, caMetadata.password);
|
| | | |
| | |
|
| | | x509log.log(MessageFormat.format("New CA certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getIssuerDN().getName()));
|
| | |
|
| | | // update serial number in metadata object
|
| | |
| | | throw new RuntimeException("Failed to generate Gitblit CA certificate!", t);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Creates a new certificate revocation list (CRL). This function will
|
| | | * destroy any existing CRL file.
|
| | | * |
| | | *
|
| | | * @param caRevocationList
|
| | | * @param storeFile
|
| | | * @param keystorePassword
|
| | |
| | |
|
| | | X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(caCert).getName());
|
| | | X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerDN, new Date());
|
| | | |
| | |
|
| | | // build and sign CRL with CA private key
|
| | | ContentSigner signer = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPrivateKey);
|
| | | X509CRLHolder crl = crlBuilder.build(signer);
|
| | |
| | | throw new RuntimeException("Failed to create new certificate revocation list " + caRevocationList, e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Imports a certificate into the trust store.
|
| | | * |
| | | *
|
| | | * @param alias
|
| | | * @param cert
|
| | | * @param storeFile
|
| | |
| | | try {
|
| | | KeyStore store = openKeyStore(storeFile, storePassword);
|
| | | store.setCertificateEntry(alias, cert);
|
| | | saveKeyStore(storeFile, store, storePassword); |
| | | saveKeyStore(storeFile, store, storePassword);
|
| | | } catch (Exception e) {
|
| | | throw new RuntimeException("Failed to import certificate into trust store " + storeFile, e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Creates a new client certificate PKCS#12 and PEM store. Any existing
|
| | | * stores are destroyed. After generation, the certificates are bundled
|
| | | * into a zip file with a personalized README file.
|
| | | * |
| | | * The zip file reference is returned. |
| | | * |
| | | *
|
| | | * The zip file reference is returned.
|
| | | *
|
| | | * @param clientMetadata a container for dynamic parameters needed for generation
|
| | | * @param caKeystoreFile
|
| | | * @param caKeystorePassword
|
| | | * @param x509log
|
| | | * @return a zip file containing the P12, PEM, and personalized README
|
| | | */
|
| | | public static File newClientBundle(X509Metadata clientMetadata, File caKeystoreFile, |
| | | public static File newClientBundle(X509Metadata clientMetadata, File caKeystoreFile,
|
| | | String caKeystorePassword, X509Log x509log) {
|
| | | try {
|
| | | // read the Gitblit CA key and certificate
|
| | | KeyStore store = openKeyStore(caKeystoreFile, caKeystorePassword);
|
| | | PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_ALIAS, caKeystorePassword.toCharArray());
|
| | | X509Certificate caCert = (X509Certificate) store.getCertificate(CA_ALIAS);
|
| | | |
| | |
|
| | | // generate the P12 and PEM files
|
| | | File targetFolder = new File(caKeystoreFile.getParentFile(), clientMetadata.commonName);
|
| | | X509Certificate cert = newClientCertificate(clientMetadata, caPrivateKey, caCert, targetFolder);
|
| | |
| | |
|
| | | // process template message
|
| | | String readme = processTemplate(new File(caKeystoreFile.getParentFile(), "instructions.tmpl"), clientMetadata);
|
| | | |
| | |
|
| | | // Create a zip bundle with the p12, pem, and a personalized readme
|
| | | File zipFile = new File(targetFolder, clientMetadata.commonName + ".zip");
|
| | | if (zipFile.exists()) {
|
| | |
| | | zos.putNextEntry(new ZipEntry(p12File.getName()));
|
| | | zos.write(FileUtils.readContent(p12File));
|
| | | zos.closeEntry();
|
| | | } |
| | | }
|
| | | File pemFile = new File(targetFolder, clientMetadata.commonName + ".pem");
|
| | | if (pemFile.exists()) {
|
| | | zos.putNextEntry(new ZipEntry(pemFile.getName()));
|
| | | zos.write(FileUtils.readContent(pemFile));
|
| | | zos.closeEntry();
|
| | | }
|
| | | |
| | |
|
| | | // include user's public certificate
|
| | | zos.putNextEntry(new ZipEntry(clientMetadata.commonName + ".cer"));
|
| | | zos.write(cert.getEncoded());
|
| | | zos.closeEntry();
|
| | | |
| | |
|
| | | // include CA public certificate
|
| | | zos.putNextEntry(new ZipEntry("ca.cer"));
|
| | | zos.write(caCert.getEncoded());
|
| | | zos.closeEntry();
|
| | | |
| | |
|
| | | if (readme != null) {
|
| | | zos.putNextEntry(new ZipEntry("README.TXT"));
|
| | | zos.write(readme.getBytes("UTF-8"));
|
| | |
| | | zos.close();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | return zipFile;
|
| | | } catch (Throwable t) {
|
| | | throw new RuntimeException("Failed to generate client bundle!", t);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Creates a new client certificate PKCS#12 and PEM store. Any existing
|
| | | * stores are destroyed.
|
| | | * |
| | | *
|
| | | * @param clientMetadata a container for dynamic parameters needed for generation
|
| | | * @param caKeystoreFile
|
| | | * @param caKeystorePassword
|
| | |
| | | PrivateKey caPrivateKey, X509Certificate caCert, File targetFolder) {
|
| | | try {
|
| | | KeyPair pair = newKeyPair();
|
| | | |
| | | X500Name userDN = buildDistinguishedName(clientMetadata); |
| | |
|
| | | X500Name userDN = buildDistinguishedName(clientMetadata);
|
| | | X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(caCert).getName());
|
| | | |
| | |
|
| | | // create a new certificate signed by the Gitblit CA certificate
|
| | | X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
|
| | | issuerDN,
|
| | |
| | | clientMetadata.notAfter,
|
| | | userDN,
|
| | | pair.getPublic());
|
| | | |
| | |
|
| | | JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
| | | certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
|
| | | certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
|
| | |
| | | PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)pair.getPrivate();
|
| | | bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
|
| | | extUtils.createSubjectKeyIdentifier(pair.getPublic()));
|
| | | |
| | |
|
| | | // confirm the validity of the user certificate
|
| | | userCert.checkValidity();
|
| | | userCert.verify(caCert.getPublicKey());
|
| | |
| | | verifyChain(userCert, caCert);
|
| | |
|
| | | targetFolder.mkdirs();
|
| | | |
| | |
|
| | | // save certificate, stamped with unique name
|
| | | String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
| | | String id = date;
|
| | |
| | | certFile = new File(targetFolder, id + ".cer");
|
| | | count++;
|
| | | }
|
| | | |
| | |
|
| | | // save user private key, user certificate and CA certificate to a PKCS#12 store
|
| | | File p12File = new File(targetFolder, clientMetadata.commonName + ".p12");
|
| | | if (p12File.exists()) {
|
| | |
| | | }
|
| | | KeyStore userStore = openKeyStore(p12File, clientMetadata.password);
|
| | | userStore.setKeyEntry(MessageFormat.format("Gitblit ({0}) {1} {2}", clientMetadata.serverHostname, clientMetadata.userDisplayname, id), pair.getPrivate(), null, new Certificate [] { userCert });
|
| | | userStore.setCertificateEntry(MessageFormat.format("Gitblit ({0}) Certificate Authority", clientMetadata.serverHostname), caCert); |
| | | userStore.setCertificateEntry(MessageFormat.format("Gitblit ({0}) Certificate Authority", clientMetadata.serverHostname), caCert);
|
| | | saveKeyStore(p12File, userStore, clientMetadata.password);
|
| | | |
| | |
|
| | | // save user private key, user certificate, and CA certificate to a PEM store
|
| | | File pemFile = new File(targetFolder, clientMetadata.commonName + ".pem");
|
| | | if (pemFile.exists()) {
|
| | |
| | | pemWriter.writeObject(caCert);
|
| | | pemWriter.flush();
|
| | | pemWriter.close();
|
| | | |
| | |
|
| | | // save certificate after successfully creating the key stores
|
| | | saveCertificate(userCert, certFile);
|
| | | |
| | |
|
| | | // update serial number in metadata object
|
| | | clientMetadata.serialNumber = userCert.getSerialNumber().toString();
|
| | | |
| | |
|
| | | return userCert;
|
| | | } catch (Throwable t) {
|
| | | throw new RuntimeException("Failed to generate client certificate!", t);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Verifies a certificate's chain to ensure that it will function properly.
|
| | | * |
| | | *
|
| | | * @param testCert
|
| | | * @param additionalCerts
|
| | | * @return
|
| | |
| | | if (isSelfSigned(testCert)) {
|
| | | throw new RuntimeException("The certificate is self-signed. Nothing to verify.");
|
| | | }
|
| | | |
| | |
|
| | | // Prepare a set of all certificates
|
| | | // chain builder must have all certs, including cert to validate
|
| | | // http://stackoverflow.com/a/10788392
|
| | | Set<X509Certificate> certs = new HashSet<X509Certificate>();
|
| | | certs.add(testCert);
|
| | | certs.addAll(Arrays.asList(additionalCerts));
|
| | | |
| | |
|
| | | // Attempt to build the certification chain and verify it
|
| | | // Create the selector that specifies the starting certificate
|
| | | X509CertSelector selector = new X509CertSelector(); |
| | | X509CertSelector selector = new X509CertSelector();
|
| | | selector.setCertificate(testCert);
|
| | | |
| | |
|
| | | // Create the trust anchors (set of root CA certificates)
|
| | | Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
|
| | | for (X509Certificate cert : additionalCerts) {
|
| | |
| | | trustAnchors.add(new TrustAnchor(cert, null));
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | // Configure the PKIX certificate builder
|
| | | PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);
|
| | | pkixParams.setRevocationEnabled(false);
|
| | | pkixParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certs), BC));
|
| | | |
| | |
|
| | | // Build and verify the certification chain
|
| | | CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", BC);
|
| | | PKIXCertPathBuilderResult verifiedCertChain = (PKIXCertPathBuilderResult) builder.build(pkixParams);
|
| | | |
| | |
|
| | | // The chain is built and verified
|
| | | return verifiedCertChain;
|
| | | } catch (CertPathBuilderException e) {
|
| | |
| | | throw new RuntimeException("Error verifying the certificate: " + testCert.getSubjectX500Principal(), e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Checks whether given X.509 certificate is self-signed.
|
| | | * |
| | | *
|
| | | * @param cert
|
| | | * @return true if the certificate is self-signed
|
| | | */
|
| | |
| | | throw new RuntimeException(e);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static String processTemplate(File template, X509Metadata metadata) {
|
| | | String content = null;
|
| | | if (template.exists()) {
|
| | |
| | | }
|
| | | return content;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Revoke a certificate.
|
| | | * |
| | | *
|
| | | * @param cert
|
| | | * @param reason
|
| | | * @param caRevocationList
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Revoke a certificate.
|
| | | * |
| | | *
|
| | | * @param cert
|
| | | * @param reason
|
| | | * @param caRevocationList
|
| | |
| | | X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(cert).getName());
|
| | | X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerDN, new Date());
|
| | | if (caRevocationList.exists()) {
|
| | | byte [] data = FileUtils.readContent(caRevocationList); |
| | | byte [] data = FileUtils.readContent(caRevocationList);
|
| | | X509CRLHolder crl = new X509CRLHolder(data);
|
| | | crlBuilder.addCRL(crl);
|
| | | }
|
| | | crlBuilder.addCRLEntry(cert.getSerialNumber(), new Date(), reason.ordinal());
|
| | | |
| | |
|
| | | // build and sign CRL with CA private key
|
| | | ContentSigner signer = new JcaContentSignerBuilder("SHA1WithRSA").setProvider(BC).build(caPrivateKey);
|
| | | X509CRLHolder crl = crlBuilder.build(signer);
|
| | | |
| | |
|
| | | File tmpFile = new File(caRevocationList.getParentFile(), Long.toHexString(System.currentTimeMillis()) + ".tmp");
|
| | | FileOutputStream fos = null;
|
| | | try {
|
| | |
| | | caRevocationList.delete();
|
| | | }
|
| | | tmpFile.renameTo(caRevocationList);
|
| | | |
| | |
|
| | | } finally {
|
| | | if (fos != null) {
|
| | | fos.close();
|
| | |
| | | tmpFile.delete();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | x509log.log(MessageFormat.format("Revoked certificate {0,number,0} reason: {1} [{2}]",
|
| | | cert.getSerialNumber(), reason.toString(), cert.getSubjectDN().getName()));
|
| | | return true;
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Returns true if the certificate has been revoked.
|
| | | * |
| | | *
|
| | | * @param cert
|
| | | * @param caRevocationList
|
| | | * @return true if the certificate is revoked
|
| | |
| | | }
|
| | | return false;
|
| | | }
|
| | | |
| | |
|
| | | public static X509Metadata getMetadata(X509Certificate cert) {
|
| | | // manually split DN into OID components
|
| | | // this is instead of parsing with LdapName which:
|
| | |
| | | String data = val[1].trim();
|
| | | oids.put(oid, data);
|
| | | }
|
| | | |
| | |
|
| | | X509Metadata metadata = new X509Metadata(oids.get("CN"), "whocares");
|
| | | metadata.oids.putAll(oids);
|
| | | metadata.serialNumber = cert.getSerialNumber().toString();
|
| | |
| | |
|
| | | @Override
|
| | | public void onUnauthorizedInstantiation(Component component) {
|
| | | |
| | |
|
| | | if (component instanceof BasePage) {
|
| | | throw new RestartResponseException(GitBlitWebApp.HOME_PAGE_CLASS);
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Page attribute to control what date as last-modified for the browser cache.
|
| | | * |
| | | *
|
| | | * http://betterexplained.com/articles/how-to-optimize-your-site-with-http-caching
|
| | | * https://developers.google.com/speed/docs/best-practices/caching
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | @Documented
|
| | | @Retention(RetentionPolicy.RUNTIME)
|
| | | public @interface CacheControl {
|
| | | |
| | |
|
| | | public static enum LastModified {
|
| | | BOOT, ACTIVITY, PROJECT, REPOSITORY, COMMIT, NONE
|
| | | }
|
| | | |
| | |
|
| | | LastModified value() default LastModified.NONE;
|
| | | } |
| | |
| | | super(id, new Model<String>(url));
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void onComponentTag(ComponentTag tag) {
|
| | | super.onComponentTag(tag);
|
| | | checkComponentTag(tag, "img");
|
| | |
| | | private UserModel user;
|
| | |
|
| | | private String errorMessage;
|
| | | |
| | |
|
| | | private String requestUrl;
|
| | | |
| | |
|
| | | private AtomicBoolean isForking;
|
| | | |
| | |
|
| | | public AuthenticationType authenticationType;
|
| | | |
| | |
|
| | | public GitBlitWebSession(Request request) {
|
| | | super(request);
|
| | | isForking = new AtomicBoolean();
|
| | | authenticationType = AuthenticationType.CREDENTIALS;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void invalidate() {
|
| | | super.invalidate();
|
| | | user = null;
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Cache the requested protected resource pending successful authentication.
|
| | | * |
| | | *
|
| | | * @param pageClass
|
| | | */
|
| | | public void cacheRequest(Class<? extends Page> pageClass) {
|
| | |
| | | bind();
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Continue any cached request. This is used when a request for a protected
|
| | | * resource is aborted/redirected pending proper authentication. Gitblit
|
| | | * no longer uses Wicket's built-in mechanism for this because of Wicket's
|
| | | * failure to properly handle parameters with forward-slashes. This is a
|
| | | * constant source of headaches with Wicket.
|
| | | * |
| | | *
|
| | | * @return false if there is no cached request to process
|
| | | */
|
| | | public boolean continueRequest() {
|
| | |
| | | }
|
| | | return user.canAdmin();
|
| | | }
|
| | | |
| | |
|
| | | public String getUsername() {
|
| | | return user == null ? "anonymous" : user.username;
|
| | | }
|
| | |
| | | errorMessage = null;
|
| | | return msg;
|
| | | }
|
| | | |
| | |
|
| | | public boolean isForking() {
|
| | | return isForking.get();
|
| | | }
|
| | | |
| | |
|
| | | public void isForking(boolean val) {
|
| | | isForking.set(val);
|
| | | }
|
| | |
| | | /**
|
| | | * Simple subclass of mixed parameter url coding strategy that works around the
|
| | | * encoded forward-slash issue that is present in some servlet containers.
|
| | | * |
| | | *
|
| | | * https://issues.apache.org/jira/browse/WICKET-1303
|
| | | * http://tomcat.apache.org/security-6.html
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GitblitParamUrlCodingStrategy extends MixedParamUrlCodingStrategy {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Construct.
|
| | | * |
| | | *
|
| | | * @param <C>
|
| | | * @param mountPath
|
| | | * mount path (not empty)
|
| | |
| | |
|
| | | /**
|
| | | * Url encodes a string that is mean for a URL path (e.g., between slashes)
|
| | | * |
| | | *
|
| | | * @param string
|
| | | * string to be encoded
|
| | | * @return encoded string
|
| | | */
|
| | | @Override
|
| | | protected String urlEncodePathComponent(String string) {
|
| | | char altChar = GitBlit.getChar(Keys.web.forwardSlashCharacter, '/');
|
| | | if (altChar != '/') {
|
| | |
| | | /**
|
| | | * Returns a decoded value of the given value (taken from a URL path
|
| | | * section)
|
| | | * |
| | | *
|
| | | * @param value
|
| | | * @return Decodes the value
|
| | | */
|
| | | @Override
|
| | | protected String urlDecodePathComponent(String value) {
|
| | | char altChar = GitBlit.getChar(Keys.web.forwardSlashCharacter, '/');
|
| | | if (altChar != '/') {
|
| | |
| | |
|
| | | /**
|
| | | * Gets the decoded request target.
|
| | | * |
| | | *
|
| | | * @param requestParameters
|
| | | * the request parameters
|
| | | * @return the decoded request target
|
| | |
| | | * This exception bypasses the servlet container rewriting relative redirect |
| | | * urls. The container can and does decode the carefully crafted %2F path |
| | | * separators on a redirect. :( Bad, bad servlet container. |
| | | * |
| | | * |
| | | * org.eclipse.jetty.server.Response#L447: String path=uri.getDecodedPath(); |
| | | * |
| | | * |
| | | * @author James Moger |
| | | */ |
| | | public class GitblitRedirectException extends AbstractRestartResponseException { |
| | |
| | | import com.gitblit.utils.StringUtils;
|
| | |
|
| | | /**
|
| | | * |
| | | *
|
| | | * Customization of the WicketFilter to allow smart browser-side caching of
|
| | | * some pages.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | *
|
| | | */
|
| | | public class GitblitWicketFilter extends WicketFilter {
|
| | | |
| | |
|
| | | /**
|
| | | * Determines the last-modified date of the requested resource.
|
| | | * |
| | | *
|
| | | * @param servletRequest
|
| | | * @return The last modified time stamp
|
| | | */
|
| | | @Override
|
| | | protected long getLastModified(final HttpServletRequest servletRequest) {
|
| | | final String pathInfo = getRelativePath(servletRequest);
|
| | | if (Strings.isEmpty(pathInfo))
|
| | |
| | | if (lastModified > -1) {
|
| | | return lastModified;
|
| | | }
|
| | | |
| | |
|
| | | // try to match request against registered CacheControl pages
|
| | | String [] paths = pathInfo.split("/");
|
| | | |
| | |
|
| | | String page = paths[0];
|
| | | String repo = "";
|
| | | String commitId = "";
|
| | |
| | | if (paths.length >= 3) {
|
| | | commitId = paths[2];
|
| | | }
|
| | | |
| | |
|
| | | if (!StringUtils.isEmpty(servletRequest.getParameter("r"))) {
|
| | | repo = servletRequest.getParameter("r");
|
| | | }
|
| | | if (!StringUtils.isEmpty(servletRequest.getParameter("h"))) {
|
| | | commitId = servletRequest.getParameter("h");
|
| | | }
|
| | | |
| | |
|
| | | repo = repo.replace("%2f", "/").replace("%2F", "/").replace(GitBlit.getChar(Keys.web.forwardSlashCharacter, '/'), '/');
|
| | |
|
| | | GitBlitWebApp app = (GitBlitWebApp) getWebApplication();
|
| | |
| | | // no commit id, use boot date
|
| | | return bootDate.getTime();
|
| | | } else {
|
| | | // last modified date is the commit date |
| | | // last modified date is the commit date
|
| | | Repository r = null;
|
| | | try {
|
| | | // return the timestamp of the associated commit
|
| | |
| | | default:
|
| | | break;
|
| | | }
|
| | | } |
| | | }
|
| | |
|
| | | return -1;
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Represents a page link registration for the topbar.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class PageRegistration implements Serializable {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
| | | PageParameters params) {
|
| | | this(translationKey, pageClass, params, false);
|
| | | }
|
| | | |
| | |
|
| | | public PageRegistration(String translationKey, Class<? extends WebPage> pageClass,
|
| | | PageParameters params, boolean hiddenPhone) {
|
| | | this.translationKey = translationKey;
|
| | |
| | |
|
| | | /**
|
| | | * Represents a page link to a non-Wicket page. Might be external.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public static class OtherPageLink extends PageRegistration {
|
| | |
|
| | |
| | | super(translationKey, null);
|
| | | this.url = url;
|
| | | }
|
| | | |
| | |
|
| | | public OtherPageLink(String translationKey, String url, boolean hiddenPhone) {
|
| | | super(translationKey, null, null, hiddenPhone);
|
| | | this.url = url;
|
| | |
| | |
|
| | | /**
|
| | | * Represents a DropDownMenu for the topbar
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public static class DropDownMenuRegistration extends PageRegistration {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * A MenuItem for the DropDownMenu.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public static class DropDownMenuItem implements Serializable {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Standard Menu Item constructor.
|
| | | * |
| | | *
|
| | | * @param displayText
|
| | | * @param parameter
|
| | | * @param value
|
| | |
| | |
|
| | | /**
|
| | | * Standard Menu Item constructor that preserves aggregate parameters.
|
| | | * |
| | | *
|
| | | * @param displayText
|
| | | * @param parameter
|
| | | * @param value
|
| | |
| | | return displayText;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | public static class DropDownToggleItem extends DropDownMenuItem {
|
| | | |
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | /**
|
| | | * Toggle Menu Item constructor that preserves aggregate parameters.
|
| | | * |
| | | *
|
| | | * @param displayText
|
| | | * @param parameter
|
| | | * @param value
|
| | |
| | | * This class is used to create a stateless form that can POST or GET to a |
| | | * bookmarkable page regardless of the pagemap and even after session expiration |
| | | * or a server restart. |
| | | * |
| | | * |
| | | * The trick is to embed "wicket:bookmarkablePage" as a hidden field of the form. |
| | | * Wicket already has logic to extract this parameter when it is trying |
| | | * to determine which page should receive the request. |
| | | * |
| | | * |
| | | * The parameters of the containing page can optionally be included as hidden |
| | | * fields in this form. Note that if a page parameter's name collides with any |
| | | * fields in this form. Note that if a page parameter's name collides with any |
| | | * child's wicket:id in this form then the page parameter is excluded. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class SessionlessForm<T> extends StatelessForm<T> { |
| | | |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private static final String HIDDEN_DIV_START = "<div style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\">"; |
| | | |
| | | |
| | | private final Class<? extends BasePage> pageClass; |
| | | |
| | | |
| | | private final PageParameters pageParameters; |
| | | |
| | | |
| | | private final Logger log = LoggerFactory.getLogger(SessionlessForm.class); |
| | | |
| | | /** |
| | | * Sessionless forms must have a bookmarkable page class. A bookmarkable |
| | | * page is defined as a page that has only a default and/or a PageParameter |
| | | * constructor. |
| | | * |
| | | * |
| | | * @param id |
| | | * @param bookmarkablePageClass |
| | | */ |
| | |
| | | * Sessionless forms must have a bookmarkable page class. A bookmarkable |
| | | * page is defined as a page that has only a default and/or a PageParameter |
| | | * constructor. |
| | | * |
| | | * |
| | | * @param id |
| | | * @param bookmarkablePageClass |
| | | * @param pageParameters |
| | |
| | | this.pageParameters = pageParameters; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Append an additional hidden input tag that forces Wicket to correctly |
| | | * determine the destination page class even after a session expiration or |
| | | * a server restart. |
| | | * |
| | | * |
| | | * @param markupStream |
| | | * The markup stream |
| | | * @param openTag |
| | |
| | | getResponse().write(buffer); |
| | | super.onComponentTagBody(markupStream, openTag); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Take URL-encoded query string value, unencode it and return HTML-escaped version |
| | | * |
| | | * |
| | | * @param s |
| | | * value to reencode |
| | | * @return reencoded value |
| | |
| | | /** |
| | | * Choice renderer for a palette or list of string values. This renderer |
| | | * controls the id value of each option such that palettes are case insensitive. |
| | | * |
| | | * |
| | | * @author James Moger |
| | | * |
| | | */ |
| | | public class StringChoiceRenderer extends ChoiceRenderer<String> { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | |
| | | public StringChoiceRenderer() { |
| | | super("", ""); |
| | | } |
| | |
| | | default:
|
| | | setCssClass(container, "badge");
|
| | | break;
|
| | | } |
| | | }
|
| | | }
|
| | |
|
| | | public static void setAlternatingBackground(Component c, int i) {
|
| | |
| | |
|
| | | public static Label newIcon(String wicketId, String css) {
|
| | | Label lbl = new Label(wicketId);
|
| | | setCssClass(lbl, css); |
| | | setCssClass(lbl, css);
|
| | | return lbl;
|
| | | }
|
| | | |
| | |
|
| | | public static Label newBlankIcon(String wicketId) {
|
| | | Label lbl = new Label(wicketId);
|
| | | setCssClass(lbl, "");
|
| | | lbl.setRenderBodyOnly(true);
|
| | | return lbl;
|
| | | }
|
| | | |
| | |
|
| | | public static ContextRelativeResource getResource(String file) {
|
| | | return new ContextRelativeResource(file);
|
| | | }
|
| | |
| | | return new HeaderContributor(new IHeaderContributor() {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void renderHead(IHeaderResponse response) {
|
| | | String contentType = "application/rss+xml";
|
| | |
|
| | |
| | | public static Label createDateLabel(String wicketId, Date date, TimeZone timeZone, TimeUtils timeUtils) {
|
| | | return createDateLabel(wicketId, date, timeZone, timeUtils, true);
|
| | | }
|
| | | |
| | |
|
| | | public static Label createDateLabel(String wicketId, Date date, TimeZone timeZone, TimeUtils timeUtils, boolean setCss) {
|
| | | String format = GitBlit.getString(Keys.web.datestampShortFormat, "MM/dd/yy");
|
| | | DateFormat df = new SimpleDateFormat(format);
|
| | |
| | | IChartData data = new AbstractChartData(max) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public double[][] getData() {
|
| | | return new double[][] { commits, tags };
|
| | | }
|
| | |
| | | IChartData data = new AbstractChartData(max) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public double[][] getData() {
|
| | | return new double[][] { x, y };
|
| | | }
|
| | |
| | |
|
| | | /**
|
| | | * Abstract parent class for Google Charts built with the Visualization API.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public abstract class GoogleChart implements Serializable {
|
| | |
|
| | |
| | | public void setHeight(int height) {
|
| | | this.height = height;
|
| | | }
|
| | | |
| | |
|
| | | public void setShowLegend(boolean val) {
|
| | | this.showLegend = val;
|
| | | }
|
| | |
| | | See the License for the specific language governing permissions and
|
| | | limitations under the License.
|
| | | */
|
| | |
|
| | | package com.gitblit.wicket.charting;
|
| | |
|
| | | import java.util.ArrayList;
|
| | |
| | | * The Google Visualization API provides interactive JavaScript based charts and
|
| | | * graphs. This class implements the JavaScript header necessary to display
|
| | | * complete graphs and charts.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GoogleCharts implements IHeaderContributor {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Builds an interactive line chart using the Visualization API.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GoogleLineChart extends GoogleChart {
|
| | |
|
| | |
| | |
|
| | | /**
|
| | | * Builds an interactive pie chart using the Visualization API.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class GooglePieChart extends GoogleChart {
|
| | |
|
| | |
| | |
|
| | | Collections.sort(values);
|
| | | List<ChartValue> list = new ArrayList<ChartValue>();
|
| | | |
| | |
|
| | | int maxSlices = 10;
|
| | | |
| | |
|
| | | if (values.size() > maxSlices) {
|
| | | list.addAll(values.subList(0, maxSlices));
|
| | | } else {
|
| | | list.addAll(values);
|
| | | }
|
| | | |
| | |
|
| | | StringBuilder colors = new StringBuilder("colors:[");
|
| | | for (int i = 0; i < list.size(); i++) {
|
| | | ChartValue value = list.get(i);
|
| | |
| | | /**
|
| | | * This is a fork of org.wicketstuff.googlecharts.Chart whose only purpose
|
| | | * is to build https urls instead of http urls.
|
| | | * |
| | | *
|
| | | * @author Daniel Spiewak
|
| | | * @author James Moger
|
| | | */
|
| | |
| | |
|
| | | url.append(param).append('=').append(value);
|
| | | }
|
| | | |
| | |
|
| | | private CharSequence convert(ChartDataEncoding encoding, double value, double max) {
|
| | | switch (encoding) {
|
| | | case TEXT:
|
| | |
| | | /**
|
| | | * This class is a pristine fork of org.wicketstuff.googlecharts.ChartDataEncoding
|
| | | * to bring the package-protected convert methods to SecureChart.
|
| | | * |
| | | *
|
| | | * @author Daniel Spiewak
|
| | | */
|
| | | public enum SecureChartDataEncoding {
|
| | |
|
| | | SIMPLE("s", "", ",") {
|
| | |
|
| | | CharSequence convert(double value, double max) {
|
| | | @Override
|
| | | CharSequence convert(double value, double max) {
|
| | | if (value < 0) {
|
| | | return "_";
|
| | | }
|
| | |
| | | },
|
| | | TEXT("t", ",", "|") {
|
| | |
|
| | | CharSequence convert(double value, double max) {
|
| | | @Override
|
| | | CharSequence convert(double value, double max) {
|
| | | if (value < 0) {
|
| | | value = -1;
|
| | | }
|
| | |
| | | },
|
| | | EXTENDED("e", "", ",") {
|
| | |
|
| | | CharSequence convert(double value, double max) {
|
| | | @Override
|
| | | CharSequence convert(double value, double max) {
|
| | | if (value < 0) {
|
| | | return "__";
|
| | | }
|
| | |
| | | public class Freemarker {
|
| | |
|
| | | private static final Configuration fm;
|
| | | |
| | |
|
| | | static {
|
| | | fm = new Configuration();
|
| | | fm.setObjectWrapper(new DefaultObjectWrapper());
|
| | | fm.setOutputEncoding("UTF-8");
|
| | | fm.setClassForTemplateLoading(Freemarker.class, "templates");
|
| | | }
|
| | | |
| | |
|
| | | public static Template getTemplate(String name) throws IOException {
|
| | | return fm.getTemplate(name);
|
| | | }
|
| | | |
| | |
|
| | | public static void evaluate(Template template, Map<String, Object> values, Writer out) throws TemplateException, IOException {
|
| | | template.process(values, out);
|
| | | }
|
| | |
| | | * snippet injector for something like a CMS. There are some cases where Wicket
|
| | | * is not flexible enough to generate content, especially when you need to generate
|
| | | * hybrid HTML/JS content outside the scope of Wicket.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | @SuppressWarnings("unchecked")
|
| | | public class FreemarkerPanel extends Panel
|
| | |
| | | private transient String stackTraceAsString;
|
| | | private transient String evaluatedTemplate;
|
| | |
|
| | | |
| | |
|
| | | /**
|
| | | * Construct.
|
| | | * |
| | | *
|
| | | * @param id
|
| | | * Component id
|
| | | * @param template
|
| | |
| | | {
|
| | | this(id, template, Model.ofMap(values));
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Construct.
|
| | | * |
| | | *
|
| | | * @param id
|
| | | * Component id
|
| | | * @param templateResource
|
| | |
| | |
|
| | | /**
|
| | | * Gets the Freemarker template.
|
| | | * |
| | | *
|
| | | * @return the Freemarker template
|
| | | */
|
| | | private Template getTemplate()
|
| | |
| | |
|
| | | /**
|
| | | * Either print or rethrow the throwable.
|
| | | * |
| | | *
|
| | | * @param exception
|
| | | * the cause
|
| | | * @param markupStream
|
| | |
| | |
|
| | | /**
|
| | | * Gets whether to escape HTML characters.
|
| | | * |
| | | *
|
| | | * @return whether to escape HTML characters. The default value is false.
|
| | | */
|
| | | public void setEscapeHtml(boolean value)
|
| | |
| | |
|
| | | /**
|
| | | * Evaluates the template and returns the result.
|
| | | * |
| | | *
|
| | | * @param templateReader
|
| | | * used to read the template
|
| | | * @return the result of evaluating the velocity template
|
| | |
| | |
|
| | | /**
|
| | | * Gets whether to parse the resulting Wicket markup.
|
| | | * |
| | | *
|
| | | * @return whether to parse the resulting Wicket markup. The default is false.
|
| | | */
|
| | | public void setParseGeneratedMarkup(boolean value)
|
| | |
| | | * want them to be able to have them correct them while the rest of the application keeps on
|
| | | * working.
|
| | | * </p>
|
| | | * |
| | | *
|
| | | * @return Whether any Freemarker exceptions should be thrown or trapped. The default is false.
|
| | | */
|
| | | public void setThrowFreemarkerExceptions(boolean value)
|
| | |
| | | * @see org.apache.wicket.markup.IMarkupResourceStreamProvider#getMarkupResourceStream(org.apache
|
| | | * .wicket.MarkupContainer, java.lang.Class)
|
| | | */
|
| | | @Override
|
| | | public final IResourceStream getMarkupResourceStream(MarkupContainer container,
|
| | | Class< ? > containerClass)
|
| | | {
|
| | |
| | | * @see org.apache.wicket.markup.IMarkupCacheKeyProvider#getCacheKey(org.apache.wicket.
|
| | | * MarkupContainer, java.lang.Class)
|
| | | */
|
| | | @Override
|
| | | public final String getCacheKey(MarkupContainer container, Class< ? > containerClass)
|
| | | {
|
| | | // don't cache the evaluated template
|
| | |
| | | See the License for the specific language governing permissions and
|
| | | limitations under the License.
|
| | | */
|
| | |
|
| | | package com.gitblit.wicket.ng;
|
| | |
|
| | | import java.text.MessageFormat;
|
| | |
| | | * Simple AngularJS data controller which injects scoped objects as static,
|
| | | * embedded JSON within the generated page. This allows use of AngularJS
|
| | | * client-side databinding (magic) with server-generated pages.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | | public class NgController implements IHeaderContributor {
|
| | |
|
| | | private static final long serialVersionUID = 1L;
|
| | | |
| | |
|
| | | final String name;
|
| | | |
| | |
|
| | | final Map<String, Object> variables;
|
| | | |
| | |
|
| | | public NgController(String name) {
|
| | | this.name = name;
|
| | | this.variables = new HashMap<String, Object>();
|
| | | }
|
| | | |
| | |
|
| | | public void addVariable(String name, Object o) {
|
| | | variables.put(name, o);
|
| | | }
|
| | |
| | | line(sb, MessageFormat.format("\t$scope.{0} = {1};", var, json));
|
| | | }
|
| | | line(sb, "}");
|
| | | |
| | |
|
| | | response.renderJavascript(sb.toString(), null);
|
| | | }
|
| | |
|
| | |
| | | /**
|
| | | * Activity Page shows a list of recent commits across all visible Gitblit
|
| | | * repositories.
|
| | | * |
| | | *
|
| | | * @author James Moger
|
| | | * |
| | | *
|
| | | */
|
| | |
|
| | | @CacheControl(LastModified.ACTIVITY)
|
| | |
| | |
|
| | | // determine repositories to view and retrieve the activity
|
| | | List<RepositoryModel> models = getRepositories(params);
|
| | | List<Activity> recentActivity = ActivityUtils.getRecentActivity(models, |
| | | List<Activity> recentActivity = ActivityUtils.getRecentActivity(models,
|
| | | daysBack, objectId, getTimeZone());
|
| | |
|
| | | String headerPattern;
|
| | |
| | | headerPattern = getString("gb.recentActivityStats");
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | if (recentActivity.size() == 0) {
|
| | | // no activity, skip graphs and activity panel
|
| | | add(new Label("subheader", MessageFormat.format(headerPattern,
|
| | |
| | | /**
|
| | | * Creates the daily activity line chart, the active repositories pie chart,
|
| | | * and the active authors pie chart
|
| | | * |
| | | *
|
| | | * @param recentActivity
|
| | | * @return
|
| | | */
|
| | |
| | | public abstract class BasePage extends SessionPage {
|
| | |
|
| | | private final Logger logger;
|
| | | |
| | |
|
| | | private transient TimeUtils timeUtils;
|
| | |
|
| | | public BasePage() {
|
| | |
| | | logger = LoggerFactory.getLogger(getClass());
|
| | | customizeHeader();
|
| | | }
|
| | | |
| | |
|
| | | private void customizeHeader() {
|
| | | if (GitBlit.getBoolean(Keys.web.useResponsiveLayout, true)) {
|
| | | add(CSSPackageResource.getHeaderContribution("bootstrap/css/bootstrap-responsive.css"));
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | protected String getLanguageCode() {
|
| | | return GitBlitWebSession.get().getLocale().getLanguage();
|
| | | }
|
| | | |
| | |
|
| | | protected String getCountryCode() {
|
| | | return GitBlitWebSession.get().getLocale().getCountry().toLowerCase();
|
| | | }
|
| | | |
| | |
|
| | | protected TimeUtils getTimeUtils() {
|
| | | if (timeUtils == null) {
|
| | | ResourceBundle bundle; |
| | | ResourceBundle bundle;
|
| | | try {
|
| | | bundle = ResourceBundle.getBundle("com.gitblit.wicket.GitBlitWebApp", GitBlitWebSession.get().getLocale());
|
| | | } catch (Throwable t) {
|
| | |
| | | }
|
| | | return timeUtils;
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void onBeforeRender() {
|
| | | if (GitBlit.isDebugMode()) {
|
| | |
| | | }
|
| | | super.onAfterRender();
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected void setHeaders(WebResponse response) {
|
| | | int expires = GitBlit.getInteger(Keys.web.pageCacheExpires, 0);
|
| | |
| | | super.setHeaders(response);
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Sets the last-modified header date, if appropriate, for this page. The
|
| | | * date used is determined by the CacheControl annotation.
|
| | | * |
| | | *
|
| | | */
|
| | | protected void setLastModified() {
|
| | | if (getClass().isAnnotationPresent(CacheControl.class)) {
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | /**
|
| | | * Sets the last-modified header field and the expires field.
|
| | | * |
| | | *
|
| | | * @param when
|
| | | */
|
| | | protected final void setLastModified(Date when) {
|
| | | if (when == null) {
|
| | | return;
|
| | | }
|
| | | |
| | |
|
| | | if (when.before(GitBlit.getBootDate())) {
|
| | | // last-modified can not be before the Gitblit boot date
|
| | | // this helps ensure that pages are properly refreshed after a
|
| | | // server config change
|
| | | when = GitBlit.getBootDate();
|
| | | }
|
| | | |
| | |
|
| | | int expires = GitBlit.getInteger(Keys.web.pageCacheExpires, 0);
|
| | | WebResponse response = (WebResponse) getResponse();
|
| | | response.setLastModifiedTime(Time.valueOf(when));
|
| | |
| | | }
|
| | | return map;
|
| | | }
|
| | | |
| | |
|
| | | protected Map<AccessPermission, String> getAccessPermissions() {
|
| | | Map<AccessPermission, String> map = new LinkedHashMap<AccessPermission, String>();
|
| | | for (AccessPermission type : AccessPermission.values()) {
|
| | |
| | | }
|
| | | return map;
|
| | | }
|
| | | |
| | |
|
| | | protected Map<FederationStrategy, String> getFederationTypes() {
|
| | | Map<FederationStrategy, String> map = new LinkedHashMap<FederationStrategy, String>();
|
| | | for (FederationStrategy type : FederationStrategy.values()) {
|
| | |
| | | }
|
| | | return map;
|
| | | }
|
| | | |
| | |
|
| | | protected Map<AuthorizationControl, String> getAuthorizationControls() {
|
| | | Map<AuthorizationControl, String> map = new LinkedHashMap<AuthorizationControl, String>();
|
| | | for (AuthorizationControl type : AuthorizationControl.values()) {
|
| | |
| | | HttpServletRequest req = servletWebRequest.getHttpServletRequest();
|
| | | return req.getServerName();
|
| | | }
|
| | | |
| | |
|
| | | protected List<ProjectModel> getProjectModels() {
|
| | | final UserModel user = GitBlitWebSession.get().getUser();
|
| | | List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true);
|
| | | return projects;
|
| | | }
|
| | | |
| | |
|
| | | protected List<ProjectModel> getProjects(PageParameters params) {
|
| | | if (params == null) {
|
| | | return getProjectModels();
|
| | |
| | | public void warn(String message, Throwable t) {
|
| | | logger.warn(message, t);
|
| | | }
|
| | | |
| | |
|
| | | public void error(String message, boolean redirect) {
|
| | | error(message, null, redirect ? getApplication().getHomePage() : null);
|
| | | }
|
| | |
| | | public void error(String message, Throwable t, boolean redirect) {
|
| | | error(message, t, getApplication().getHomePage());
|
| | | }
|
| | | |
| | |
|
| | | public void error(String message, Throwable t, Class<? extends Page> toPage) {
|
| | | error(message, t, toPage, null);
|
| | | }
|
| | | |
| | |
|
| | | public void error(String message, Throwable t, Class<? extends Page> toPage, PageParameters params) {
|
| | | if (t == null) {
|
| | | logger.error(message + " for " + GitBlitWebSession.get().getUsername());
|
| | |
| | | "EEEE, MMMM d, yyyy HH:mm Z");
|
| | | final DateFormat df = new SimpleDateFormat(format);
|
| | | df.setTimeZone(getTimeZone());
|
| | | |
| | |
|
| | | PathModel pathModel = null;
|
| | | List<PathModel> paths = JGitUtils.getFilesInPath(getRepository(), StringUtils.getRootPath(blobPath), commit);
|
| | | for (PathModel path : paths) {
|
| | |
| | | break;
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | if (pathModel == null) {
|
| | | add(new Label("annotation").setVisible(false));
|
| | | add(new Label("missingBlob", missingBlob(blobPath, commit)).setEscapeModelStrings(false));
|
| | | return;
|
| | | }
|
| | | |
| | |
|
| | | add(new Label("missingBlob").setVisible(false));
|
| | | |
| | |
|
| | | List<AnnotatedLine> lines = DiffUtils.blame(getRepository(), blobPath, objectId);
|
| | | ListDataProvider<AnnotatedLine> blameDp = new ListDataProvider<AnnotatedLine>(lines);
|
| | | DataView<AnnotatedLine> blameView = new DataView<AnnotatedLine>("annotation", blameDp) {
|
| | |
| | | private boolean showInitials = true;
|
| | | private String zeroId = ObjectId.zeroId().getName();
|
| | |
|
| | | @Override
|
| | | public void populateItem(final Item<AnnotatedLine> item) {
|
| | | AnnotatedLine entry = item.getModelObject();
|
| | | item.add(new Label("line", "" + entry.lineNumber));
|
| | |
| | | protected String getPageName() {
|
| | | return getString("gb.blame");
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected Class<? extends BasePage> getRepoNavPageClass() {
|
| | | return TreePage.class;
|
| | | }
|
| | | |
| | |
|
| | | protected String missingBlob(String blobPath, RevCommit commit) {
|
| | | StringBuilder sb = new StringBuilder();
|
| | | sb.append("<div class=\"alert alert-error\">");
|
| | |
| | | protected String getPageName() {
|
| | | return getString("gb.diff");
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected Class<? extends BasePage> getRepoNavPageClass() {
|
| | | return TreePage.class;
|
| | |
| | | import com.gitblit.utils.JGitUtils;
|
| | | import com.gitblit.utils.StringUtils;
|
| | | import com.gitblit.wicket.CacheControl;
|
| | | import com.gitblit.wicket.CacheControl.LastModified;
|
| | | import com.gitblit.wicket.ExternalImage;
|
| | | import com.gitblit.wicket.WicketUtils;
|
| | | import com.gitblit.wicket.CacheControl.LastModified;
|
| | | import com.gitblit.wicket.panels.CommitHeaderPanel;
|
| | | import com.gitblit.wicket.panels.PathBreadcrumbsPanel;
|
| | |
|
| | |
| | | Repository r = getRepository();
|
| | | final String blobPath = WicketUtils.getPath(params);
|
| | | String [] encodings = GitBlit.getEncodings();
|
| | | |
| | |
|
| | | if (StringUtils.isEmpty(blobPath)) {
|
| | | // blob by objectid
|
| | |
|
| | |
| | | }
|
| | | }
|
| | | }
|
| | | |
| | |
|
| | | protected String missingBlob(String blobPath, RevCommit commit) {
|
| | | StringBuilder sb = new StringBuilder();
|
| | | sb.append("<div class=\"alert alert-error\">");
|
| | |
| | |
|
| | | protected String generateSourceView(String source, String extension, boolean prettyPrint) {
|
| | | String [] lines = source.split("\n");
|
| | | |
| | |
|
| | | StringBuilder sb = new StringBuilder();
|
| | | sb.append("<!-- start blob table -->");
|
| | | sb.append("<table width=\"100%\"><tbody><tr>");
|
| | | |
| | |
|
| | | // nums column
|
| | | sb.append("<!-- start nums column -->");
|
| | | sb.append("<td id=\"nums\">");
|
| | |
| | | sb.append("</pre>");
|
| | | sb.append("<!-- end nums column -->");
|
| | | sb.append("</td>");
|
| | | |
| | |
|
| | | sb.append("<!-- start lines column -->");
|
| | | sb.append("<td id=\"lines\">");
|
| | | sb.append("<div class=\"sourceview\">");
|
| | |
| | | sb.append("<pre class=\"plainprint\">");
|
| | | }
|
| | | lines = StringUtils.escapeForHtml(source, true).split("\n");
|
| | | |
| | |
|
| | | sb.append("<table width=\"100%\"><tbody>");
|
| | | |
| | |
|
| | | String linePattern = "<tr class=\"{0}\"><td><div><span class=\"line\">{1}</span></div>\r</tr>";
|
| | | for (int i = 0; i < lines.length; i++) {
|
| | | String line = lines[i].replace('\r', ' ');
|
| | |
| | | sb.append("</div>");
|
| | | sb.append("</td>");
|
| | | sb.append("<!-- end lines column -->");
|
| | | |
| | |
|
| | | sb.append("</tr></tbody></table>");
|
| | | sb.append("<!-- end blob table -->");
|
| | | |
| | |
|
| | | return sb.toString();
|
| | | }
|
| | |
|
| | |
| | | protected String getPageName() {
|
| | | return getString("gb.view");
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected Class<? extends BasePage> getRepoNavPageClass() {
|
| | | return TreePage.class;
|
| | |
| | | // no authentication enabled
|
| | | throw new RestartResponseException(getApplication().getHomePage());
|
| | | }
|
| | | |
| | | UserModel user = GitBlitWebSession.get().getUser(); |
| | |
|
| | | UserModel user = GitBlitWebSession.get().getUser();
|
| | | if (!GitBlit.self().supportsCredentialChanges(user)) {
|
| | | error(MessageFormat.format(getString("gb.userServiceDoesNotPermitPasswordChanges"),
|
| | | GitBlit.getString(Keys.realm.userService, "${baseFolder}/users.conf")), true);
|
| | | }
|
| | | |
| | |
|
| | | setupPage(getString("gb.changePassword"), user.username);
|
| | |
|
| | | StatelessForm<Void> form = new StatelessForm<Void>("passwordForm") {
|
| | |
| | | DataView<GitNote> notesView = new DataView<GitNote>("notes", notesDp) { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Override |
| | | public void populateItem(final Item<GitNote> item) { |
| | | GitNote entry = item.getModelObject(); |
| | | item.add(new RefsPanel("refName", repositoryName, Arrays.asList(entry.notesRef))); |
| | |
| | | } |
| | | }; |
| | | add(notesView.setVisible(notes.size() > 0)); |
| | | |
| | | |
| | | // changed paths list |
| | | add(new CommitLegendPanel("commitLegend", diff.stat.paths)); |
| | | ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(diff.stat.paths); |
| | |
| | | private static final long serialVersionUID = 1L; |
| | | int counter; |
| | | |
| | | @Override |
| | | public void populateItem(final Item<PathChangeModel> item) { |
| | | final PathChangeModel entry = item.getModelObject(); |
| | | Label changeType = new Label("changeType", ""); |
| | |
| | | .newPathParameter(repositoryName, entry.commitId, entry.path)) |
| | | .setEnabled(!entry.changeType.equals(ChangeType.ADD))); |
| | | } |
| | | |
| | | |
| | | WicketUtils.setAlternatingBackground(item, counter); |
| | | counter++; |
| | | } |
| | |
| | | protected String getPageName() { |
| | | return getString("gb.commitdiff"); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | protected Class<? extends BasePage> getRepoNavPageClass() { |
| | | return LogPage.class; |
| | |
| | |
|
| | | Repository r = getRepository();
|
| | | RevCommit c = getCommit();
|
| | | |
| | |
|
| | | List<String> parents = new ArrayList<String>();
|
| | | if (c.getParentCount() > 0) {
|
| | | for (RevCommit parent : c.getParents()) {
|
| | |
| | | add(createPersonPanel("commitAuthor", c.getAuthorIdent(), Constants.SearchType.AUTHOR));
|
| | | add(WicketUtils.createTimestampLabel("commitAuthorDate", c.getAuthorIdent().getWhen(),
|
| | | getTimeZone(), getTimeUtils()));
|
| | | |
| | |
|
| | | // committer
|
| | | add(createPersonPanel("commitCommitter", c.getCommitterIdent(), Constants.SearchType.COMMITTER));
|
| | | add(WicketUtils.createTimestampLabel("commitCommitterDate",
|
| | |
| | | newCommitParameter()));
|
| | | add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));
|
| | | final String baseUrl = WicketUtils.getGitblitURL(getRequest());
|
| | | |
| | |
|
| | | add(new CompressedDownloadsPanel("compressedLinks", baseUrl, repositoryName, objectId, null));
|
| | |
|
| | | // Parent Commits
|
| | |
| | | DataView<String> parentsView = new DataView<String>("commitParents", parentsDp) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void populateItem(final Item<String> item) {
|
| | | String entry = item.getModelObject();
|
| | | item.add(new LinkPanel("commitParent", "list", entry, CommitPage.class,
|
| | |
| | | DataView<GitNote> notesView = new DataView<GitNote>("notes", notesDp) {
|
| | | private static final long serialVersionUID = 1L;
|
| | |
|
| | | @Override
|
| | | public void populateItem(final Item<GitNote> item) {
|
| | | GitNote entry = item.getModelObject();
|
| | | item.add(new RefsPanel("refName", repositoryName, Arrays.asList(entry.notesRef)));
|
| | |
| | |
|
| | | // changed paths list
|
| | | List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, c);
|
| | | |
| | |
|
| | | // add commit diffstat
|
| | | int insertions = 0;
|
| | | int deletions = 0;
|
| | |
| | | deletions += pcm.deletions;
|
| | | }
|
| | | add(new DiffStatPanel("diffStat", insertions, deletions));
|
| | | |
| | |
|
| | | add(new CommitLegendPanel("commitLegend", paths));
|
| | | ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
|
| | | DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
|
| | | private static final long serialVersionUID = 1L;
|
| | | int counter;
|
| | |
|
| | | @Override
|
| | | public void populateItem(final Item<PathChangeModel> item) {
|
| | | final PathChangeModel entry = item.getModelObject();
|
| | | Label changeType = new Label("changeType", "");
|
| | |
| | | SubmoduleModel submodule = getSubmodule(entry.path);
|
| | | submodulePath = submodule.gitblitPath;
|
| | | hasSubmodule = submodule.hasSubmodule;
|
| | | |
| | |
|
| | | item.add(new LinkPanel("pathName", "list", entry.path + " @ " +
|
| | | getShortObjectId(submoduleId), TreePage.class,
|
| | | WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule));
|
| | |
| | | WicketUtils
|
| | | .newPathParameter(repositoryName, entry.commitId, path)));
|
| | | }
|
| | | |
| | |
|
| | | // quick links
|
| | | if (entry.isSubmodule()) {
|
| | | // submodule |
| | | // submodule
|
| | | item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, WicketUtils
|
| | | .newPathParameter(repositoryName, entry.commitId, entry.path))
|
| | | .setEnabled(!entry.changeType.equals(ChangeType.ADD)));
|
| | |
| | | protected String getPageName() {
|
| | | return getString("gb.commit");
|
| | | }
|
| | | |
| | |
|
| | | @Override
|
| | | protected Class<? extends BasePage> getRepoNavPageClass() {
|
| | | return LogPage.class;
|
src/main/java/com/gitblit/wicket/pages/ComparePage.java
src/main/java/com/gitblit/wicket/pages/DashboardPage.java
src/main/java/com/gitblit/wicket/pages/DocsPage.java
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
src/main/java/com/gitblit/wicket/pages/EditUserPage.java
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java
src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.java
src/main/java/com/gitblit/wicket/pages/ForkPage.java
src/main/java/com/gitblit/wicket/pages/ForksPage.java
src/main/java/com/gitblit/wicket/pages/GitSearchPage.java
src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.java
src/main/java/com/gitblit/wicket/pages/HistoryPage.java
src/main/java/com/gitblit/wicket/pages/LogPage.java
src/main/java/com/gitblit/wicket/pages/LogoutPage.java
src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.java
src/main/java/com/gitblit/wicket/pages/MarkdownPage.java
src/main/java/com/gitblit/wicket/pages/MetricsPage.java
src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java
src/main/java/com/gitblit/wicket/pages/OverviewPage.java
src/main/java/com/gitblit/wicket/pages/ProjectPage.java
src/main/java/com/gitblit/wicket/pages/ProjectsPage.java
src/main/java/com/gitblit/wicket/pages/RawPage.java
src/main/java/com/gitblit/wicket/pages/ReflogPage.java
src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java
src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java
src/main/java/com/gitblit/wicket/pages/RootPage.java
src/main/java/com/gitblit/wicket/pages/RootSubPage.java
src/main/java/com/gitblit/wicket/pages/SendProposalPage.java
src/main/java/com/gitblit/wicket/pages/SummaryPage.java
src/main/java/com/gitblit/wicket/pages/TagPage.java
src/main/java/com/gitblit/wicket/pages/TagsPage.java
src/main/java/com/gitblit/wicket/pages/TreePage.java
src/main/java/com/gitblit/wicket/pages/UserPage.java
src/main/java/com/gitblit/wicket/pages/UsersPage.java
src/main/java/com/gitblit/wicket/panels/ActivityPanel.java
src/main/java/com/gitblit/wicket/panels/BasePanel.java
src/main/java/com/gitblit/wicket/panels/BranchesPanel.java
src/main/java/com/gitblit/wicket/panels/BulletListPanel.java
src/main/java/com/gitblit/wicket/panels/CommitLegendPanel.java
src/main/java/com/gitblit/wicket/panels/CompressedDownloadsPanel.java
src/main/java/com/gitblit/wicket/panels/DigestsPanel.java
src/main/java/com/gitblit/wicket/panels/DropDownMenu.java
src/main/java/com/gitblit/wicket/panels/FederationProposalsPanel.java
src/main/java/com/gitblit/wicket/panels/FederationRegistrationsPanel.java
src/main/java/com/gitblit/wicket/panels/FederationTokensPanel.java
src/main/java/com/gitblit/wicket/panels/FilterableProjectList.java
src/main/java/com/gitblit/wicket/panels/FilterableRepositoryList.java
src/main/java/com/gitblit/wicket/panels/GravatarImage.java
src/main/java/com/gitblit/wicket/panels/HistoryPanel.java
src/main/java/com/gitblit/wicket/panels/LinkPanel.java
src/main/java/com/gitblit/wicket/panels/LogPanel.java
src/main/java/com/gitblit/wicket/panels/NavigationPanel.java
src/main/java/com/gitblit/wicket/panels/ObjectContainer.java
src/main/java/com/gitblit/wicket/panels/PagerPanel.java
src/main/java/com/gitblit/wicket/panels/PathBreadcrumbsPanel.java
src/main/java/com/gitblit/wicket/panels/ReflogPanel.java
src/main/java/com/gitblit/wicket/panels/RefsPanel.java
src/main/java/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java
src/main/java/com/gitblit/wicket/panels/RepositoriesPanel.java
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java
src/main/java/com/gitblit/wicket/panels/SearchPanel.java
src/main/java/com/gitblit/wicket/panels/ShockWaveComponent.java
src/main/java/com/gitblit/wicket/panels/TagsPanel.java
src/main/java/com/gitblit/wicket/panels/TeamsPanel.java
src/main/java/com/gitblit/wicket/panels/UsersPanel.java
src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java
src/test/java/com/gitblit/tests/JnaUtilsTest.java |