James Moger
2011-10-25 486ee115abb831b2ec78be6777fb1bca9e931df0
Documentation. Changed status RPC protection. Status tab for Manager.
2 files added
10 files modified
333 ■■■■■ changed files
distrib/gitblit.properties 2 ●●● patch | view | raw | blame | history
docs/01_setup.mkd 2 ●●● patch | view | raw | blame | history
docs/02_rpc.mkd 8 ●●●● patch | view | raw | blame | history
docs/03_faq.mkd 9 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java 8 ●●●●● patch | view | raw | blame | history
src/com/gitblit/RpcServlet.java 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitClient.java 7 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitPanel.java 46 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/PropertiesTableModel.java 106 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/StatusPanel.java 119 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/ServerStatus.java 11 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 9 ●●●● patch | view | raw | blame | history
distrib/gitblit.properties
@@ -184,7 +184,7 @@
# to preemptively replace '/' with '*' or '!' for url string parameters.
#
# <https://issues.apache.org/jira/browse/WICKET-1303>
# <http://tomcat.apache.org/security-6.html>
# <http://tomcat.apache.org/security-6.html#Fixed_in_Apache_Tomcat_6.0.10>
#
# SINCE 0.5.2
web.forwardSlashCharacter = /
docs/01_setup.mkd
@@ -159,7 +159,7 @@
Whitespace is illegal.
#### Passwords
User passwords are CASE-SENSITIVE and may be *plain* or *md5* formatted (see `gitblit.properties` -> *realm.passwordStorage*).
User passwords are CASE-SENSITIVE and may be *plain*, *md5*, or *combined-md5* formatted (see `gitblit.properties` -> *realm.passwordStorage*).
#### User Roles
There are two actual *roles* in Gitblit: *#admin*, which grants administrative powers to that user, and *#notfederated*, which prevents an account from being pulled by another Gitblit instance.  Administrators automatically have access to all repositories.  All other *roles* are repository names.  If a repository is access-restricted, the user must have the repository's name within his/her roles to bypass the access restriction.  This is how users are granted access to a restricted repository.
docs/02_rpc.mkd
@@ -14,9 +14,11 @@
### RPC Requests
<table>
<tr><th colspan='2'>url parameters</th><th rowspan='2'>required<br/>permission</th><th colspan='2'>json</th></tr>
<tr><th colspan='2'>url parameters</th><th rowspan='2'>required<br/>user<br/>permission</th><th colspan='2'>json</th></tr>
<tr><th>req=</th><th>name=</th><th>post body</th><th>response body</th></tr>
<tr><td colspan='5'><em>web.enableRpcServlet=true</em></td></tr>
<tr><td>LIST_REPOSITORIES</td><td>-</td><td>-</td><td>-</td><td>Map&lt;String, RepositoryModel&gt;</td></tr>
<tr><td colspan='5'><em>web.enableRpcManagement=true</em></td></tr>
<tr><td>CREATE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>RepositoryModel</td><td>-</td></tr>
<tr><td>EDIT_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>RepositoryModel</td><td>-</td></tr>
<tr><td>DELETE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>-</td><td>-</td></tr>
@@ -30,6 +32,7 @@
<tr><td>LIST_FEDERATION_RESULTS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;FederationModel&gt;</td></tr>
<tr><td>LIST_FEDERATION_PROPOSALS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;FederationProposal&gt;</td></tr>
<tr><td>LIST_FEDERATION_SETS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List&lt;FederationSet&gt;</td></tr>
<tr><td colspan='5'><em>web.enableRpcAdministration=true</em></td></tr>
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>admin</em></td><td>-</td><td>ServerSettings (see example below)</td></tr>
<tr><td>EDIT_SETTINGS</td><td>-</td><td><em>admin</em></td><td>Map&lt;String, String&gt;</td><td>-</td></tr>
<tr><td>LIST_STATUS</td><td>-</td><td><em>admin</em></td><td>-</td><td>ServerStatus (see example below)</td></tr>
@@ -51,6 +54,9 @@
[Gitblit Manager](http://code.google.com/p/gitblit/downloads/detail?name=%MANAGER%) is an example Java/Swing application that allows remote administration of a Gitblit server.  
This application exercises many, but not all, methods from the utility class `com.gitblit.utils.RpcUtils`.
**NOTE:**
Gitblit Manager stores your login credentials **INSECURELY** in homedir/.gitblit/config.
### EGit "Import from Gitblit" Feature (Planning)
One obvious goal of a Gitblit RPC mechanism would be to have an EGit Feature that allows authentication and enumeration of Gitblit repositories from the Eclipse *Import...* menu.  Cloning (hopefully batch) would be delegated to EGit.
docs/03_faq.mkd
@@ -61,6 +61,15 @@
If you are using Apache mod_proxy, specify [AllowEncodedSlashes NoDecode](http://httpd.apache.org/docs/2.2/mod/core.html#allowencodedslashes).
### Running Gitblit on Tomcat
Tomcat takes the extra precaution of [disallowing embedded slashes by default](http://tomcat.apache.org/security-6.html#Fixed_in_Apache_Tomcat_6.0.10).  This breaks Gitblit urls.
You have a few options on how to handle this scenario:
1. [Tweak Tomcat](http://tomcat.apache.org/security-6.html#Fixed_in_Apache_Tomcat_6.0.10)
2. *web.mountParameters = false* and use non-pretty, parameterized urls
3. *web.forwardSlashCharacter = !* which tells Gitblit to use **!** instead of **/**
## General Interest Questions
### Gitblit?  What kind of name is that?
src/com/gitblit/GitBlit.java
@@ -253,7 +253,7 @@
     * @return true if the update succeeded
     */
    public boolean updateSettings(Map<String, String> updatedSettings) {
        return settings.saveSettings(updatedSettings);
        return settings.saveSettings(updatedSettings);
    }
    public ServerStatus getStatus() {
@@ -1389,7 +1389,7 @@
        repositoriesFolder = new File(settings.getString(Keys.git.repositoriesFolder, "git"));
        logger.info("Git repositories folder " + repositoriesFolder.getAbsolutePath());
        repositoryResolver = new FileResolver<Void>(repositoriesFolder, exportAll);
        serverStatus = new ServerStatus();
        serverStatus = new ServerStatus(isGO());
        String realm = settings.getString(Keys.realm.userService, "users.properties");
        IUserService loginService = null;
        try {
@@ -1433,12 +1433,14 @@
    @Override
    public void contextInitialized(ServletContextEvent contextEvent) {
        servletContext = contextEvent.getServletContext();
        settingsModel = loadSettingModels();
        settingsModel = loadSettingModels();
        if (settings == null) {
            // Gitblit WAR is running in a servlet container
            WebXmlSettings webxmlSettings = new WebXmlSettings(contextEvent.getServletContext());
            configureContext(webxmlSettings, true);
        }
        serverStatus.servletContainer = servletContext.getServerInfo();
    }
    /**
src/com/gitblit/RpcServlet.java
@@ -199,7 +199,11 @@
            }
        } else if (RpcRequest.LIST_STATUS.equals(reqType)) {
            // return the server's status information
            result = GitBlit.self().getStatus();
            if (GitBlit.getBoolean(Keys.web.enableRpcAdministration, false)) {
                result = GitBlit.self().getStatus();
            } else {
                response.sendError(notAllowedCode);
            }
        }
        // send the result of the request
src/com/gitblit/client/GitblitClient.java
@@ -87,7 +87,7 @@
        try {
            refreshSettings();
            status = RpcUtils.getStatus(url, account, password);
            refreshStatus();
            allowAdministration = true;
        } catch (UnauthorizedException e) {
        } catch (ForbiddenException e) {
@@ -141,6 +141,11 @@
        settings = RpcUtils.getSettings(url, account, password);
        return settings;
    }
    public ServerStatus refreshStatus() throws IOException {
        status = RpcUtils.getStatus(url, account, password);
        return status;
    }
    public List<FederationModel> refreshFederationRegistrations() throws IOException {
        List<FederationModel> list = RpcUtils.getFederationRegistrations(url, account, password);
src/com/gitblit/client/GitblitPanel.java
@@ -118,6 +118,8 @@
    private HeaderPanel settingsHeader;
    private StatusPanel statusPanel;
    public GitblitPanel(GitblitRegistration reg) {
        this(reg.url, reg.account, reg.password);
    }
@@ -129,6 +131,7 @@
        tabs.addTab(Translation.get("gb.repositories"), createRepositoriesPanel());
        tabs.addTab(Translation.get("gb.users"), createUsersPanel());
        tabs.addTab(Translation.get("gb.settings"), createSettingsPanel());
        tabs.addTab(Translation.get("gb.status"), createStatusPanel());
        setLayout(new BorderLayout());
        add(tabs, BorderLayout.CENTER);
@@ -482,6 +485,24 @@
        return settingsPanel;
    }
    private JPanel createStatusPanel() {
        JButton refreshStatus = new JButton(Translation.get("gb.refresh"));
        refreshStatus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshStatus();
            }
        });
        JPanel controls = new JPanel();
        controls.add(refreshStatus);
        JPanel panel = new JPanel(new BorderLayout());
        statusPanel = new StatusPanel();
        panel.add(statusPanel, BorderLayout.CENTER);
        panel.add(controls, BorderLayout.SOUTH);
        return panel;
    }
    public void login() throws IOException {
        gitblit.login();
@@ -505,6 +526,7 @@
        if (gitblit.allowAdministration()) {
            updateSettingsTable();
            updateStatusPanel();
            Utils.packColumns(settingsTable, 5);
        } else {
            // remove the settings tab
@@ -537,6 +559,10 @@
        settingsModel.setSettings(gitblit.getSettings());
        settingsModel.fireTableDataChanged();
        settingsHeader.setText(Translation.get("gb.settings"));
    }
    private void updateStatusPanel() {
        statusPanel.setStatus(gitblit.getStatus());
    }
    private void filterRepositories(final String fragment) {
@@ -934,6 +960,22 @@
        worker.execute();
    }
    protected void refreshStatus() {
        GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_STATUS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshStatus();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateStatusPanel();
            }
        };
        worker.execute();
    }
    protected void editSetting(final SettingModel settingModel) {
        final JTextField textField = new JTextField(settingModel.currentValue);
        JPanel editPanel = new JPanel(new GridLayout(0, 1));
@@ -949,8 +991,8 @@
        if (settingModel.currentValue.equals(settingModel.defaultValue)) {
            options = new String[] { Translation.get("gb.cancel"), Translation.get("gb.save") };
        } else {
            options = new String[] { Translation.get("gb.cancel"), Translation.get("gb.setDefault"),
                    Translation.get("gb.save") };
            options = new String[] { Translation.get("gb.cancel"),
                    Translation.get("gb.setDefault"), Translation.get("gb.save") };
        }
        String defaultOption = options[0];
        int selection = JOptionPane.showOptionDialog(GitblitPanel.this, settingPanel,
src/com/gitblit/client/PropertiesTableModel.java
New file
@@ -0,0 +1,106 @@
/*
 * 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.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.table.AbstractTableModel;
/**
 * Table model of a map of properties.
 *
 * @author James Moger
 *
 */
public class PropertiesTableModel extends AbstractTableModel {
    private static final long serialVersionUID = 1L;
    List<String> keys;
    Map<String, String> map;
    enum Columns {
        Name, Value;
        @Override
        public String toString() {
            return name().replace('_', ' ');
        }
    }
    public PropertiesTableModel() {
        this(new HashMap<String, String>());
    }
    public PropertiesTableModel(Map<String, String> map) {
        setProperties(map);
    }
    public void setProperties(Map<String, String> map) {
        this.map = map;
        keys = new ArrayList<String>(map.keySet());
        Collections.sort(this.keys);
    }
    @Override
    public int getRowCount() {
        return keys.size();
    }
    @Override
    public int getColumnCount() {
        return Columns.values().length;
    }
    @Override
    public String getColumnName(int column) {
        Columns col = Columns.values()[column];
        switch (col) {
        case Name:
            return Translation.get("gb.name");
        }
        return "";
    }
    /**
     * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
     *
     * @param columnIndex
     *            the column being queried
     * @return the Object.class
     */
    public Class<?> getColumnClass(int columnIndex) {
        return String.class;
    }
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        String key = keys.get(rowIndex);
        Columns col = Columns.values()[columnIndex];
        switch (col) {
        case Name:
            return key;
        case Value:
            return map.get(key);
        }
        return null;
    }
}
src/com/gitblit/client/StatusPanel.java
New file
@@ -0,0 +1,119 @@
/*
 * 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.client;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import com.gitblit.models.ServerStatus;
import com.gitblit.utils.ByteFormat;
/**
 * This panel displays the server status.
 *
 * @author James Moger
 */
public class StatusPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final Insets insets = new Insets(5, 5, 5, 5);
    private JLabel bootDate;
    private JLabel servletContainer;
    private JLabel heapMaximum;
    private JLabel heapAllocated;
    private JLabel heapUsed;
    private PropertiesTableModel model;
    private HeaderPanel headerPanel;
    public StatusPanel() {
        super();
        initialize();
    }
    public StatusPanel(ServerStatus status) {
        this();
        setStatus(status);
    }
    private void initialize() {
        bootDate = new JLabel();
        servletContainer = new JLabel();
        heapMaximum = new JLabel();
        heapAllocated = new JLabel();
        heapUsed = new JLabel();
        JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));
        fieldsPanel.add(createFieldPanel("gb.bootDate", bootDate));
        fieldsPanel.add(createFieldPanel("gb.servletContainer", servletContainer));
        fieldsPanel.add(createFieldPanel("gb.heapUsed", heapUsed));
        fieldsPanel.add(createFieldPanel("gb.heapAllocated", heapAllocated));
        fieldsPanel.add(createFieldPanel("gb.heapMaximum", heapMaximum));
        model = new PropertiesTableModel();
        JTable propertiesTable = Utils.newTable(model);
        String name = propertiesTable.getColumnName(PropertiesTableModel.Columns.Name.ordinal());
        NameRenderer nameRenderer = new NameRenderer();
        propertiesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);
        propertiesTable.getColumn(name).setCellRenderer(nameRenderer);
        JPanel centerPanel = new JPanel(new BorderLayout());
        centerPanel.add(fieldsPanel, BorderLayout.NORTH);
        centerPanel.add(new JScrollPane(propertiesTable), BorderLayout.CENTER);
        headerPanel = new HeaderPanel(Translation.get("gb.status"), null);
        setLayout(new BorderLayout());
        add(headerPanel, BorderLayout.NORTH);
        add(centerPanel, BorderLayout.CENTER);
    }
    private JPanel createFieldPanel(String key, JLabel valueLabel) {
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
        JLabel textLabel = new JLabel(Translation.get(key));
        textLabel.setFont(textLabel.getFont().deriveFont(Font.BOLD));
        textLabel.setPreferredSize(new Dimension(120, valueLabel.getFont().getSize() + 4));
        panel.add(textLabel);
        panel.add(valueLabel);
        return panel;
    }
    @Override
    public Insets getInsets() {
        return insets;
    }
    public void setStatus(ServerStatus status) {
        headerPanel.setText(Translation.get("gb.status"));
        bootDate.setText(status.bootDate.toString());
        servletContainer.setText(status.servletContainer);
        ByteFormat byteFormat = new ByteFormat();
        heapMaximum.setText(byteFormat.format(status.heapMaximum));
        heapAllocated.setText(byteFormat.format(status.heapAllocated));
        heapUsed.setText(byteFormat.format(status.heapAllocated - status.heapFree) + " ("
                + byteFormat.format(status.heapFree) + " " + Translation.get("gb.free") + ")");
        model.setProperties(status.systemProperties);
        model.fireTableDataChanged();
    }
}
src/com/gitblit/models/ServerStatus.java
@@ -33,18 +33,23 @@
    public final Date bootDate;
    
    public final boolean isGO;
    public final Map<String, String> systemProperties;
    public final long heapSize;
    public final long heapMaximum;
    public volatile long heapAllocated;
    
    public volatile long heapFree;
    public String servletContainer;
    public ServerStatus() {
    public ServerStatus(boolean isGO) {
        bootDate = new Date();
        this.isGO = isGO;
        
        heapSize = Runtime.getRuntime().maxMemory();
        heapMaximum = Runtime.getRuntime().maxMemory();
        
        systemProperties = new TreeMap<String, String>();
        put("file.encoding");
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -166,4 +166,11 @@
gb.accessLevel = access level
gb.default = default
gb.setDefault = set default
gb.since = since
gb.since = since
gb.status = status
gb.bootDate = boot date
gb.servletContainer = servlet container
gb.heapMaximum = maximum heap
gb.heapAllocated = allocated heap
gb.heapUsed = used heap
gb.free = free