Package com.gitblit.manager

Source Code of com.gitblit.manager.FederationManager

/*
* Copyright 2013 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.manager;

import java.io.File;
import java.io.FileFilter;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.FederationSet;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.Base64;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.JsonUtils;
import com.gitblit.utils.StringUtils;

/**
* Federation manager controls all aspects of handling federation sets, tokens,
* and proposals.
*
* @author James Moger
*
*/
public class FederationManager implements IFederationManager {

  private final Logger logger = LoggerFactory.getLogger(getClass());

  private final List<FederationModel> federationRegistrations = Collections
      .synchronizedList(new ArrayList<FederationModel>());

  private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>();

  private final IStoredSettings settings;

  private final IRuntimeManager runtimeManager;

  private final INotificationManager notificationManager;

  private final IRepositoryManager repositoryManager;

  public FederationManager(
      IRuntimeManager runtimeManager,
      INotificationManager notificationManager,
      IRepositoryManager repositoryManager) {

    this.settings = runtimeManager.getSettings();
    this.runtimeManager = runtimeManager;
    this.notificationManager = notificationManager;
    this.repositoryManager = repositoryManager;
  }

  @Override
  public FederationManager start() {
    return this;
  }

  @Override
  public FederationManager stop() {
    return this;
  }

  /**
   * 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
   */
  @Override
  public File getProposalsFolder() {
    return runtimeManager.getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals");
  }

  @Override
  public boolean canFederate() {
    String passphrase = settings.getString(Keys.federation.passphrase, "");
    return !StringUtils.isEmpty(passphrase);
  }

  /**
   * Returns the federation user account.
   *
   * @return the federation user account
   */
  @Override
  public UserModel getFederationUser() {
    // the federation user is an administrator
    UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
    federationUser.canAdmin = true;
    return federationUser;
  }

  @Override
  public UserModel authenticate(HttpServletRequest httpRequest) {
    if (canFederate()) {
      // try to authenticate federation user for cloning
      final String authorization = httpRequest.getHeader("Authorization");
      if (authorization != null && authorization.startsWith("Basic")) {
        // Authorization: Basic base64credentials
        String base64Credentials = authorization.substring("Basic".length()).trim();
        String credentials = new String(Base64.decode(base64Credentials),
            Charset.forName("UTF-8"));
        // credentials = username:password
        final String[] values = credentials.split(":", 2);
        if (values.length == 2) {
          String username = StringUtils.decodeUsername(values[0]);
          String password = values[1];
          if (username.equalsIgnoreCase(Constants.FEDERATION_USER)) {
            List<String> tokens = getFederationTokens();
            if (tokens.contains(password)) {
              return getFederationUser();
            }
          }
        }
      }
    }
    return null;
  }

  /**
   * Returns the list of federated gitblit instances that this instance will
   * try to pull.
   *
   * @return list of registered gitblit instances
   */
  @Override
  public List<FederationModel> getFederationRegistrations() {
    if (federationRegistrations.isEmpty()) {
      federationRegistrations.addAll(FederationUtils.getFederationRegistrations(settings));
    }
    return federationRegistrations;
  }

  /**
   * Retrieve the specified federation registration.
   *
   * @param name
   *            the name of the registration
   * @return a federation registration
   */
  @Override
  public FederationModel getFederationRegistration(String url, String name) {
    // check registrations
    for (FederationModel r : getFederationRegistrations()) {
      if (r.name.equals(name) && r.url.equals(url)) {
        return r;
      }
    }

    // check the results
    for (FederationModel r : getFederationResultRegistrations()) {
      if (r.name.equals(name) && r.url.equals(url)) {
        return r;
      }
    }
    return null;
  }

