# to preemptively replace '/' with '*' or '!' for url string parameters.\r
#\r
# <https://issues.apache.org/jira/browse/WICKET-1303>\r
-# <http://tomcat.apache.org/security-6.html>\r
+# <http://tomcat.apache.org/security-6.html#Fixed_in_Apache_Tomcat_6.0.10>\r
#\r
# SINCE 0.5.2\r
web.forwardSlashCharacter = /\r
Whitespace is illegal.\r
\r
#### Passwords\r
-User passwords are CASE-SENSITIVE and may be *plain* or *md5* formatted (see `gitblit.properties` -> *realm.passwordStorage*).\r
+User passwords are CASE-SENSITIVE and may be *plain*, *md5*, or *combined-md5* formatted (see `gitblit.properties` -> *realm.passwordStorage*).\r
\r
#### User Roles\r
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.\r
### RPC Requests\r
\r
<table>\r
-<tr><th colspan='2'>url parameters</th><th rowspan='2'>required<br/>permission</th><th colspan='2'>json</th></tr>\r
+<tr><th colspan='2'>url parameters</th><th rowspan='2'>required<br/>user<br/>permission</th><th colspan='2'>json</th></tr>\r
<tr><th>req=</th><th>name=</th><th>post body</th><th>response body</th></tr>\r
+<tr><td colspan='5'><em>web.enableRpcServlet=true</em></td></tr>\r
<tr><td>LIST_REPOSITORIES</td><td>-</td><td>-</td><td>-</td><td>Map<String, RepositoryModel></td></tr>\r
+<tr><td colspan='5'><em>web.enableRpcManagement=true</em></td></tr>\r
<tr><td>CREATE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>RepositoryModel</td><td>-</td></tr>\r
<tr><td>EDIT_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>RepositoryModel</td><td>-</td></tr>\r
<tr><td>DELETE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>-</td><td>-</td></tr>\r
<tr><td>LIST_FEDERATION_RESULTS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List<FederationModel></td></tr>\r
<tr><td>LIST_FEDERATION_PROPOSALS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List<FederationProposal></td></tr>\r
<tr><td>LIST_FEDERATION_SETS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List<FederationSet></td></tr>\r
+<tr><td colspan='5'><em>web.enableRpcAdministration=true</em></td></tr>\r
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>admin</em></td><td>-</td><td>ServerSettings (see example below)</td></tr>\r
<tr><td>EDIT_SETTINGS</td><td>-</td><td><em>admin</em></td><td>Map<String, String></td><td>-</td></tr>\r
<tr><td>LIST_STATUS</td><td>-</td><td><em>admin</em></td><td>-</td><td>ServerStatus (see example below)</td></tr>\r
[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. \r
This application exercises many, but not all, methods from the utility class `com.gitblit.utils.RpcUtils`.\r
\r
+**NOTE:** \r
+Gitblit Manager stores your login credentials **INSECURELY** in homedir/.gitblit/config.\r
+\r
### EGit "Import from Gitblit" Feature (Planning)\r
\r
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.\r
\r
If you are using Apache mod_proxy, specify [AllowEncodedSlashes NoDecode](http://httpd.apache.org/docs/2.2/mod/core.html#allowencodedslashes).\r
\r
+### Running Gitblit on Tomcat\r
+\r
+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. \r
+You have a few options on how to handle this scenario:\r
+\r
+1. [Tweak Tomcat](http://tomcat.apache.org/security-6.html#Fixed_in_Apache_Tomcat_6.0.10) \r
+2. *web.mountParameters = false* and use non-pretty, parameterized urls\r
+3. *web.forwardSlashCharacter = !* which tells Gitblit to use **!** instead of **/**\r
+\r
## General Interest Questions\r
\r
### Gitblit? What kind of name is that?\r
* @return true if the update succeeded\r
*/\r
public boolean updateSettings(Map<String, String> updatedSettings) {\r
- return settings.saveSettings(updatedSettings); \r
+ return settings.saveSettings(updatedSettings);\r
}\r
\r
public ServerStatus getStatus() {\r
repositoriesFolder = new File(settings.getString(Keys.git.repositoriesFolder, "git"));\r
logger.info("Git repositories folder " + repositoriesFolder.getAbsolutePath());\r
repositoryResolver = new FileResolver<Void>(repositoriesFolder, exportAll);\r
- serverStatus = new ServerStatus();\r
+ serverStatus = new ServerStatus(isGO());\r
String realm = settings.getString(Keys.realm.userService, "users.properties");\r
IUserService loginService = null;\r
try {\r
@Override\r
public void contextInitialized(ServletContextEvent contextEvent) {\r
servletContext = contextEvent.getServletContext();\r
- settingsModel = loadSettingModels();\r
+ settingsModel = loadSettingModels(); \r
if (settings == null) {\r
// Gitblit WAR is running in a servlet container\r
WebXmlSettings webxmlSettings = new WebXmlSettings(contextEvent.getServletContext());\r
configureContext(webxmlSettings, true);\r
}\r
+ \r
+ serverStatus.servletContainer = servletContext.getServerInfo();\r
}\r
\r
/**\r
}\r
} else if (RpcRequest.LIST_STATUS.equals(reqType)) {\r
// return the server's status information\r
- result = GitBlit.self().getStatus();\r
+ if (GitBlit.getBoolean(Keys.web.enableRpcAdministration, false)) {\r
+ result = GitBlit.self().getStatus();\r
+ } else {\r
+ response.sendError(notAllowedCode);\r
+ }\r
}\r
\r
// send the result of the request\r
\r
try {\r
refreshSettings();\r
- status = RpcUtils.getStatus(url, account, password);\r
+ refreshStatus();\r
allowAdministration = true;\r
} catch (UnauthorizedException e) {\r
} catch (ForbiddenException e) {\r
settings = RpcUtils.getSettings(url, account, password);\r
return settings;\r
}\r
+ \r
+ public ServerStatus refreshStatus() throws IOException {\r
+ status = RpcUtils.getStatus(url, account, password);\r
+ return status;\r
+ }\r
\r
public List<FederationModel> refreshFederationRegistrations() throws IOException {\r
List<FederationModel> list = RpcUtils.getFederationRegistrations(url, account, password);\r
\r
private HeaderPanel settingsHeader;\r
\r
+ private StatusPanel statusPanel;\r
+\r
public GitblitPanel(GitblitRegistration reg) {\r
this(reg.url, reg.account, reg.password);\r
}\r
tabs.addTab(Translation.get("gb.repositories"), createRepositoriesPanel());\r
tabs.addTab(Translation.get("gb.users"), createUsersPanel());\r
tabs.addTab(Translation.get("gb.settings"), createSettingsPanel());\r
+ tabs.addTab(Translation.get("gb.status"), createStatusPanel());\r
\r
setLayout(new BorderLayout());\r
add(tabs, BorderLayout.CENTER);\r
return settingsPanel;\r
}\r
\r
+ private JPanel createStatusPanel() {\r
+ JButton refreshStatus = new JButton(Translation.get("gb.refresh"));\r
+ refreshStatus.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ refreshStatus();\r
+ }\r
+ });\r
+\r
+ JPanel controls = new JPanel();\r
+ controls.add(refreshStatus);\r
+\r
+ JPanel panel = new JPanel(new BorderLayout());\r
+ statusPanel = new StatusPanel();\r
+ panel.add(statusPanel, BorderLayout.CENTER);\r
+ panel.add(controls, BorderLayout.SOUTH);\r
+ return panel;\r
+ }\r
+\r
public void login() throws IOException {\r
gitblit.login();\r
\r
\r
if (gitblit.allowAdministration()) {\r
updateSettingsTable();\r
+ updateStatusPanel();\r
Utils.packColumns(settingsTable, 5);\r
} else {\r
// remove the settings tab\r
settingsHeader.setText(Translation.get("gb.settings"));\r
}\r
\r
+ private void updateStatusPanel() {\r
+ statusPanel.setStatus(gitblit.getStatus());\r
+ }\r
+\r
private void filterRepositories(final String fragment) {\r
if (StringUtils.isEmpty(fragment)) {\r
repositoriesTable.setRowSorter(defaultRepositoriesSorter);\r
worker.execute();\r
}\r
\r
+ protected void refreshStatus() {\r
+ GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_STATUS) {\r
+ @Override\r
+ protected Boolean doRequest() throws IOException {\r
+ gitblit.refreshStatus();\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ protected void onSuccess() {\r
+ updateStatusPanel();\r
+ }\r
+ };\r
+ worker.execute();\r
+ }\r
+\r
protected void editSetting(final SettingModel settingModel) {\r
final JTextField textField = new JTextField(settingModel.currentValue);\r
JPanel editPanel = new JPanel(new GridLayout(0, 1));\r
if (settingModel.currentValue.equals(settingModel.defaultValue)) {\r
options = new String[] { Translation.get("gb.cancel"), Translation.get("gb.save") };\r
} else {\r
- options = new String[] { Translation.get("gb.cancel"), Translation.get("gb.setDefault"),\r
- Translation.get("gb.save") };\r
+ options = new String[] { Translation.get("gb.cancel"),\r
+ Translation.get("gb.setDefault"), Translation.get("gb.save") };\r
}\r
String defaultOption = options[0];\r
int selection = JOptionPane.showOptionDialog(GitblitPanel.this, settingPanel,\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.client;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import javax.swing.table.AbstractTableModel;\r
+\r
+/**\r
+ * Table model of a map of properties.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class PropertiesTableModel extends AbstractTableModel {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ List<String> keys;\r
+\r
+ Map<String, String> map;\r
+\r
+ enum Columns {\r
+ Name, Value;\r
+\r
+ @Override\r
+ public String toString() {\r
+ return name().replace('_', ' ');\r
+ }\r
+ }\r
+\r
+ public PropertiesTableModel() {\r
+ this(new HashMap<String, String>());\r
+ }\r
+\r
+ public PropertiesTableModel(Map<String, String> map) {\r
+ setProperties(map);\r
+ }\r
+\r
+ public void setProperties(Map<String, String> map) {\r
+ this.map = map;\r
+ keys = new ArrayList<String>(map.keySet());\r
+ Collections.sort(this.keys);\r
+ }\r
+\r
+ @Override\r
+ public int getRowCount() {\r
+ return keys.size();\r
+ }\r
+\r
+ @Override\r
+ public int getColumnCount() {\r
+ return Columns.values().length;\r
+ }\r
+\r
+ @Override\r
+ public String getColumnName(int column) {\r
+ Columns col = Columns.values()[column];\r
+ switch (col) {\r
+ case Name:\r
+ return Translation.get("gb.name");\r
+ }\r
+ return "";\r
+ }\r
+\r
+ /**\r
+ * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.\r
+ * \r
+ * @param columnIndex\r
+ * the column being queried\r
+ * @return the Object.class\r
+ */\r
+ public Class<?> getColumnClass(int columnIndex) {\r
+ return String.class;\r
+ }\r
+\r
+ @Override\r
+ public Object getValueAt(int rowIndex, int columnIndex) {\r
+ String key = keys.get(rowIndex);\r
+ Columns col = Columns.values()[columnIndex];\r
+ switch (col) {\r
+ case Name:\r
+ return key;\r
+ case Value:\r
+ return map.get(key);\r
+ }\r
+ return null;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.client;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Dimension;\r
+import java.awt.FlowLayout;\r
+import java.awt.Font;\r
+import java.awt.GridLayout;\r
+import java.awt.Insets;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+\r
+import com.gitblit.models.ServerStatus;\r
+import com.gitblit.utils.ByteFormat;\r
+\r
+/**\r
+ * This panel displays the server status.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class StatusPanel extends JPanel {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+ private final Insets insets = new Insets(5, 5, 5, 5);\r
+ private JLabel bootDate;\r
+ private JLabel servletContainer;\r
+ private JLabel heapMaximum;\r
+ private JLabel heapAllocated;\r
+ private JLabel heapUsed;\r
+ private PropertiesTableModel model;\r
+ private HeaderPanel headerPanel;\r
+\r
+ public StatusPanel() {\r
+ super();\r
+ initialize();\r
+ }\r
+\r
+ public StatusPanel(ServerStatus status) {\r
+ this();\r
+ setStatus(status);\r
+ }\r
+\r
+ private void initialize() {\r
+ bootDate = new JLabel();\r
+ servletContainer = new JLabel();\r
+\r
+ heapMaximum = new JLabel();\r
+ heapAllocated = new JLabel();\r
+ heapUsed = new JLabel();\r
+\r
+ JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));\r
+ fieldsPanel.add(createFieldPanel("gb.bootDate", bootDate));\r
+ fieldsPanel.add(createFieldPanel("gb.servletContainer", servletContainer));\r
+ fieldsPanel.add(createFieldPanel("gb.heapUsed", heapUsed));\r
+ fieldsPanel.add(createFieldPanel("gb.heapAllocated", heapAllocated));\r
+ fieldsPanel.add(createFieldPanel("gb.heapMaximum", heapMaximum));\r
+\r
+ model = new PropertiesTableModel();\r
+ JTable propertiesTable = Utils.newTable(model);\r
+ String name = propertiesTable.getColumnName(PropertiesTableModel.Columns.Name.ordinal());\r
+ NameRenderer nameRenderer = new NameRenderer();\r
+ propertiesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
+ propertiesTable.getColumn(name).setCellRenderer(nameRenderer);\r
+\r
+ JPanel centerPanel = new JPanel(new BorderLayout());\r
+ centerPanel.add(fieldsPanel, BorderLayout.NORTH);\r
+ centerPanel.add(new JScrollPane(propertiesTable), BorderLayout.CENTER);\r
+\r
+ headerPanel = new HeaderPanel(Translation.get("gb.status"), null);\r
+ setLayout(new BorderLayout());\r
+ add(headerPanel, BorderLayout.NORTH);\r
+ add(centerPanel, BorderLayout.CENTER);\r
+ }\r
+\r
+ private JPanel createFieldPanel(String key, JLabel valueLabel) {\r
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));\r
+ JLabel textLabel = new JLabel(Translation.get(key));\r
+ textLabel.setFont(textLabel.getFont().deriveFont(Font.BOLD));\r
+ textLabel.setPreferredSize(new Dimension(120, valueLabel.getFont().getSize() + 4));\r
+ panel.add(textLabel);\r
+ panel.add(valueLabel);\r
+ return panel;\r
+ }\r
+\r
+ @Override\r
+ public Insets getInsets() {\r
+ return insets;\r
+ }\r
+\r
+ public void setStatus(ServerStatus status) {\r
+ headerPanel.setText(Translation.get("gb.status"));\r
+ bootDate.setText(status.bootDate.toString());\r
+ servletContainer.setText(status.servletContainer);\r
+ ByteFormat byteFormat = new ByteFormat();\r
+ heapMaximum.setText(byteFormat.format(status.heapMaximum));\r
+ heapAllocated.setText(byteFormat.format(status.heapAllocated));\r
+ heapUsed.setText(byteFormat.format(status.heapAllocated - status.heapFree) + " ("\r
+ + byteFormat.format(status.heapFree) + " " + Translation.get("gb.free") + ")");\r
+ model.setProperties(status.systemProperties);\r
+ model.fireTableDataChanged();\r
+ }\r
+}\r
\r
public final Date bootDate;\r
\r
+ public final boolean isGO;\r
+ \r
public final Map<String, String> systemProperties;\r
\r
- public final long heapSize;\r
+ public final long heapMaximum;\r
\r
public volatile long heapAllocated;\r
\r
public volatile long heapFree;\r
+ \r
+ public String servletContainer;\r
\r
- public ServerStatus() {\r
+ public ServerStatus(boolean isGO) {\r
bootDate = new Date();\r
+ this.isGO = isGO;\r
\r
- heapSize = Runtime.getRuntime().maxMemory();\r
+ heapMaximum = Runtime.getRuntime().maxMemory();\r
\r
systemProperties = new TreeMap<String, String>();\r
put("file.encoding");\r
gb.accessLevel = access level\r
gb.default = default\r
gb.setDefault = set default\r
-gb.since = since
\ No newline at end of file
+gb.since = since\r
+gb.status = status\r
+gb.bootDate = boot date\r
+gb.servletContainer = servlet container\r
+gb.heapMaximum = maximum heap\r
+gb.heapAllocated = allocated heap\r
+gb.heapUsed = used heap\r
+gb.free = free
\ No newline at end of file