  /**
   * Returns the list of federation sets.
   *
   * @return list of federation sets
   */
  @Override
  public List<FederationSet> getFederationSets(String gitblitUrl) {
    List<FederationSet> list = new ArrayList<FederationSet>();
    // generate standard tokens
    for (FederationToken type : FederationToken.values()) {
      FederationSet fset = new FederationSet(type.toString(), type, getFederationToken(type));
      fset.repositories = getRepositories(gitblitUrl, fset.token);
      list.add(fset);
    }
    // generate tokens for federation sets
    for (String set : settings.getStrings(Keys.federation.sets)) {
      FederationSet fset = new FederationSet(set, FederationToken.REPOSITORIES,
          getFederationToken(set));
      fset.repositories = getRepositories(gitblitUrl, fset.token);
      list.add(fset);
    }
    return list;
  }

  /**
   * Returns the list of possible federation tokens for this Gitblit instance.
   *
   * @return list of federation tokens
   */
  @Override
  public List<String> getFederationTokens() {
    List<String> tokens = new ArrayList<String>();
    // generate standard tokens
    for (FederationToken type : FederationToken.values()) {
      tokens.add(getFederationToken(type));
    }
    // generate tokens for federation sets
    for (String set : settings.getStrings(Keys.federation.sets)) {
      tokens.add(getFederationToken(set));
    }
    return tokens;
  }

  /**
   * Returns the specified federation token for this Gitblit instance.
   *
   * @param type
   * @return a federation token
   */
  @Override
  public String getFederationToken(FederationToken type) {
    return getFederationToken(type.name());
  }

  /**
   * Returns the specified federation token for this Gitblit instance.
   *
   * @param value
   * @return a federation token
   */
  @Override
  public String getFederationToken(String value) {
    String passphrase = settings.getString(Keys.federation.passphrase, "");
    return StringUtils.getSHA1(passphrase + "-" + value);
  }

  /**
   * 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
   */
  @Override
  public boolean validateFederationRequest(FederationRequest req, String token) {
    String all = getFederationToken(FederationToken.ALL);
    String unr = getFederationToken(FederationToken.USERS_AND_REPOSITORIES);
    String jur = getFederationToken(FederationToken.REPOSITORIES);
    switch (req) {
    case PULL_REPOSITORIES:
      return token.equals(all) || token.equals(unr) || token.equals(jur);
    case PULL_USERS:
    case PULL_TEAMS:
      return token.equals(all) || token.equals(unr);
    case PULL_SETTINGS:
    case PULL_SCRIPTS:
      return token.equals(all);
    default:
      break;
    }
    return false;
  }

  /**
   * Acknowledge and cache the status of a remote Gitblit instance.
   *
   * @param identification
   *            the identification of the pulling Gitblit instance
   * @param registration
   *            the registration from the pulling Gitblit instance
   * @return true if acknowledged
   */
  @Override
  public boolean acknowledgeFederationStatus(String identification, FederationModel registration) {
    // reset the url to the identification of the pulling Gitblit instance
    registration.url = identification;
    String id = identification;
    if (!StringUtils.isEmpty(registration.folder)) {
      id += "-" + registration.folder;
    }
    federationPullResults.put(id, registration);
    return true;
  }

  /**
   * Returns the list of registration results.
   *
   * @return the list of registration results
   */
  @Override
  public List<FederationModel> getFederationResultRegistrations() {
    return new ArrayList<FederationModel>(federationPullResults.values());
  }

  /**
   * Submit a federation proposal. The proposal is cached locally and the
   * Gitblit administrator(s) are notified via email.
   *
   * @param proposal
   *            the proposal
   * @param gitblitUrl
   *            the url of your gitblit instance to send an email to
   *            administrators
   * @return true if the proposal was submitted
   */
  @Override
  public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) {
    // convert proposal to json
    String json = JsonUtils.toJsonString(proposal);

    try {
      // make the proposals folder
      File proposalsFolder = getProposalsFolder();
      proposalsFolder.mkdirs();

      // cache json to a file
      File file = new File(proposalsFolder, proposal.token + Constants.PROPOSAL_EXT);
      com.gitblit.utils.FileUtils.writeContent(file, json);
    } catch (Exception e) {
      logger.error(MessageFormat.format("Failed to cache proposal from {0}", proposal.url), e);
    }

    // send an email, if possible
    notificationManager.sendMailToAdministrators("Federation proposal from " + proposal.url,
        "Please review the proposal @ " + gitblitUrl + "/proposal/" + proposal.token);
    return true;
  }

  /**
   * Returns the list of pending federation proposals
   *
   * @return list of federation proposals
   */
  @Override
  public List<FederationProposal> getPendingFederationProposals() {
    List<FederationProposal> list = new ArrayList<FederationProposal>();
    File folder = getProposalsFolder();
    if (folder.exists()) {
      File[] files = folder.listFiles(new FileFilter() {
        @Override
        public boolean accept(File file) {
          return file.isFile()
              && file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT);
        }
      });
      for (File file : files) {
        String json = com.gitblit.utils.FileUtils.readContent(file, null);
        FederationProposal proposal = JsonUtils.fromJsonString(json,
            FederationProposal.class);
        list.add(proposal);
      }
    }
    return list;
  }

  /**
   * Get repositories for the specified token.
   *
   * @param gitblitUrl
   *            the base url of this gitblit instance
   * @param token
   *            the federation token
   * @return a map of <cloneurl, RepositoryModel>
   */
  @Override
  public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) {
    Map<String, String> federationSets = new HashMap<String, String>();
    for (String set : settings.getStrings(Keys.federation.sets)) {
      federationSets.put(getFederationToken(set), set);
    }

    // Determine the Gitblit clone url
    StringBuilder sb = new StringBuilder();
    sb.append(gitblitUrl);
    sb.append(Constants.R_PATH);
    sb.append("{0}");
    String cloneUrl = sb.toString();

    // Retrieve all available repositories
    UserModel user = getFederationUser();
    List<RepositoryModel> list = repositoryManager.getRepositoryModels(user);

    // create the [cloneurl, repositoryModel] map
    Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
    for (RepositoryModel model : list) {
      // by default, setup the url for THIS repository
      String url = MessageFormat.format(cloneUrl, model.name);
      switch (model.federationStrategy) {
      case EXCLUDE:
        // skip this repository
        continue;
      case FEDERATE_ORIGIN:
        // federate the origin, if it is defined
        if (!StringUtils.isEmpty(model.origin)) {
          url = model.origin;
        }
        break;
      default:
        break;
      }

      if (federationSets.containsKey(token)) {
        // include repositories only for federation set
        String set = federationSets.get(token);
        if (model.federationSets.contains(set)) {
          repositories.put(url, model);
        }
      } else {
        // standard federation token for ALL
        repositories.put(url, model);
      }
    }
    return repositories;
  }

  /**
   * Creates a proposal from the token.
   *
   * @param gitblitUrl
   *            the url of this Gitblit instance
   * @param token
   * @return a potential proposal
   */
  @Override
  public FederationProposal createFederationProposal(String gitblitUrl, String token) {
    FederationToken tokenType = FederationToken.REPOSITORIES;
    for (FederationToken type : FederationToken.values()) {
      if (token.equals(getFederationToken(type))) {
        tokenType = type;
        break;
      }
    }
    Map<String, RepositoryModel> repositories = getRepositories(gitblitUrl, token);
    FederationProposal proposal = new FederationProposal(gitblitUrl, tokenType, token,
        repositories);
    return proposal;
  }

  /**
   * Returns the proposal identified by the supplied token.
   *
   * @param token
   * @return the specified proposal or null
   */
  @Override
  public FederationProposal getPendingFederationProposal(String token) {
    List<FederationProposal> list = getPendingFederationProposals();
    for (FederationProposal proposal : list) {
      if (proposal.token.equals(token)) {
        return proposal;
      }
    }
    return null;
  }

  /**
   * Deletes a pending federation proposal.
   *
   * @param a
   *            proposal
   * @return true if the proposal was deleted
   */
  @Override
  public boolean deletePendingFederationProposal(FederationProposal proposal) {
    File folder = getProposalsFolder();
    File file = new File(folder, proposal.token + Constants.PROPOSAL_EXT);
    return file.delete();
  }
}
TOP

Related Classes of com.gitblit.manager.FederationManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.